mirror of https://github.com/Lapikud/tipilan
commit
e5edbee7ec
44 changed files with 3071 additions and 1426 deletions
@ -1,7 +1,27 @@ |
|||||||
import type { NextConfig } from "next"; |
import type { NextConfig } from "next"; |
||||||
|
import createNextIntlPlugin from "next-intl/plugin"; |
||||||
|
|
||||||
|
const withNextIntl = createNextIntlPlugin("./src/i18n/request.ts"); |
||||||
|
|
||||||
const nextConfig: NextConfig = { |
const nextConfig: NextConfig = { |
||||||
/* config options here */ |
async headers() { |
||||||
|
return [ |
||||||
|
{ |
||||||
|
source: "/(.*)", |
||||||
|
headers: [ |
||||||
|
{ |
||||||
|
key: "Content-Security-Policy", |
||||||
|
value: |
||||||
|
"frame-src 'self' https://tipilan.ee https://player.twitch.tv https://embed.twitch.tv; frame-ancestors 'self' https://tipilan.ee;", |
||||||
|
}, |
||||||
|
{ |
||||||
|
key: "X-Frame-Options", |
||||||
|
value: "SAMEORIGIN", |
||||||
|
}, |
||||||
|
], |
||||||
|
}, |
||||||
|
]; |
||||||
|
}, |
||||||
}; |
}; |
||||||
|
|
||||||
export default nextConfig; |
export default withNextIntl(nextConfig); |
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
@ -0,0 +1,77 @@ |
|||||||
|
"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-col sm:flex-row flex-wrap gap-5 items-stretch" |
||||||
|
> |
||||||
|
<div |
||||||
|
className={`${vipnagorgialla.className} md:w-[180px] w-30 text-[#00A3E0] text-3xl md:text-4xl font-bold italic flex-shrink-0 flex items-center justify-center`} |
||||||
|
> |
||||||
|
{item.time} |
||||||
|
</div> |
||||||
|
<div className="flex-1 flex flex-col justify-center min-w-0 min-h-[120px]"> |
||||||
|
<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-xl md:text-2xl text-[#938BA1] dark:text-[#938BA1] text-balance"> |
||||||
|
{item.description} |
||||||
|
</div> |
||||||
|
)} |
||||||
|
<div className="text-xl md:text-2xl text-[#938BA1] dark:text-[#938BA1] text-balance"> |
||||||
|
{t(item.locationKey)} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
))} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<SectionDivider /> |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
||||||
@ -0,0 +1,64 @@ |
|||||||
|
// app/kodukord/page.tsx (App Router)
|
||||||
|
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({ |
||||||
|
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(); |
||||||
|
|
||||||
|
return ( |
||||||
|
<div> |
||||||
|
<div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16"> |
||||||
|
{/* Page title (separate from markdown headings) */} |
||||||
|
<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("rules.houseRules")} |
||||||
|
</h1> |
||||||
|
|
||||||
|
<div className="prose prose-lg dark:prose-invert max-w-none"> |
||||||
|
<ReactMarkdown |
||||||
|
components={ |
||||||
|
{ |
||||||
|
h1: (props) => ( |
||||||
|
<h1 className="text-3xl md:text-4xl font-bold my-4"> |
||||||
|
{props.children} |
||||||
|
</h1> |
||||||
|
), |
||||||
|
h2: (props) => ( |
||||||
|
<h2 className="text-2xl md:text-3xl font-semibold my-3"> |
||||||
|
{props.children} |
||||||
|
</h2> |
||||||
|
), |
||||||
|
ol: (props) => ( |
||||||
|
<ol className="list-decimal ml-6 md:text-xl"> |
||||||
|
{props.children} |
||||||
|
</ol> |
||||||
|
), |
||||||
|
ul: (props) => ( |
||||||
|
<ul className="list-disc ml-6 md:text-xl"> |
||||||
|
{props.children} |
||||||
|
</ul> |
||||||
|
), |
||||||
|
p: (props) => <p className="md:text-xl">{props.children}</p>, |
||||||
|
} as Components |
||||||
|
} |
||||||
|
> |
||||||
|
{content} |
||||||
|
</ReactMarkdown> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<SectionDivider /> |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
||||||
@ -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> |
||||||
|
); |
||||||
|
} |
||||||
@ -0,0 +1,974 @@ |
|||||||
|
"use client"; |
||||||
|
|
||||||
|
import { vipnagorgialla } from "@/components/Vipnagorgialla"; |
||||||
|
import * as THREE from "three"; |
||||||
|
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js"; |
||||||
|
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 { |
||||||
|
toggleDividers?: (show: boolean) => void; |
||||||
|
switchView?: (view: "tudengimaja" | "fuajee") => void; |
||||||
|
} |
||||||
|
|
||||||
|
export default function Expo() { |
||||||
|
const mountRef = useRef<MountRefCurrent | null>(null); |
||||||
|
const [hoveredRoom, setHoveredRoom] = useState<string | null>(null); |
||||||
|
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 }); |
||||||
|
const [showDividers, setShowDividers] = useState<boolean>(true); |
||||||
|
const [currentView, setCurrentView] = useState<"tudengimaja" | "fuajee">( |
||||||
|
"tudengimaja", |
||||||
|
); |
||||||
|
const currentViewRef = useRef<"tudengimaja" | "fuajee">("tudengimaja"); |
||||||
|
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", |
||||||
|
// fuajee rooms
|
||||||
|
ityk: t("expo.areas.ityk"), |
||||||
|
estoniagamedev: t("expo.areas.estoniagamedev"), |
||||||
|
info: t("expo.areas.info"), |
||||||
|
tartuyk: t("expo.areas.tartuyk"), |
||||||
|
tly: t("expo.areas.tly"), |
||||||
|
gameup: "GameUP!", |
||||||
|
ittk: t("expo.areas.ittk"), |
||||||
|
photobooth: t("expo.areas.photobooth"), |
||||||
|
}), |
||||||
|
[t], |
||||||
|
); |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
if (!mountRef.current) return; |
||||||
|
|
||||||
|
// Copy ref to variable to avoid stale closure in cleanup
|
||||||
|
const mountElement = mountRef.current; |
||||||
|
let dividersRef: THREE.Mesh[] = []; |
||||||
|
const fuajeeMeshes: THREE.Mesh[] = []; |
||||||
|
let tudengimajaObjects: THREE.Object3D[] = []; |
||||||
|
let fuajeeMesh: THREE.Group | null = null; |
||||||
|
const fuajeeRooms: THREE.Mesh[] = []; |
||||||
|
|
||||||
|
// Scene setup
|
||||||
|
const scene = new THREE.Scene(); |
||||||
|
scene.background = new THREE.Color(0x0e0f19); |
||||||
|
|
||||||
|
// Get responsive dimensions
|
||||||
|
const getResponsiveDimensions = () => { |
||||||
|
const container = mountRef.current; |
||||||
|
if (!container) return { width: 800, height: 600 }; |
||||||
|
|
||||||
|
const containerWidth = container.offsetWidth; |
||||||
|
const maxWidth = Math.min(containerWidth, 800); |
||||||
|
const width = Math.max(maxWidth, 300); // Minimum width
|
||||||
|
const height = (width * 600) / 800; // Maintain aspect ratio
|
||||||
|
|
||||||
|
return { width, height }; |
||||||
|
}; |
||||||
|
|
||||||
|
const { width, height } = getResponsiveDimensions(); |
||||||
|
|
||||||
|
// Isometric camera setup with responsive sizing
|
||||||
|
const aspect = width / height; |
||||||
|
const baseFrustumSize = 14; |
||||||
|
const frustumSize = baseFrustumSize; // Keep consistent frustum size
|
||||||
|
const camera = new THREE.OrthographicCamera( |
||||||
|
(frustumSize * aspect) / -2, |
||||||
|
(frustumSize * aspect) / 2, |
||||||
|
frustumSize / 2, |
||||||
|
frustumSize / -2, |
||||||
|
1, |
||||||
|
1000, |
||||||
|
); |
||||||
|
|
||||||
|
// Camera positions for different views
|
||||||
|
const cameraPositions = { |
||||||
|
tudengimaja: { |
||||||
|
position: new THREE.Vector3(10, 10, 14), |
||||||
|
lookAt: new THREE.Vector3(-1.4, 0, 0), |
||||||
|
}, |
||||||
|
fuajee: { |
||||||
|
position: new THREE.Vector3(30, 20, 15), |
||||||
|
lookAt: new THREE.Vector3(0, 0, 0), |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
// Position camera for isometric view (default to tudengimaja)
|
||||||
|
camera.position.copy(cameraPositions.tudengimaja.position); |
||||||
|
camera.lookAt(cameraPositions.tudengimaja.lookAt); |
||||||
|
|
||||||
|
// Renderer
|
||||||
|
const renderer = new THREE.WebGLRenderer({ antialias: true }); |
||||||
|
renderer.setSize(width, height); |
||||||
|
renderer.shadowMap.enabled = true; |
||||||
|
renderer.shadowMap.type = THREE.PCFSoftShadowMap; |
||||||
|
mountElement.appendChild(renderer.domElement); |
||||||
|
|
||||||
|
// Raycaster for mouse interactions
|
||||||
|
const raycaster = new THREE.Raycaster(); |
||||||
|
const mouse = new THREE.Vector2(); |
||||||
|
|
||||||
|
// Lighting
|
||||||
|
const ambientLight = new THREE.AmbientLight(0x404040, 1.2); |
||||||
|
scene.add(ambientLight); |
||||||
|
|
||||||
|
const directionalLight = new THREE.DirectionalLight(0xffffff, 1.5); |
||||||
|
directionalLight.position.set(10, 10, 5); |
||||||
|
directionalLight.castShadow = false; |
||||||
|
directionalLight.shadow.mapSize.width = 2048; |
||||||
|
directionalLight.shadow.mapSize.height = 2048; |
||||||
|
scene.add(directionalLight); |
||||||
|
|
||||||
|
// Room colors and names
|
||||||
|
const roomColors = [ |
||||||
|
0x343434, // Gray - Lauamängude ala
|
||||||
|
0x4ecdc4, // Turquoise - Baariala
|
||||||
|
0xffe66d, // Yellow - EVAL
|
||||||
|
0xff6600, // Orange - Redbull Sim Racing
|
||||||
|
0xff1493, // Deep Pink - Võitlusmängu ala
|
||||||
|
0x3498db, // Blue - Sony
|
||||||
|
0x2ecc71, // Green - Lava
|
||||||
|
0x080682, // Dark Blue - LVLup!
|
||||||
|
0xc02841, // Red - RedBull
|
||||||
|
]; |
||||||
|
|
||||||
|
// Create individual rooms as rectangles with custom positions
|
||||||
|
const rooms: THREE.Mesh[] = []; |
||||||
|
const roomData: Array<{ |
||||||
|
mesh: THREE.Mesh; |
||||||
|
name: string; |
||||||
|
originalColor: number; |
||||||
|
originalScale: THREE.Vector3; |
||||||
|
view: "tudengimaja" | "fuajee"; |
||||||
|
}> = []; |
||||||
|
const dividers: THREE.Mesh[] = []; |
||||||
|
|
||||||
|
// Define rooms with custom positions, sizes and colors
|
||||||
|
const roomDefinitions = [ |
||||||
|
{ |
||||||
|
width: 7, |
||||||
|
height: 0.7, |
||||||
|
depth: 3, |
||||||
|
x: 2.5, |
||||||
|
z: 4, |
||||||
|
color: roomColors[0], |
||||||
|
name: roomNames.boardGames, |
||||||
|
}, |
||||||
|
{ |
||||||
|
width: 3.5, |
||||||
|
height: 0.7, |
||||||
|
depth: 1.2, |
||||||
|
x: 0.7, |
||||||
|
z: -0.3, |
||||||
|
color: roomColors[1], |
||||||
|
name: roomNames.bar, |
||||||
|
}, |
||||||
|
{ |
||||||
|
width: 1.8, |
||||||
|
height: 0.7, |
||||||
|
depth: 1.5, |
||||||
|
x: 1, |
||||||
|
z: -3.5, |
||||||
|
color: roomColors[2], |
||||||
|
name: roomNames.eval, |
||||||
|
}, |
||||||
|
{ |
||||||
|
width: 2, |
||||||
|
height: 0.7, |
||||||
|
depth: 4.5, |
||||||
|
x: 5.2, |
||||||
|
z: -2, |
||||||
|
color: roomColors[3], |
||||||
|
name: roomNames.simRacing, |
||||||
|
}, |
||||||
|
{ |
||||||
|
width: 3, |
||||||
|
height: 0.7, |
||||||
|
depth: 1.5, |
||||||
|
x: -1.7, |
||||||
|
z: -3.5, |
||||||
|
color: roomColors[4], |
||||||
|
name: roomNames.fighting, |
||||||
|
}, |
||||||
|
// {
|
||||||
|
// width: 1.8,
|
||||||
|
// height: 0.7,
|
||||||
|
// depth: 1.5,
|
||||||
|
// x: -4.3,
|
||||||
|
// z: -3.5,
|
||||||
|
// color: roomColors[5],
|
||||||
|
// name: "Sony",
|
||||||
|
// },
|
||||||
|
{ |
||||||
|
width: 3, |
||||||
|
height: 0.7, |
||||||
|
depth: 1.7, |
||||||
|
x: -3.5, |
||||||
|
z: -0.5, |
||||||
|
color: roomColors[7], |
||||||
|
name: roomNames.lvlup, |
||||||
|
}, |
||||||
|
//{
|
||||||
|
// width: 2,
|
||||||
|
// height: 0.7,
|
||||||
|
// depth: 4,
|
||||||
|
// x: -6.4,
|
||||||
|
// z: -2.3,
|
||||||
|
// color: roomColors[6],
|
||||||
|
// name: "Lava",
|
||||||
|
//},
|
||||||
|
{ |
||||||
|
width: 1.8, |
||||||
|
height: 0.7, |
||||||
|
depth: 1.5, |
||||||
|
x: 3, |
||||||
|
z: -3.5, |
||||||
|
color: roomColors[8], |
||||||
|
name: roomNames.redbull, |
||||||
|
}, |
||||||
|
]; |
||||||
|
|
||||||
|
roomDefinitions.forEach((roomDef) => { |
||||||
|
const geometry = new THREE.BoxGeometry( |
||||||
|
roomDef.width, |
||||||
|
roomDef.height, |
||||||
|
roomDef.depth, |
||||||
|
); |
||||||
|
const material = new THREE.MeshLambertMaterial({ |
||||||
|
color: roomDef.color, |
||||||
|
}); |
||||||
|
|
||||||
|
const room = new THREE.Mesh(geometry, material); |
||||||
|
room.position.set(roomDef.x, roomDef.height / 2, roomDef.z); |
||||||
|
room.castShadow = true; |
||||||
|
room.receiveShadow = true; |
||||||
|
room.userData = { name: roomDef.name, originalColor: roomDef.color }; |
||||||
|
|
||||||
|
scene.add(room); |
||||||
|
rooms.push(room); |
||||||
|
roomData.push({ |
||||||
|
mesh: room, |
||||||
|
name: roomDef.name, |
||||||
|
originalColor: roomDef.color, |
||||||
|
originalScale: room.scale.clone(), |
||||||
|
view: "tudengimaja", |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
// Create toggleable room dividers
|
||||||
|
const createTogglableDivider = ( |
||||||
|
width: number, |
||||||
|
height: number, |
||||||
|
depth: number, |
||||||
|
x: number, |
||||||
|
z: number, |
||||||
|
) => { |
||||||
|
const wallGeometry = new THREE.BoxGeometry(width, height, depth); |
||||||
|
const wallMaterial = new THREE.MeshLambertMaterial({ |
||||||
|
color: 0x555555, |
||||||
|
transparent: true, |
||||||
|
opacity: 0, |
||||||
|
}); |
||||||
|
|
||||||
|
const wall = new THREE.Mesh(wallGeometry, wallMaterial); |
||||||
|
wall.position.set(x, height / 2, z); |
||||||
|
wall.visible = false; |
||||||
|
scene.add(wall); |
||||||
|
dividers.push(wall); |
||||||
|
}; |
||||||
|
|
||||||
|
// Add strategic dividers between major areas
|
||||||
|
createTogglableDivider(10, 2, 2, -2.5, 1.5); // Wall between main entrance
|
||||||
|
createTogglableDivider(2, 2, 2, 5.5, 1.5); // Wall right next to Lauamängud & Redbull Sim Racing
|
||||||
|
|
||||||
|
// Store dividers reference for later access
|
||||||
|
dividersRef = [...dividers]; |
||||||
|
|
||||||
|
// Ground plane
|
||||||
|
const groundGeometry = new THREE.PlaneGeometry(14, 10.5); |
||||||
|
const groundMaterial = new THREE.MeshLambertMaterial({ color: 0xcccccc }); |
||||||
|
const ground = new THREE.Mesh(groundGeometry, groundMaterial); |
||||||
|
ground.rotation.x = -Math.PI / 2; |
||||||
|
ground.position.x = -1.1; |
||||||
|
ground.position.y = -0.5; |
||||||
|
ground.receiveShadow = true; |
||||||
|
scene.add(ground); |
||||||
|
|
||||||
|
// Second ground plane
|
||||||
|
const groundGeometry2 = new THREE.PlaneGeometry(2, 7); |
||||||
|
const groundMaterial2 = new THREE.MeshLambertMaterial({ |
||||||
|
color: 0xcccccc, |
||||||
|
}); |
||||||
|
const ground2 = new THREE.Mesh(groundGeometry2, groundMaterial2); |
||||||
|
ground2.rotation.x = -Math.PI / 2; |
||||||
|
ground2.position.x = -12.2; |
||||||
|
ground2.position.y = -5; |
||||||
|
ground2.receiveShadow = true; |
||||||
|
scene.add(ground2); |
||||||
|
|
||||||
|
// Store tudengimaja objects (rooms, ground, dividers)
|
||||||
|
tudengimajaObjects = [...rooms, ground, ground2, ...dividers]; |
||||||
|
|
||||||
|
// Load fuajee GLTF model
|
||||||
|
const loader = new GLTFLoader(); |
||||||
|
loader.load( |
||||||
|
"/spaces/fuajeeTalTech.glb", |
||||||
|
(gltf) => { |
||||||
|
fuajeeMesh = gltf.scene; |
||||||
|
fuajeeMesh.position.set(-1.5, 1, 0); |
||||||
|
fuajeeMesh.scale.set(0.3, 0.3, 0.3); |
||||||
|
fuajeeMesh.visible = false; // Initially hidden
|
||||||
|
|
||||||
|
// Traverse the model to collect meshes
|
||||||
|
fuajeeMesh.traverse((child) => { |
||||||
|
if (child instanceof THREE.Mesh) { |
||||||
|
child.castShadow = true; |
||||||
|
child.receiveShadow = true; |
||||||
|
fuajeeMeshes.push(child); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
scene.add(fuajeeMesh); |
||||||
|
|
||||||
|
// Create example rooms for fuajee after the model loads
|
||||||
|
createfuajeeRooms(); |
||||||
|
}, |
||||||
|
(progress) => { |
||||||
|
console.log( |
||||||
|
"Loading progress:", |
||||||
|
(progress.loaded / progress.total) * 100 + "%", |
||||||
|
); |
||||||
|
}, |
||||||
|
(error) => { |
||||||
|
console.error("Error loading GLTF:", error); |
||||||
|
}, |
||||||
|
); |
||||||
|
|
||||||
|
// Function to create example rooms for fuajee
|
||||||
|
const createfuajeeRooms = () => { |
||||||
|
const fuajeeRoomColors = [ |
||||||
|
0x7b1642, // ITÜK - Cherry Red
|
||||||
|
0x365591, // Light Blue - Tartu Ülikool
|
||||||
|
0xa82838, // Red - Tallinna Ülikool
|
||||||
|
0x183bbf, // Dark Blue - Eesti Gamedev
|
||||||
|
0xd12e7d, // Purple - Taltech
|
||||||
|
0x228b22, // Green - GameUP
|
||||||
|
0xff6347, // Orange - Info
|
||||||
|
0x20b2aa, // Light Sea Green - Photobooth
|
||||||
|
]; |
||||||
|
|
||||||
|
const fuajeeRoomDefinitions = [ |
||||||
|
{ |
||||||
|
width: 5, |
||||||
|
height: 0.5, |
||||||
|
depth: 3.5, |
||||||
|
x: -6, |
||||||
|
z: 2.8, |
||||||
|
color: fuajeeRoomColors[0], |
||||||
|
name: roomNames.ityk, |
||||||
|
}, |
||||||
|
{ |
||||||
|
width: 5, |
||||||
|
height: 0.5, |
||||||
|
depth: 2, |
||||||
|
x: 2.2, |
||||||
|
z: -1.5, |
||||||
|
color: fuajeeRoomColors[1], |
||||||
|
name: roomNames.tartuyk, |
||||||
|
}, |
||||||
|
{ |
||||||
|
width: 6, |
||||||
|
height: 0.5, |
||||||
|
depth: 2, |
||||||
|
x: -5.8, |
||||||
|
z: -1.2, |
||||||
|
color: fuajeeRoomColors[3], |
||||||
|
name: roomNames.estoniagamedev, |
||||||
|
}, |
||||||
|
{ |
||||||
|
width: 2, |
||||||
|
height: 0.5, |
||||||
|
depth: 2, |
||||||
|
x: -1.5, |
||||||
|
z: -1.5, |
||||||
|
color: fuajeeRoomColors[6], |
||||||
|
name: roomNames.info, |
||||||
|
}, |
||||||
|
{ |
||||||
|
width: 2, |
||||||
|
height: 0.5, |
||||||
|
depth: 1.5, |
||||||
|
x: 6, |
||||||
|
z: -1.7, |
||||||
|
color: fuajeeRoomColors[2], |
||||||
|
name: roomNames.tly, |
||||||
|
}, |
||||||
|
{ |
||||||
|
width: 2, |
||||||
|
height: 0.5, |
||||||
|
depth: 1.5, |
||||||
|
x: 11, |
||||||
|
z: -1.7, |
||||||
|
color: fuajeeRoomColors[4], |
||||||
|
name: roomNames.ittk, |
||||||
|
}, |
||||||
|
{ |
||||||
|
width: 2, |
||||||
|
height: 0.5, |
||||||
|
depth: 1.5, |
||||||
|
x: 13.5, |
||||||
|
z: -1.7, |
||||||
|
color: fuajeeRoomColors[7], |
||||||
|
name: roomNames.photobooth, |
||||||
|
}, |
||||||
|
{ |
||||||
|
width: 2, |
||||||
|
height: 0.5, |
||||||
|
depth: 1.5, |
||||||
|
x: 8.5, |
||||||
|
z: -1.7, |
||||||
|
color: fuajeeRoomColors[5], |
||||||
|
name: roomNames.gameup, |
||||||
|
}, |
||||||
|
]; |
||||||
|
|
||||||
|
fuajeeRoomDefinitions.forEach((roomDef) => { |
||||||
|
const geometry = new THREE.BoxGeometry( |
||||||
|
roomDef.width, |
||||||
|
roomDef.height, |
||||||
|
roomDef.depth, |
||||||
|
); |
||||||
|
const material = new THREE.MeshLambertMaterial({ |
||||||
|
color: roomDef.color, |
||||||
|
}); |
||||||
|
|
||||||
|
const room = new THREE.Mesh(geometry, material); |
||||||
|
room.position.set(roomDef.x, roomDef.height / 2 + 2, roomDef.z); |
||||||
|
room.castShadow = true; |
||||||
|
room.receiveShadow = true; |
||||||
|
room.userData = { name: roomDef.name, originalColor: roomDef.color }; |
||||||
|
room.visible = false; // Initially hidden
|
||||||
|
|
||||||
|
scene.add(room); |
||||||
|
fuajeeRooms.push(room); |
||||||
|
roomData.push({ |
||||||
|
mesh: room, |
||||||
|
name: roomDef.name, |
||||||
|
originalColor: roomDef.color, |
||||||
|
originalScale: room.scale.clone(), |
||||||
|
view: "fuajee", |
||||||
|
}); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
// Resize handler
|
||||||
|
const handleResize = () => { |
||||||
|
const { width: newWidth, height: newHeight } = getResponsiveDimensions(); |
||||||
|
|
||||||
|
// Update camera
|
||||||
|
const newAspect = newWidth / newHeight; |
||||||
|
const newFrustumSize = baseFrustumSize; |
||||||
|
|
||||||
|
camera.left = (newFrustumSize * newAspect) / -2; |
||||||
|
camera.right = (newFrustumSize * newAspect) / 2; |
||||||
|
camera.top = newFrustumSize / 2; |
||||||
|
camera.bottom = newFrustumSize / -2; |
||||||
|
camera.updateProjectionMatrix(); |
||||||
|
|
||||||
|
// Update renderer
|
||||||
|
renderer.setSize(newWidth, newHeight); |
||||||
|
}; |
||||||
|
|
||||||
|
// Add resize event listener
|
||||||
|
window.addEventListener("resize", handleResize); |
||||||
|
|
||||||
|
// Mouse event handlers
|
||||||
|
const onMouseMove = (event: MouseEvent) => { |
||||||
|
const rect = renderer.domElement.getBoundingClientRect(); |
||||||
|
mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; |
||||||
|
mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; |
||||||
|
|
||||||
|
// Update mouse position for tooltip
|
||||||
|
setMousePosition({ x: event.clientX, y: event.clientY }); |
||||||
|
|
||||||
|
// Handle mouse interactions based on current view
|
||||||
|
if (currentViewRef.current === "tudengimaja") { |
||||||
|
// Update raycaster
|
||||||
|
raycaster.setFromCamera(mouse, camera); |
||||||
|
const intersects = raycaster.intersectObjects(rooms); |
||||||
|
|
||||||
|
// Reset all tudengimaja rooms to original state
|
||||||
|
roomData |
||||||
|
.filter((r) => r.view === "tudengimaja") |
||||||
|
.forEach(({ mesh, originalColor, originalScale }) => { |
||||||
|
(mesh.material as THREE.MeshLambertMaterial).color.setHex( |
||||||
|
originalColor, |
||||||
|
); |
||||||
|
mesh.scale.copy(originalScale); |
||||||
|
}); |
||||||
|
|
||||||
|
if (intersects.length > 0) { |
||||||
|
const hoveredMesh = intersects[0].object as THREE.Mesh; |
||||||
|
const roomInfo = roomData.find((r) => r.mesh === hoveredMesh); |
||||||
|
|
||||||
|
if (roomInfo) { |
||||||
|
// Apply hover effects
|
||||||
|
(hoveredMesh.material as THREE.MeshLambertMaterial).color.setHex( |
||||||
|
0xffffff, |
||||||
|
); |
||||||
|
hoveredMesh.scale.multiplyScalar(1.02); |
||||||
|
setHoveredRoom(roomInfo.name); |
||||||
|
} |
||||||
|
} else { |
||||||
|
setHoveredRoom(null); |
||||||
|
} |
||||||
|
} else if (currentViewRef.current === "fuajee") { |
||||||
|
// Update raycaster for fuajee rooms
|
||||||
|
raycaster.setFromCamera(mouse, camera); |
||||||
|
const intersects = raycaster.intersectObjects(fuajeeRooms); |
||||||
|
|
||||||
|
// Reset all fuajee rooms to original state
|
||||||
|
roomData |
||||||
|
.filter((r) => r.view === "fuajee") |
||||||
|
.forEach(({ mesh, originalColor, originalScale }) => { |
||||||
|
(mesh.material as THREE.MeshLambertMaterial).color.setHex( |
||||||
|
originalColor, |
||||||
|
); |
||||||
|
|
||||||
|
mesh.scale.copy(originalScale); |
||||||
|
}); |
||||||
|
|
||||||
|
if (intersects.length > 0) { |
||||||
|
const hoveredMesh = intersects[0].object as THREE.Mesh; |
||||||
|
const roomInfo = roomData.find((r) => r.mesh === hoveredMesh); |
||||||
|
|
||||||
|
if (roomInfo) { |
||||||
|
// Apply hover effects with better visibility
|
||||||
|
(hoveredMesh.material as THREE.MeshLambertMaterial).color.setHex( |
||||||
|
0xffffff, |
||||||
|
); |
||||||
|
|
||||||
|
hoveredMesh.scale.multiplyScalar(1.1); |
||||||
|
setHoveredRoom(roomInfo.name); |
||||||
|
} |
||||||
|
} else { |
||||||
|
setHoveredRoom(null); |
||||||
|
} |
||||||
|
} else { |
||||||
|
setHoveredRoom(null); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
// Add mouse event listener
|
||||||
|
renderer.domElement.addEventListener("mousemove", onMouseMove); |
||||||
|
|
||||||
|
// Function to switch camera views
|
||||||
|
const switchView = (view: "tudengimaja" | "fuajee") => { |
||||||
|
const targetPosition = cameraPositions[view].position; |
||||||
|
const targetLookAt = cameraPositions[view].lookAt; |
||||||
|
|
||||||
|
// Animate camera transition
|
||||||
|
const startPosition = camera.position.clone(); |
||||||
|
const startLookAt = new THREE.Vector3(); |
||||||
|
camera.getWorldDirection(startLookAt); |
||||||
|
startLookAt.multiplyScalar(-1).add(camera.position); |
||||||
|
|
||||||
|
let progress = 0; |
||||||
|
const animateCamera = () => { |
||||||
|
progress += 0.05; |
||||||
|
if (progress >= 1) { |
||||||
|
progress = 1; |
||||||
|
} |
||||||
|
|
||||||
|
// Smooth interpolation
|
||||||
|
const easeProgress = 1 - Math.cos(progress * Math.PI * 0.5); |
||||||
|
|
||||||
|
camera.position.lerpVectors( |
||||||
|
startPosition, |
||||||
|
targetPosition, |
||||||
|
easeProgress, |
||||||
|
); |
||||||
|
const currentLookAt = new THREE.Vector3().lerpVectors( |
||||||
|
startLookAt, |
||||||
|
targetLookAt, |
||||||
|
easeProgress, |
||||||
|
); |
||||||
|
camera.lookAt(currentLookAt); |
||||||
|
|
||||||
|
if (progress < 1) { |
||||||
|
requestAnimationFrame(animateCamera); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
animateCamera(); |
||||||
|
|
||||||
|
// Reset hover state when switching views
|
||||||
|
setHoveredRoom(null); |
||||||
|
|
||||||
|
// Reset all room states to original
|
||||||
|
roomData.forEach(({ mesh, originalColor, originalScale }) => { |
||||||
|
(mesh.material as THREE.MeshLambertMaterial).color.setHex( |
||||||
|
originalColor, |
||||||
|
); |
||||||
|
mesh.scale.copy(originalScale); |
||||||
|
}); |
||||||
|
|
||||||
|
// Toggle visibility of objects based on view
|
||||||
|
if (view === "fuajee") { |
||||||
|
tudengimajaObjects.forEach((obj) => (obj.visible = false)); |
||||||
|
if (fuajeeMesh) { |
||||||
|
fuajeeMesh.visible = true; |
||||||
|
} |
||||||
|
fuajeeRooms.forEach((room) => (room.visible = true)); |
||||||
|
} else { |
||||||
|
tudengimajaObjects.forEach((obj) => (obj.visible = true)); |
||||||
|
if (fuajeeMesh) { |
||||||
|
fuajeeMesh.visible = false; |
||||||
|
} |
||||||
|
fuajeeRooms.forEach((room) => (room.visible = false)); |
||||||
|
// Re-apply divider visibility state
|
||||||
|
if (mountElement.toggleDividers) { |
||||||
|
mountElement.toggleDividers(showDividers); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
// Animation loop
|
||||||
|
const animate = () => { |
||||||
|
requestAnimationFrame(animate); |
||||||
|
|
||||||
|
// Gentle floating animation for rooms
|
||||||
|
if (currentViewRef.current === "tudengimaja") { |
||||||
|
rooms.forEach((room, index) => { |
||||||
|
const originalY = 0.25; // height / 2 for the room height of 0.5
|
||||||
|
const baseY = originalY + Math.sin(Date.now() * 0.001 + index) * 0.05; |
||||||
|
|
||||||
|
// Maintain current scale while updating Y position
|
||||||
|
room.position.y = baseY; |
||||||
|
}); |
||||||
|
} else if (currentViewRef.current === "fuajee") { |
||||||
|
fuajeeRooms.forEach((room, index) => { |
||||||
|
const originalY = 2.25; // height / 2 for the room height of 0.5 + 2 offset
|
||||||
|
const baseY = originalY + Math.sin(Date.now() * 0.001 + index) * 0.05; |
||||||
|
|
||||||
|
// Maintain current scale while updating Y position
|
||||||
|
room.position.y = baseY; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
renderer.render(scene, camera); |
||||||
|
}; |
||||||
|
|
||||||
|
animate(); |
||||||
|
|
||||||
|
// Function to toggle dividers
|
||||||
|
const toggleDividers = (show: boolean) => { |
||||||
|
dividersRef.forEach((divider) => { |
||||||
|
divider.visible = show; |
||||||
|
(divider.material as THREE.MeshLambertMaterial).opacity = show |
||||||
|
? 0.4 |
||||||
|
: 0; |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
// Expose functions to parent scope
|
||||||
|
mountElement.toggleDividers = toggleDividers; |
||||||
|
mountElement.switchView = switchView; |
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
return () => { |
||||||
|
window.removeEventListener("resize", handleResize); |
||||||
|
renderer.domElement.removeEventListener("mousemove", onMouseMove); |
||||||
|
if (mountElement && renderer.domElement) { |
||||||
|
mountElement.removeChild(renderer.domElement); |
||||||
|
} |
||||||
|
renderer.dispose(); |
||||||
|
}; |
||||||
|
}, [roomNames]); |
||||||
|
|
||||||
|
// Update dividers when showDividers state changes
|
||||||
|
useEffect(() => { |
||||||
|
if (mountRef.current?.toggleDividers) { |
||||||
|
mountRef.current.toggleDividers(showDividers); |
||||||
|
} |
||||||
|
}, [showDividers]); |
||||||
|
|
||||||
|
// Handle view switching
|
||||||
|
const handleViewSwitch = (view: "tudengimaja" | "fuajee") => { |
||||||
|
setCurrentView(view); |
||||||
|
currentViewRef.current = view; // Update ref immediately
|
||||||
|
setHoveredRoom(null); // Clear any existing hover state
|
||||||
|
if (mountRef.current?.switchView) { |
||||||
|
mountRef.current.switchView(view); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
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`} |
||||||
|
> |
||||||
|
{t("expo.title")} |
||||||
|
</h1> |
||||||
|
<div className="mb-6"> |
||||||
|
<h2 className="text-2xl text-[#2A2C3F] dark:text-[#EEE5E5] mb-3"> |
||||||
|
{currentView === "tudengimaja" |
||||||
|
? t("schedule.locations.studentHouse") |
||||||
|
: t("schedule.locations.entranceHall")} |
||||||
|
</h2> |
||||||
|
|
||||||
|
{currentView === "tudengimaja" && ( |
||||||
|
<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]"> |
||||||
|
{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> |
||||||
|
)} |
||||||
|
|
||||||
|
{currentView === "fuajee" && ( |
||||||
|
<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: "#7b1642" }} |
||||||
|
></div> |
||||||
|
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]"> |
||||||
|
{t("expo.areas.ityk")} |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
<div className="flex items-center gap-2"> |
||||||
|
<div |
||||||
|
className="w-4 h-4 border border-gray-300" |
||||||
|
style={{ backgroundColor: "#365591" }} |
||||||
|
></div> |
||||||
|
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]"> |
||||||
|
{t("expo.areas.tartuyk")} |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
<div className="flex items-center gap-2"> |
||||||
|
<div |
||||||
|
className="w-4 h-4 border border-gray-300" |
||||||
|
style={{ backgroundColor: "#183bbf" }} |
||||||
|
></div> |
||||||
|
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]"> |
||||||
|
{t("expo.areas.estoniagamedev")} |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
<div className="flex items-center gap-2"> |
||||||
|
<div |
||||||
|
className="w-4 h-4 border border-gray-300" |
||||||
|
style={{ backgroundColor: "#a82838" }} |
||||||
|
></div> |
||||||
|
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]"> |
||||||
|
{t("expo.areas.tly")} |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
<div className="flex items-center gap-2"> |
||||||
|
<div |
||||||
|
className="w-4 h-4 border border-gray-300" |
||||||
|
style={{ backgroundColor: "#d12e7d" }} |
||||||
|
></div> |
||||||
|
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]"> |
||||||
|
{t("expo.areas.ittk")} |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
<div className="flex items-center gap-2"> |
||||||
|
<div |
||||||
|
className="w-4 h-4 border border-gray-300" |
||||||
|
style={{ backgroundColor: "#ff6347" }} |
||||||
|
></div> |
||||||
|
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]"> |
||||||
|
{t("expo.areas.info")} |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
<div className="flex items-center gap-2"> |
||||||
|
<div |
||||||
|
className="w-4 h-4 border border-gray-300" |
||||||
|
style={{ backgroundColor: "#228b22" }} |
||||||
|
></div> |
||||||
|
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]"> |
||||||
|
{t("expo.areas.gameup")} |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
<div className="flex items-center gap-2"> |
||||||
|
<div |
||||||
|
className="w-4 h-4 border border-gray-300" |
||||||
|
style={{ backgroundColor: "#20b2aa" }} |
||||||
|
></div> |
||||||
|
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]"> |
||||||
|
{t("expo.areas.photobooth")} |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
)} |
||||||
|
|
||||||
|
<div className="flex flex-col lg:flex-row gap-8 items-start"> |
||||||
|
<div className="relative w-full max-w-[800px]"> |
||||||
|
<div className="flex-shrink-0 border-3 border-[#1F5673] w-full relative"> |
||||||
|
<div ref={mountRef} className="w-full" /> |
||||||
|
{/* Left Arrow - Only show when on fuajee to go back to tudengimaja */} |
||||||
|
{currentView === "fuajee" && ( |
||||||
|
<button |
||||||
|
onClick={() => handleViewSwitch("tudengimaja")} |
||||||
|
className="group absolute left-4 bottom-4 p-4 md:p-6 transition-all duration-300 hover:scale-110 z-20 touch-manipulation min-h-[48px] min-w-[48px] flex items-center justify-center" |
||||||
|
title="Switch to Tudengimaja" |
||||||
|
aria-label="Switch to Tudengimaja view" |
||||||
|
> |
||||||
|
<span className="material-symbols-outlined !text-[clamp(2.5rem,2rem+2vw,4rem)] !font-bold text-[#007CAB] dark:text-[#00A3E0] group-hover:-translate-x-2 dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5] transition transform rotate-180"> |
||||||
|
arrow_right_alt |
||||||
|
</span> |
||||||
|
</button> |
||||||
|
)} |
||||||
|
|
||||||
|
{/* Right Arrow - Only show when on tudengimaja to go to fuajee */} |
||||||
|
{currentView === "tudengimaja" && ( |
||||||
|
<button |
||||||
|
onClick={() => handleViewSwitch("fuajee")} |
||||||
|
className="group absolute right-4 bottom-4 p-4 md:p-6 transition-all duration-300 hover:scale-110 z-20 touch-manipulation min-h-[48px] min-w-[48px] flex items-center justify-center" |
||||||
|
title="Switch to Fuajee" |
||||||
|
aria-label="Switch to Fuajee view" |
||||||
|
> |
||||||
|
<span className="material-symbols-outlined !text-[clamp(2.5rem,2rem+2vw,4rem)] !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 |
||||||
|
</span> |
||||||
|
</button> |
||||||
|
)} |
||||||
|
|
||||||
|
{currentView === "tudengimaja" && ( |
||||||
|
<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> |
||||||
|
</div> |
||||||
|
|
||||||
|
{/* Tooltip - only show for current view */} |
||||||
|
{hoveredRoom && |
||||||
|
((currentView === "tudengimaja" && |
||||||
|
[ |
||||||
|
roomNames.boardGames, |
||||||
|
roomNames.bar, |
||||||
|
roomNames.eval, |
||||||
|
roomNames.simRacing, |
||||||
|
roomNames.fighting, |
||||||
|
roomNames.lvlup, |
||||||
|
roomNames.redbull, |
||||||
|
].includes(hoveredRoom)) || |
||||||
|
(currentView === "fuajee" && |
||||||
|
[ |
||||||
|
roomNames.ityk, |
||||||
|
roomNames.tartuyk, |
||||||
|
roomNames.estoniagamedev, |
||||||
|
roomNames.info, |
||||||
|
roomNames.tly, |
||||||
|
roomNames.ittk, |
||||||
|
roomNames.photobooth, |
||||||
|
roomNames.gameup, |
||||||
|
].includes(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> |
||||||
|
); |
||||||
|
} |
||||||
@ -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> |
||||||
|
); |
||||||
|
} |
||||||
@ -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 wrap-break-word 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> |
||||||
|
); |
||||||
|
} |
||||||
@ -0,0 +1,106 @@ |
|||||||
|
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 translation keys
|
||||||
|
const rulesMap = { |
||||||
|
lol: { |
||||||
|
filePath: "src/data/rules/lol.md", |
||||||
|
titleKey: "rules.lolRules", |
||||||
|
}, |
||||||
|
cs2: { |
||||||
|
filePath: "src/data/rules/cs2.md", |
||||||
|
titleKey: "rules.cs2Rules", |
||||||
|
}, |
||||||
|
} as const; |
||||||
|
|
||||||
|
type RuleSlug = keyof typeof rulesMap; |
||||||
|
|
||||||
|
interface PageProps { |
||||||
|
params: Promise<{ slug: string; locale: string }>; |
||||||
|
} |
||||||
|
|
||||||
|
async function getRuleContent(slug: string) { |
||||||
|
if (!Object.keys(rulesMap).includes(slug)) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
const ruleConfig = rulesMap[slug as RuleSlug]; |
||||||
|
|
||||||
|
try { |
||||||
|
const file = Bun.file(ruleConfig.filePath); |
||||||
|
const content = await file.text(); |
||||||
|
return { |
||||||
|
content, |
||||||
|
titleKey: ruleConfig.titleKey, |
||||||
|
}; |
||||||
|
} catch (error) { |
||||||
|
console.error(`Error reading rule file for slug ${slug}:`, error); |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export default async function RulePage({ params }: PageProps) { |
||||||
|
const { slug, locale } = await params; |
||||||
|
setRequestLocale(locale); |
||||||
|
const t = await getTranslations({ locale }); |
||||||
|
const ruleData = await getRuleContent(slug); |
||||||
|
|
||||||
|
if (!ruleData) { |
||||||
|
notFound(); |
||||||
|
} |
||||||
|
|
||||||
|
const headingStyle = `text-5xl sm:text-6xl ${vipnagorgialla.className} font-bold uppercase italic text-[#2A2C3F] dark:text-[#EEE5E5]`; |
||||||
|
|
||||||
|
return ( |
||||||
|
<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`}> |
||||||
|
{t(ruleData.titleKey)} |
||||||
|
</h1> |
||||||
|
|
||||||
|
<div className="prose prose-lg dark:prose-invert max-w-none"> |
||||||
|
<ReactMarkdown |
||||||
|
components={ |
||||||
|
{ |
||||||
|
h1: (props) => ( |
||||||
|
<h1 className="text-3xl md:text-4xl font-bold my-4"> |
||||||
|
{props.children} |
||||||
|
</h1> |
||||||
|
), |
||||||
|
h2: (props) => ( |
||||||
|
<h2 className="text-2xl md:text-3xl font-semibold my-3"> |
||||||
|
{props.children} |
||||||
|
</h2> |
||||||
|
), |
||||||
|
ol: (props) => ( |
||||||
|
<ol className="list-none ml-6 md:text-xl"> |
||||||
|
{props.children} |
||||||
|
</ol> |
||||||
|
), |
||||||
|
ul: (props) => ( |
||||||
|
<ul className="list-disc ml-6 md:text-xl"> |
||||||
|
{props.children} |
||||||
|
</ul> |
||||||
|
), |
||||||
|
p: (props) => <p className="md:text-xl">{props.children}</p>, |
||||||
|
} as Components |
||||||
|
} |
||||||
|
> |
||||||
|
{ruleData.content} |
||||||
|
</ReactMarkdown> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<SectionDivider /> |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
export async function generateStaticParams() { |
||||||
|
return Object.keys(rulesMap).map((slug) => ({ |
||||||
|
slug, |
||||||
|
})); |
||||||
|
} |
||||||
@ -0,0 +1,63 @@ |
|||||||
|
import { vipnagorgialla } from "@/components/Vipnagorgialla"; |
||||||
|
import SectionDivider from "@/components/SectionDivider"; |
||||||
|
import { getTranslations, setRequestLocale } from "next-intl/server"; |
||||||
|
import NextLink from "next/link"; |
||||||
|
|
||||||
|
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]`; |
||||||
|
|
||||||
|
const boxTextStyle = `text-3xl ${vipnagorgialla.className} font-bold uppercase text-[#EEE5E5] pb-2`; |
||||||
|
|
||||||
|
return ( |
||||||
|
<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`}> |
||||||
|
{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"> |
||||||
|
<NextLink href="/kodukord"> |
||||||
|
<div className={`${boxStyle} bg-[#007CAB] py-20 px-8`}> |
||||||
|
<h2 className={`${boxTextStyle}`}>{t("rules.houseRules")}</h2> |
||||||
|
</div> |
||||||
|
</NextLink> |
||||||
|
|
||||||
|
<NextLink href="/reeglid/cs2"> |
||||||
|
<div className={`${boxStyle} bg-[#1F5673] py-20 px-8`}> |
||||||
|
<h2 className={`${boxTextStyle}`}>{t("rules.cs2Rules")}</h2> |
||||||
|
</div> |
||||||
|
</NextLink> |
||||||
|
|
||||||
|
<NextLink href="reeglid/lol"> |
||||||
|
<div className={`${boxStyle} bg-[#007CAB] py-20 px-8`}> |
||||||
|
<h2 className={`${boxTextStyle}`}>{t("rules.lolRules")}</h2> |
||||||
|
</div> |
||||||
|
</NextLink> |
||||||
|
|
||||||
|
{/* Minitourn. link coming soon*/} |
||||||
|
{/*<Link href="">*/} |
||||||
|
<div className={`${boxStyle} bg-[#1F5673] py-16 px-8`}> |
||||||
|
<h2 className={`${boxTextStyle}`}> |
||||||
|
{t("rules.miniRules")}
|
||||||
|
</h2> |
||||||
|
</div> |
||||||
|
{/*</Link>*/} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<SectionDivider /> |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
||||||
@ -0,0 +1,224 @@ |
|||||||
|
import { vipnagorgialla } from "@/components/Vipnagorgialla"; |
||||||
|
import Link from "next/link"; |
||||||
|
import Image from "next/image"; |
||||||
|
import { getTranslations, setRequestLocale } from "next-intl/server"; |
||||||
|
|
||||||
|
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"> |
||||||
|
{/* Title */} |
||||||
|
<div className="grid grid-cols-1 items-center justify-between mt-18 gap-12 pt-8"> |
||||||
|
<Image |
||||||
|
src="/tipilan-white.svg" |
||||||
|
width={850} |
||||||
|
height={120} |
||||||
|
alt="TipiLAN Logo" |
||||||
|
className="px-8 py-8 md:px-12 md:py-14 dark:hidden w-[max(300px,min(100%,850px))] h-auto" |
||||||
|
/> |
||||||
|
<Image |
||||||
|
src="/tipilan-dark.svg" |
||||||
|
width={850} |
||||||
|
height={120} |
||||||
|
alt="TipiLAN Logo" |
||||||
|
className="px-8 py-8 md:px-12 md:py-14 not-dark:hidden w-[max(300px,min(100%,850px))] h-auto2" |
||||||
|
/> |
||||||
|
<Link |
||||||
|
href="/ajakava" |
||||||
|
className="px-8 md:px-12 py-8 flex flex-col gap-4 border-b-3 border-t-3 group border-[#1F5673] hover:bg-[#007CAB] dark:hover:bg-[#00A3E0] transition" |
||||||
|
> |
||||||
|
<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] group-hover:text-black dark:group-hover:text-[#2A2C3F]`} |
||||||
|
> |
||||||
|
{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 |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
<div className="flex flex-col gap-4"> |
||||||
|
<span className="material-symbols-outlined !text-[clamp(2rem,1.5rem+1.5vw,3.5rem)] text-[#007CAB] dark:text-[#00A3E0] dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5]"> |
||||||
|
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"> |
||||||
|
{t("home.sections.schedule.description")} |
||||||
|
</p> |
||||||
|
</div> |
||||||
|
</Link> |
||||||
|
</div> |
||||||
|
{/* Stream iframe from Twitch */} |
||||||
|
<div className="border-[#1F5673] -ml-0.75 border-l-0 md:border-l-3 border-b-3 h-full pt-0 md:pt-16"> |
||||||
|
<iframe |
||||||
|
src="https://player.twitch.tv/?channel=tipilan_ee&parent=localhost&parent=tipilan.ee" |
||||||
|
height="100%" |
||||||
|
width="100%" |
||||||
|
className="w-full h-full min-h-[400px]" |
||||||
|
allow="autoplay; encrypted-media" |
||||||
|
></iframe> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
{/* Grid of buttons */} |
||||||
|
<div className="grid grid-cols-1 xl:grid-cols-2 border-[#1F5673]"> |
||||||
|
<Link |
||||||
|
href="/turniirid" |
||||||
|
className="px-8 md:px-12 py-8 flex flex-col gap-4 border-b-3 lg:border-r-3 group border-[#1F5673] hover:bg-[#007CAB] dark:hover:bg-[#00A3E0] transition" |
||||||
|
> |
||||||
|
<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`} |
||||||
|
> |
||||||
|
{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 |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div className="flex flex-col gap-4"> |
||||||
|
<span className="material-symbols-outlined !text-[clamp(2rem,1.5rem+1.5vw,3.5rem)] text-[#007CAB] dark:text-[#00A3E0] dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5]"> |
||||||
|
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"> |
||||||
|
{t("home.sections.tournaments.description")} |
||||||
|
</p> |
||||||
|
</div> |
||||||
|
</Link> |
||||||
|
<Link |
||||||
|
href="/messiala" |
||||||
|
className="px-8 md:px-12 py-8 flex flex-col gap-4 border-b-3 border-[#1F5673] group hover:bg-[#007CAB] dark:hover:bg-[#00A3E0] transition-all" |
||||||
|
> |
||||||
|
<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`} |
||||||
|
> |
||||||
|
{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 |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
<div className="flex flex-col gap-4"> |
||||||
|
<span className="material-symbols-outlined !text-[clamp(2rem,1.5rem+1.5vw,3.5rem)] text-[#007CAB] dark:text-[#00A3E0] dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5]"> |
||||||
|
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"> |
||||||
|
{t("home.sections.expo.description")} |
||||||
|
</p> |
||||||
|
</div> |
||||||
|
</Link> |
||||||
|
</div> |
||||||
|
{/* Date */} |
||||||
|
<Link |
||||||
|
href="/piletid" |
||||||
|
className={`p-8 md:p-12 flex flex-col ${vipnagorgialla.className} font-bold italic border-b-3 border-[#1F5673] hover:bg-[#007CAB] dark:hover:bg-[#00A3E0] group transition`} |
||||||
|
> |
||||||
|
<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"> |
||||||
|
{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 |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
<h2 className="text-[clamp(2.5rem,2.25rem+1.25vw,3.75rem)] text-[#007CAB] dark:text-[#00A3E0] dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5]"> |
||||||
|
24.-26. okt. |
||||||
|
</h2> |
||||||
|
</Link> |
||||||
|
{/* Sponsors */} |
||||||
|
<div |
||||||
|
className={`p-12 flex flex-col ${vipnagorgialla.className} font-bold italic border-b-3 border-[#1F5673]`} |
||||||
|
> |
||||||
|
<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"> |
||||||
|
{t("home.sections.poweredBy")} |
||||||
|
</h3> |
||||||
|
<div className="flex flex-row flex-wrap gap-8 md:gap-18 items-center justify-center"> |
||||||
|
<Link href="https://taltech.ee" target="_blank"> |
||||||
|
<Image |
||||||
|
src="/sponsors/taltech-color.png" |
||||||
|
alt="Taltech (Tallinna Tehnikaülikool)" |
||||||
|
width={192} |
||||||
|
height={192} |
||||||
|
className="object-contain" |
||||||
|
/> |
||||||
|
</Link> |
||||||
|
<Link href="https://www.redbull.com/ee-et/" target="_blank"> |
||||||
|
<Image |
||||||
|
src="/sponsors/redbull.png" |
||||||
|
alt="Redbull" |
||||||
|
width={80} |
||||||
|
height={80} |
||||||
|
className="object-contain" |
||||||
|
/> |
||||||
|
</Link> |
||||||
|
<Link href="https://www.alecoq.ee" target="_blank"> |
||||||
|
<Image |
||||||
|
src="/sponsors/alecoq.svg" |
||||||
|
alt="Alecoq" |
||||||
|
width={200} |
||||||
|
height={200} |
||||||
|
className="object-contain" |
||||||
|
/> |
||||||
|
</Link> |
||||||
|
<Link href="https://www.simracing.ee/" target="_blank"> |
||||||
|
<Image |
||||||
|
src="/sponsors/EVAL.png" |
||||||
|
alt="EVAL" |
||||||
|
width={200} |
||||||
|
height={200} |
||||||
|
className="object-contain" |
||||||
|
/> |
||||||
|
</Link> |
||||||
|
<Link href="https://balsnack.ee" target="_blank"> |
||||||
|
<Image |
||||||
|
src="/sponsors/balsnack.svg" |
||||||
|
alt="Balsnack" |
||||||
|
width={200} |
||||||
|
height={200} |
||||||
|
className="object-contain" |
||||||
|
/> |
||||||
|
</Link> |
||||||
|
<Link |
||||||
|
href="https://www.rara.ee/sundmused/interaktiivne-videomangude-muuseum-lvlup/" |
||||||
|
target="_blank" |
||||||
|
> |
||||||
|
<Image |
||||||
|
src="/sponsors/lvlup_logo_export.svg" |
||||||
|
alt="LVLup!" |
||||||
|
width={192} |
||||||
|
height={192} |
||||||
|
className="object-contain" |
||||||
|
/> |
||||||
|
</Link> |
||||||
|
<Link href="https://www.facebook.com/bfglOfficial" target="_blank"> |
||||||
|
<Image |
||||||
|
src="/sponsors/BFGL.png" |
||||||
|
alt="BFGL" |
||||||
|
width={192} |
||||||
|
height={192} |
||||||
|
className="object-contain" |
||||||
|
/> |
||||||
|
</Link> |
||||||
|
<Link href="https://www.tallinn.ee/et/haridus" target="_blank"> |
||||||
|
<Image |
||||||
|
src="/sponsors/tallinnaharidusamet.jpg" |
||||||
|
alt="Tallinna Haridusamet" |
||||||
|
width={192} |
||||||
|
height={192} |
||||||
|
className="object-contain" |
||||||
|
/> |
||||||
|
</Link> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
||||||
@ -0,0 +1,188 @@ |
|||||||
|
import { vipnagorgialla } from "@/components/Vipnagorgialla"; |
||||||
|
import Link from "next/link"; |
||||||
|
import Image from "next/image"; |
||||||
|
import { getTranslations, setRequestLocale } from "next-intl/server"; |
||||||
|
|
||||||
|
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
|
||||||
|
text-[#2A2C3F] dark:text-[#EEE5E5] md:m-16`}
|
||||||
|
> |
||||||
|
{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}`}> |
||||||
|
{t("tournaments.cs2.title")} |
||||||
|
</h2> |
||||||
|
<p |
||||||
|
className={ |
||||||
|
"text-2xl mb-4 text-neutral-500 group-hover:text-black" |
||||||
|
} |
||||||
|
> |
||||||
|
{t("tournaments.cs2.timing")} |
||||||
|
</p> |
||||||
|
<p className="text-balance"> |
||||||
|
{t("tournaments.cs2.description1")} |
||||||
|
</p> |
||||||
|
<br /> |
||||||
|
<p className="text-balance"> |
||||||
|
{t("tournaments.cs2.description2")} |
||||||
|
</p> |
||||||
|
<br /> |
||||||
|
|
||||||
|
<div className={"flex flex-row flex-wrap gap-8"}> |
||||||
|
<Link href="/reeglid/cs2" target="_blank"> |
||||||
|
<button |
||||||
|
className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic text-[#ECE5E5]`} |
||||||
|
> |
||||||
|
{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]`} |
||||||
|
> |
||||||
|
{t("tournaments.cs2.buyTicket")} |
||||||
|
</button> |
||||||
|
</a> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div className="hidden md:block"> |
||||||
|
<div> |
||||||
|
{/* Outside div needs to remain so that overflow won't occur*/} |
||||||
|
<Image |
||||||
|
src="/images/cs2_tournament_logo.png" |
||||||
|
alt="CS2 tournament" |
||||||
|
width={600} |
||||||
|
height={400} |
||||||
|
/> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
{/* LoL turniir */} |
||||||
|
<div className="hover:bg-[#007CAB] py-8 md:py-16 border-t-[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="hidden md:block"> |
||||||
|
<div> |
||||||
|
{/* Outside div needs to remain so that overflow won't occur*/} |
||||||
|
<Image |
||||||
|
src="/images/lol_tournament_logo.png" |
||||||
|
alt="LoL tournament" |
||||||
|
width={600} |
||||||
|
height={400} |
||||||
|
/> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div className="flex-auto text-right -skew-x-2 md:-skew-x-5"> |
||||||
|
<h2 className={`${headingStyle}`}> |
||||||
|
{t("tournaments.lol.title")} |
||||||
|
</h2> |
||||||
|
<p |
||||||
|
className={ |
||||||
|
"text-2xl mb-4 text-neutral-500 group-hover:text-black" |
||||||
|
} |
||||||
|
> |
||||||
|
{t("tournaments.lol.timing")} |
||||||
|
</p> |
||||||
|
<p className="text-balance"> |
||||||
|
{t("tournaments.lol.description1")} |
||||||
|
</p> |
||||||
|
<br /> |
||||||
|
<p className="text-balance"> |
||||||
|
{t("tournaments.lol.description2")} |
||||||
|
</p> |
||||||
|
<br /> |
||||||
|
<div className="flex flex-row flex-wrap gap-4 md:gap-8 justify-end"> |
||||||
|
<Link href="/reeglid/lol" target="_blank"> |
||||||
|
<button |
||||||
|
className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic text-[#ECE5E5]`} |
||||||
|
> |
||||||
|
{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]`} |
||||||
|
> |
||||||
|
{t("tournaments.lol.buyTicket")} |
||||||
|
</button> |
||||||
|
</a> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
{/* Mini-turniirid */} |
||||||
|
<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}`}> |
||||||
|
{t("tournaments.mini.title")} |
||||||
|
</h2> |
||||||
|
<p |
||||||
|
className={ |
||||||
|
"text-2xl mb-4 text-neutral-500 group-hover:text-black" |
||||||
|
} |
||||||
|
> |
||||||
|
{t("tournaments.mini.timing")} |
||||||
|
</p> |
||||||
|
<p className="text-balance"> |
||||||
|
{t("tournaments.mini.description1")} |
||||||
|
</p> |
||||||
|
<br /> |
||||||
|
<p className="text-balance"> |
||||||
|
{t("tournaments.mini.description2")} |
||||||
|
</p> |
||||||
|
<br /> |
||||||
|
<div className="flex flex-row flex-wrap gap-4 md:gap-8"> |
||||||
|
<Link href="/kodukord" target="_blank"> |
||||||
|
<button |
||||||
|
className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic text-[#ECE5E5]`} |
||||||
|
> |
||||||
|
{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]`} |
||||||
|
> |
||||||
|
{t("tournaments.mini.buyTicket")} |
||||||
|
</button> |
||||||
|
</a> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div className="hidden md:block"> |
||||||
|
<div> |
||||||
|
{/* Outside div needs to remain so that overflow won't occur*/} |
||||||
|
<Image |
||||||
|
src="/images/minitournament_logo.png" |
||||||
|
alt="mini tournaments" |
||||||
|
width={600} |
||||||
|
height={400} |
||||||
|
/> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
||||||
@ -1,72 +0,0 @@ |
|||||||
"use client"; |
|
||||||
|
|
||||||
import { useState } from "react"; |
|
||||||
import { vipnagorgialla } from "@/components/Vipnagorgialla"; |
|
||||||
import { scheduleData } from "@/data/timetable"; |
|
||||||
|
|
||||||
const tabs = Object.keys(scheduleData); |
|
||||||
|
|
||||||
export default function Timetable() { |
|
||||||
const [activeTab, setActiveTab] = useState(tabs[0]); |
|
||||||
const schedule = scheduleData[activeTab]; |
|
||||||
|
|
||||||
return ( |
|
||||||
<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} 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 gap-12" |
|
||||||
> |
|
||||||
<div |
|
||||||
className={` ${vipnagorgialla.className} text-[#00A3E0] text-5xl font-bold italic`} |
|
||||||
> |
|
||||||
{item.time} |
|
||||||
</div> |
|
||||||
<div> |
|
||||||
<div |
|
||||||
className={`${vipnagorgialla.className} text-4xl italic font-bold text-[#2A2C3F] dark:text-[#EEE5E5]`} |
|
||||||
> |
|
||||||
{item.title} |
|
||||||
</div> |
|
||||||
{item.description && ( |
|
||||||
<div className="text-2xl text-[#938BA1] dark:text-[#938BA1]"> |
|
||||||
{item.description} |
|
||||||
</div> |
|
||||||
)} |
|
||||||
{item.location && ( |
|
||||||
<div className="text-2xl text-[#938BA1] dark:text-[#938BA1]"> |
|
||||||
{item.location} |
|
||||||
</div> |
|
||||||
)} |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
))} |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
); |
|
||||||
} |
|
||||||
@ -1,63 +0,0 @@ |
|||||||
import { vipnagorgialla } from "@/components/Vipnagorgialla"; |
|
||||||
|
|
||||||
export default function Rulebook() { |
|
||||||
return ( |
|
||||||
<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`} |
|
||||||
> |
|
||||||
Kodukord |
|
||||||
</h1> |
|
||||||
<ol className="list-decimal ml-6 md:text-xl text-[#2A2C3F] dark:text-[#EEE5E5] y-4"> |
|
||||||
<li> |
|
||||||
Keelatud on: |
|
||||||
<ol className="list-[lower-alpha] ml-6 y-2"> |
|
||||||
<li>alkoholi ja uimastite omamine ja tarbimine ürituse vältel</li> |
|
||||||
<li>alkoholijoobes või uimastite mõju all viibimine üritusel</li> |
|
||||||
<li> |
|
||||||
suitsetamine (ka e-sigaret) selleks mitte ettenähtud kohtades – |
|
||||||
suitsetada võib suitsuruumis või õues vastava prügikasti juures |
|
||||||
<ul className="list-disc ml-6"> |
|
||||||
<li>suitsetamine alaealistel</li> |
|
||||||
<li>mokatubaka kasutamine TalTech-i territooriumil</li> |
|
||||||
</ul> |
|
||||||
</li> |
|
||||||
<li>külm- ja imitatsioonrelvad</li> |
|
||||||
<li> |
|
||||||
ürituse alal igasuguse vägivalla kasutamine teiste ja teiste vara |
|
||||||
suhtes |
|
||||||
</li> |
|
||||||
<li>teiste vara omavoliline kasutamine, näppimine</li> |
|
||||||
<li>turniiri reeglitele mittevastavalt mängimine</li> |
|
||||||
<li> |
|
||||||
omavoliline taristu (võrgu) näppimine – võrguprobleemidega tuleb |
|
||||||
pöörduda korraldajate poole |
|
||||||
</li> |
|
||||||
<li> |
|
||||||
mängimiseks ebavajalike seadmete ühendamine vooluvõrku (nt |
|
||||||
veekeetja, puhur, sirgendaja) |
|
||||||
</li> |
|
||||||
</ol> |
|
||||||
</li> |
|
||||||
<li>Iga külastaja vastutab enda asjade ja vara eest ise</li> |
|
||||||
<li> |
|
||||||
Korraldajale varalise kahju tekitanud külastaja on kohustatud korvama |
|
||||||
täies ulatuses tekitatud kahju |
|
||||||
</li> |
|
||||||
<li> |
|
||||||
Magamiseks ettenähtud ajal ja magamiseks ettenähtud ruumis tuleb olla |
|
||||||
vaikselt ja võimaldada kaas mängijatel magada |
|
||||||
</li> |
|
||||||
<li>Korraldajad ei vastuta külastajate eest</li> |
|
||||||
<li> |
|
||||||
Mängijad on TipiLAN-il kohustatud kinni pidama mängule seatud |
|
||||||
vanusepiirangutest |
|
||||||
</li> |
|
||||||
</ol> |
|
||||||
<p className="md:text-xl text-[#2A2C3F] dark:text-[#EEE5E5] y-4 mt-4"> |
|
||||||
NB! Reeglite rikkumise puhul on korraldajatel õigus mängija (koos tema |
|
||||||
meeskonnaga) eemaldada ja rakendada edasist keeldu TipiLAN-i üritustelt. |
|
||||||
</p> |
|
||||||
</div> |
|
||||||
); |
|
||||||
} |
|
||||||
@ -1,480 +0,0 @@ |
|||||||
"use client"; |
|
||||||
|
|
||||||
import { vipnagorgialla } from "@/components/Vipnagorgialla"; |
|
||||||
import * as THREE from "three"; |
|
||||||
import { useEffect, useRef, useState } from "react"; |
|
||||||
import { EyeClosed, Eye } from "lucide-react"; |
|
||||||
|
|
||||||
// Define interface for the ref with toggle function
|
|
||||||
interface MountRefCurrent extends HTMLDivElement { |
|
||||||
toggleDividers?: (show: boolean) => void; |
|
||||||
} |
|
||||||
|
|
||||||
export default function Expo() { |
|
||||||
const mountRef = useRef<MountRefCurrent | null>(null); |
|
||||||
const [hoveredRoom, setHoveredRoom] = useState<string | null>(null); |
|
||||||
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 }); |
|
||||||
const [showDividers, setShowDividers] = useState<boolean>(true); |
|
||||||
|
|
||||||
useEffect(() => { |
|
||||||
if (!mountRef.current) return; |
|
||||||
|
|
||||||
// Copy ref to variable to avoid stale closure in cleanup
|
|
||||||
const mountElement = mountRef.current; |
|
||||||
let dividersRef: THREE.Mesh[] = []; |
|
||||||
|
|
||||||
// Scene setup
|
|
||||||
const scene = new THREE.Scene(); |
|
||||||
scene.background = new THREE.Color(0x0e0f19); |
|
||||||
|
|
||||||
// Get responsive dimensions
|
|
||||||
const getResponsiveDimensions = () => { |
|
||||||
const container = mountRef.current; |
|
||||||
if (!container) return { width: 800, height: 600 }; |
|
||||||
|
|
||||||
const containerWidth = container.offsetWidth; |
|
||||||
const maxWidth = Math.min(containerWidth, 800); |
|
||||||
const width = Math.max(maxWidth, 300); // Minimum width
|
|
||||||
const height = (width * 600) / 800; // Maintain aspect ratio
|
|
||||||
|
|
||||||
return { width, height }; |
|
||||||
}; |
|
||||||
|
|
||||||
const { width, height } = getResponsiveDimensions(); |
|
||||||
|
|
||||||
// Isometric camera setup with responsive sizing
|
|
||||||
const aspect = width / height; |
|
||||||
const baseFrustumSize = 14; |
|
||||||
const frustumSize = width < 600 ? baseFrustumSize * 0.8 : baseFrustumSize; // Smaller frustum for mobile
|
|
||||||
const camera = new THREE.OrthographicCamera( |
|
||||||
(frustumSize * aspect) / -2, |
|
||||||
(frustumSize * aspect) / 2, |
|
||||||
frustumSize / 2, |
|
||||||
frustumSize / -2, |
|
||||||
1, |
|
||||||
1000, |
|
||||||
); |
|
||||||
|
|
||||||
// Position camera for isometric view
|
|
||||||
camera.position.set(10, 10, 14); |
|
||||||
camera.lookAt(-1.4, 0, 0); |
|
||||||
|
|
||||||
// Renderer
|
|
||||||
const renderer = new THREE.WebGLRenderer({ antialias: true }); |
|
||||||
renderer.setSize(width, height); |
|
||||||
renderer.shadowMap.enabled = true; |
|
||||||
renderer.shadowMap.type = THREE.PCFSoftShadowMap; |
|
||||||
mountElement.appendChild(renderer.domElement); |
|
||||||
|
|
||||||
// Raycaster for mouse interactions
|
|
||||||
const raycaster = new THREE.Raycaster(); |
|
||||||
const mouse = new THREE.Vector2(); |
|
||||||
|
|
||||||
// Lighting
|
|
||||||
const ambientLight = new THREE.AmbientLight(0x404040, 1.2); |
|
||||||
scene.add(ambientLight); |
|
||||||
|
|
||||||
const directionalLight = new THREE.DirectionalLight(0xffffff, 1.5); |
|
||||||
directionalLight.position.set(10, 10, 5); |
|
||||||
directionalLight.castShadow = false; |
|
||||||
directionalLight.shadow.mapSize.width = 2048; |
|
||||||
directionalLight.shadow.mapSize.height = 2048; |
|
||||||
scene.add(directionalLight); |
|
||||||
|
|
||||||
// Room colors and names
|
|
||||||
const roomColors = [ |
|
||||||
0xff6b35, // Orange - Mänguklubi
|
|
||||||
0x4ecdc4, // Turquoise - Baariala
|
|
||||||
0xffe66d, // Yellow - EVAL
|
|
||||||
0xe74c3c, // Red - Redbull
|
|
||||||
0x9b59b6, // Purple - Võitlusmängu ala
|
|
||||||
0x3498db, // Blue - Sony
|
|
||||||
0x2ecc71, // Green - Chillimisala
|
|
||||||
]; |
|
||||||
|
|
||||||
const roomNames = [ |
|
||||||
"Mänguklubi", |
|
||||||
"Baariala", |
|
||||||
"EVAL", |
|
||||||
"Redbull", |
|
||||||
"Võitlusmängu ala", |
|
||||||
"Sony", |
|
||||||
"Chillimisala", |
|
||||||
]; |
|
||||||
|
|
||||||
// Create individual rooms as rectangles with custom positions
|
|
||||||
const rooms: THREE.Mesh[] = []; |
|
||||||
const roomData: Array<{ |
|
||||||
mesh: THREE.Mesh; |
|
||||||
name: string; |
|
||||||
originalColor: number; |
|
||||||
originalScale: THREE.Vector3; |
|
||||||
}> = []; |
|
||||||
const dividers: THREE.Mesh[] = []; |
|
||||||
|
|
||||||
// Define rooms with custom positions, sizes and colors
|
|
||||||
const roomDefinitions = [ |
|
||||||
{ |
|
||||||
width: 7, |
|
||||||
height: 0.7, |
|
||||||
depth: 3, |
|
||||||
x: 2.5, |
|
||||||
z: 4, |
|
||||||
color: roomColors[0], |
|
||||||
name: roomNames[0], |
|
||||||
}, // Mänguklubi
|
|
||||||
// {
|
|
||||||
// width: 2.5,
|
|
||||||
// height: 0.7,
|
|
||||||
// depth: 0.7,
|
|
||||||
// x: 1,
|
|
||||||
// z: 0,
|
|
||||||
// color: roomColors[1],
|
|
||||||
// name: roomNames[1],
|
|
||||||
// }, // Baariala
|
|
||||||
{ |
|
||||||
width: 1.8, |
|
||||||
height: 0.7, |
|
||||||
depth: 1.5, |
|
||||||
x: 2.5, |
|
||||||
z: -3.5, |
|
||||||
color: roomColors[2], |
|
||||||
name: roomNames[2], |
|
||||||
}, // EVAL
|
|
||||||
{ |
|
||||||
width: 2.2, |
|
||||||
height: 0.7, |
|
||||||
depth: 4.5, |
|
||||||
x: 5, |
|
||||||
z: -2, |
|
||||||
color: roomColors[3], |
|
||||||
name: roomNames[3], |
|
||||||
}, // Redbull
|
|
||||||
{ |
|
||||||
width: 3, |
|
||||||
height: 0.7, |
|
||||||
depth: 1.3, |
|
||||||
x: 0, |
|
||||||
z: -3.5, |
|
||||||
color: roomColors[4], |
|
||||||
name: roomNames[4], |
|
||||||
}, // Võitlusmängu ala
|
|
||||||
{ |
|
||||||
width: 1.8, |
|
||||||
height: 0.7, |
|
||||||
depth: 1.5, |
|
||||||
x: -2.55, |
|
||||||
z: -3.5, |
|
||||||
color: roomColors[5], |
|
||||||
name: roomNames[5], |
|
||||||
}, // Sony
|
|
||||||
{ |
|
||||||
width: 4, |
|
||||||
height: 0.7, |
|
||||||
depth: 4, |
|
||||||
x: -5.5, |
|
||||||
z: -2.3, |
|
||||||
color: roomColors[6], |
|
||||||
name: roomNames[6], |
|
||||||
}, // Chillimisala
|
|
||||||
]; |
|
||||||
|
|
||||||
roomDefinitions.forEach((roomDef) => { |
|
||||||
const geometry = new THREE.BoxGeometry( |
|
||||||
roomDef.width, |
|
||||||
roomDef.height, |
|
||||||
roomDef.depth, |
|
||||||
); |
|
||||||
const material = new THREE.MeshLambertMaterial({ |
|
||||||
color: roomDef.color, |
|
||||||
}); |
|
||||||
|
|
||||||
const room = new THREE.Mesh(geometry, material); |
|
||||||
room.position.set(roomDef.x, roomDef.height / 2, roomDef.z); |
|
||||||
room.castShadow = true; |
|
||||||
room.receiveShadow = true; |
|
||||||
room.userData = { name: roomDef.name, originalColor: roomDef.color }; |
|
||||||
|
|
||||||
scene.add(room); |
|
||||||
rooms.push(room); |
|
||||||
roomData.push({ |
|
||||||
mesh: room, |
|
||||||
name: roomDef.name, |
|
||||||
originalColor: roomDef.color, |
|
||||||
originalScale: room.scale.clone(), |
|
||||||
}); |
|
||||||
}); |
|
||||||
|
|
||||||
// Create toggleable room dividers
|
|
||||||
const createTogglableDivider = ( |
|
||||||
width: number, |
|
||||||
height: number, |
|
||||||
depth: number, |
|
||||||
x: number, |
|
||||||
z: number, |
|
||||||
) => { |
|
||||||
const wallGeometry = new THREE.BoxGeometry(width, height, depth); |
|
||||||
const wallMaterial = new THREE.MeshLambertMaterial({ |
|
||||||
color: 0x555555, |
|
||||||
transparent: true, |
|
||||||
opacity: 0, |
|
||||||
}); |
|
||||||
|
|
||||||
const wall = new THREE.Mesh(wallGeometry, wallMaterial); |
|
||||||
wall.position.set(x, height / 2, z); |
|
||||||
wall.visible = false; |
|
||||||
scene.add(wall); |
|
||||||
dividers.push(wall); |
|
||||||
}; |
|
||||||
|
|
||||||
// Add strategic dividers between major areas
|
|
||||||
createTogglableDivider(10, 2, 2, -2.5, 1.5); // Wall between main entrance
|
|
||||||
createTogglableDivider(2, 2, 2, 5.5, 1.5); // Wall right next to Mänguklubi & Redbull
|
|
||||||
|
|
||||||
// Store dividers reference for later access
|
|
||||||
dividersRef = [...dividers]; |
|
||||||
|
|
||||||
// Ground plane
|
|
||||||
const groundGeometry = new THREE.PlaneGeometry(14, 10.5); |
|
||||||
const groundMaterial = new THREE.MeshLambertMaterial({ color: 0xcccccc }); |
|
||||||
const ground = new THREE.Mesh(groundGeometry, groundMaterial); |
|
||||||
ground.rotation.x = -Math.PI / 2; |
|
||||||
ground.position.x = -1.1; |
|
||||||
ground.position.y = -0.5; |
|
||||||
ground.receiveShadow = true; |
|
||||||
scene.add(ground); |
|
||||||
|
|
||||||
// Second ground plane
|
|
||||||
const groundGeometry2 = new THREE.PlaneGeometry(2, 7); |
|
||||||
const groundMaterial2 = new THREE.MeshLambertMaterial({ |
|
||||||
color: 0xcccccc, |
|
||||||
}); |
|
||||||
const ground2 = new THREE.Mesh(groundGeometry2, groundMaterial2); |
|
||||||
ground2.rotation.x = -Math.PI / 2; |
|
||||||
ground2.position.x = -12.2; |
|
||||||
ground2.position.y = -5; |
|
||||||
ground2.receiveShadow = true; |
|
||||||
scene.add(ground2); |
|
||||||
|
|
||||||
// Resize handler
|
|
||||||
const handleResize = () => { |
|
||||||
const { width: newWidth, height: newHeight } = getResponsiveDimensions(); |
|
||||||
|
|
||||||
// Update camera
|
|
||||||
const newAspect = newWidth / newHeight; |
|
||||||
const newFrustumSize = |
|
||||||
newWidth < 600 ? baseFrustumSize * 0.8 : baseFrustumSize; |
|
||||||
|
|
||||||
camera.left = (newFrustumSize * newAspect) / -2; |
|
||||||
camera.right = (newFrustumSize * newAspect) / 2; |
|
||||||
camera.top = newFrustumSize / 2; |
|
||||||
camera.bottom = newFrustumSize / -2; |
|
||||||
camera.updateProjectionMatrix(); |
|
||||||
|
|
||||||
// Update renderer
|
|
||||||
renderer.setSize(newWidth, newHeight); |
|
||||||
}; |
|
||||||
|
|
||||||
// Add resize event listener
|
|
||||||
window.addEventListener("resize", handleResize); |
|
||||||
|
|
||||||
// Mouse event handlers
|
|
||||||
const onMouseMove = (event: MouseEvent) => { |
|
||||||
const rect = renderer.domElement.getBoundingClientRect(); |
|
||||||
mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; |
|
||||||
mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; |
|
||||||
|
|
||||||
// Update mouse position for tooltip
|
|
||||||
setMousePosition({ x: event.clientX, y: event.clientY }); |
|
||||||
|
|
||||||
// Update raycaster
|
|
||||||
raycaster.setFromCamera(mouse, camera); |
|
||||||
const intersects = raycaster.intersectObjects(rooms); |
|
||||||
|
|
||||||
// Reset all rooms to original state
|
|
||||||
roomData.forEach(({ mesh, originalColor, originalScale }) => { |
|
||||||
(mesh.material as THREE.MeshLambertMaterial).color.setHex( |
|
||||||
originalColor, |
|
||||||
); |
|
||||||
mesh.scale.copy(originalScale); |
|
||||||
}); |
|
||||||
|
|
||||||
if (intersects.length > 0) { |
|
||||||
const hoveredMesh = intersects[0].object as THREE.Mesh; |
|
||||||
const roomInfo = roomData.find((r) => r.mesh === hoveredMesh); |
|
||||||
|
|
||||||
if (roomInfo) { |
|
||||||
// Apply hover effects
|
|
||||||
(hoveredMesh.material as THREE.MeshLambertMaterial).color.setHex( |
|
||||||
0xffffff, |
|
||||||
); |
|
||||||
hoveredMesh.scale.multiplyScalar(1.02); |
|
||||||
setHoveredRoom(roomInfo.name); |
|
||||||
} |
|
||||||
} else { |
|
||||||
setHoveredRoom(null); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
// Add mouse event listener
|
|
||||||
renderer.domElement.addEventListener("mousemove", onMouseMove); |
|
||||||
|
|
||||||
// Animation loop
|
|
||||||
const animate = () => { |
|
||||||
requestAnimationFrame(animate); |
|
||||||
|
|
||||||
// Gentle floating animation for rooms
|
|
||||||
rooms.forEach((room, index) => { |
|
||||||
const originalY = 0.25; // height / 2 for the room height of 0.5
|
|
||||||
const baseY = originalY + Math.sin(Date.now() * 0.001 + index) * 0.05; |
|
||||||
|
|
||||||
// Maintain current scale while updating Y position
|
|
||||||
room.position.y = baseY; |
|
||||||
}); |
|
||||||
|
|
||||||
renderer.render(scene, camera); |
|
||||||
}; |
|
||||||
|
|
||||||
animate(); |
|
||||||
|
|
||||||
// Function to toggle dividers
|
|
||||||
const toggleDividers = (show: boolean) => { |
|
||||||
dividersRef.forEach((divider) => { |
|
||||||
divider.visible = show; |
|
||||||
(divider.material as THREE.MeshLambertMaterial).opacity = show |
|
||||||
? 0.4 |
|
||||||
: 0; |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
// Expose toggle function to parent scope
|
|
||||||
mountElement.toggleDividers = toggleDividers; |
|
||||||
|
|
||||||
// Cleanup
|
|
||||||
return () => { |
|
||||||
window.removeEventListener("resize", handleResize); |
|
||||||
renderer.domElement.removeEventListener("mousemove", onMouseMove); |
|
||||||
if (mountElement && renderer.domElement) { |
|
||||||
mountElement.removeChild(renderer.domElement); |
|
||||||
} |
|
||||||
renderer.dispose(); |
|
||||||
}; |
|
||||||
}, []); |
|
||||||
|
|
||||||
// Update dividers when showDividers state changes
|
|
||||||
useEffect(() => { |
|
||||||
if (mountRef.current?.toggleDividers) { |
|
||||||
mountRef.current.toggleDividers(showDividers); |
|
||||||
} |
|
||||||
}, [showDividers]); |
|
||||||
|
|
||||||
return ( |
|
||||||
<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: "#ff6b35" }} |
|
||||||
></div> |
|
||||||
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]"> |
|
||||||
Mänguklubi |
|
||||||
</span> |
|
||||||
</div> |
|
||||||
<div className="items-center gap-2 hidden"> |
|
||||||
<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: "#e74c3c" }} |
|
||||||
></div> |
|
||||||
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]"> |
|
||||||
Redbull |
|
||||||
</span> |
|
||||||
</div> |
|
||||||
<div className="flex items-center gap-2"> |
|
||||||
<div |
|
||||||
className="w-4 h-4 border border-gray-300" |
|
||||||
style={{ backgroundColor: "#9b59b6" }} |
|
||||||
></div> |
|
||||||
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]"> |
|
||||||
Võitlusmängu ala |
|
||||||
</span> |
|
||||||
</div> |
|
||||||
<div className="flex items-center gap-2"> |
|
||||||
<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: "#2ecc71" }} |
|
||||||
></div> |
|
||||||
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]"> |
|
||||||
Chillimisala |
|
||||||
</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 |
|
||||||
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> |
|
||||||
); |
|
||||||
} |
|
||||||
@ -1,10 +1,29 @@ |
|||||||
import { vipnagorgialla } from "@/components/Vipnagorgialla"; |
import { vipnagorgialla } from "@/components/Vipnagorgialla"; |
||||||
|
import { ThemeProvider } from "@/components/Theme-provider"; |
||||||
|
|
||||||
export default function NotFound() { |
export default function NotFound() { |
||||||
return ( |
return ( |
||||||
<div className="flex flex-col min-h-[90vh] p-12 justify-center items-center"> |
<ThemeProvider |
||||||
<h1 className={`text-7xl ${vipnagorgialla.className} font-bold italic uppercase text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 mb-4`}>404</h1> |
attribute="class" |
||||||
<p className="text-2xl text-[#2A2C3F] dark:text-[#EEE5E5]">Seda lehte me ei leidnud.</p> |
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> |
||||||
); |
</div> |
||||||
|
</ThemeProvider> |
||||||
|
); |
||||||
} |
} |
||||||
|
|||||||
@ -1,96 +0,0 @@ |
|||||||
import { vipnagorgialla } from "@/components/Vipnagorgialla"; |
|
||||||
import Link from "next/link"; |
|
||||||
|
|
||||||
export default function Tickets() { |
|
||||||
return ( |
|
||||||
<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> |
|
||||||
); |
|
||||||
} |
|
||||||
@ -1,44 +0,0 @@ |
|||||||
import { vipnagorgialla } from "@/components/Vipnagorgialla"; |
|
||||||
import path from "node:path"; |
|
||||||
import fs from "node:fs/promises"; |
|
||||||
import ReactMarkdown from "react-markdown"; |
|
||||||
|
|
||||||
type Props = { |
|
||||||
params: Promise<{ slug: string }>; |
|
||||||
}; |
|
||||||
|
|
||||||
export default async function RulePage({ params }: Props) { |
|
||||||
const { slug } = await params; |
|
||||||
|
|
||||||
const filePath = path.join(process.cwd(), "src/data/rules", `${slug}.md`); |
|
||||||
let file: string; |
|
||||||
|
|
||||||
try { |
|
||||||
file = await fs.readFile(filePath, "utf8"); |
|
||||||
} catch { |
|
||||||
file = `# ${slug.toUpperCase()} REEGLID\n\nSisu hetkel puudub.`; |
|
||||||
} |
|
||||||
|
|
||||||
const data = { title: undefined as string | undefined }; |
|
||||||
|
|
||||||
return ( |
|
||||||
<> |
|
||||||
<h1 |
|
||||||
className={`not-prose ${vipnagorgialla.className} font-bold italic uppercase text-[64px] leading-[96px] tracking-[-0.02em] text-[#2A2C3F] dark:text-[#EEE5E5] mx-auto mt-16 mb-6 px-8`} |
|
||||||
> |
|
||||||
{data.title || `${slug.toUpperCase()} REEGLID`} |
|
||||||
</h1> |
|
||||||
|
|
||||||
<div |
|
||||||
className={`mx-auto px-8 font-worksans
|
|
||||||
[&_ol]:ml-6 |
|
||||||
[&_ol_ol]:ml-10 |
|
||||||
[&_ol_ol_ol]:ml-14 |
|
||||||
[&_h2]:font-bold |
|
||||||
`}
|
|
||||||
> |
|
||||||
<ReactMarkdown>{file}</ReactMarkdown> |
|
||||||
</div> |
|
||||||
</> |
|
||||||
); |
|
||||||
} |
|
||||||
@ -1,63 +0,0 @@ |
|||||||
import {vipnagorgialla} from "@/components/Vipnagorgialla"; |
|
||||||
import Link from "next/link"; |
|
||||||
|
|
||||||
export default function RulesMenu() { |
|
||||||
const headingStyle = `text-5xl sm:text-6xl ${vipnagorgialla.className} font-bold italic text-[#2A2C3F] dark:text-[#EEE5E5]`; |
|
||||||
|
|
||||||
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]`; |
|
||||||
|
|
||||||
const boxTextStyle = `text-3xl ${vipnagorgialla.className} font-bold uppercase text-[#EEE5E5] pb-2`; |
|
||||||
|
|
||||||
const SectionDivider = () => <div className="border-b-[3px] border-[#1F5673] w-full"/>; |
|
||||||
|
|
||||||
return ( |
|
||||||
<div> |
|
||||||
<div className="flex flex-col md:m-16"> |
|
||||||
<h1 className={`${headingStyle} mt-8 md:mt-16`}> |
|
||||||
REEGLID |
|
||||||
</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"> |
|
||||||
<div className={`${boxStyle} bg-[#007CAB] py-20`}> |
|
||||||
<h2 className={`${boxTextStyle}`}> |
|
||||||
Kodukord |
|
||||||
</h2> |
|
||||||
</div> |
|
||||||
</Link> |
|
||||||
|
|
||||||
<Link href="/reeglid/cs2"> |
|
||||||
<div className={`${boxStyle} bg-[#1F5673] py-20`}> |
|
||||||
<h2 className={`${boxTextStyle}`}> |
|
||||||
CS2 reeglid |
|
||||||
</h2> |
|
||||||
</div> |
|
||||||
</Link> |
|
||||||
|
|
||||||
|
|
||||||
<Link href="reeglid/lol"> |
|
||||||
<div className={`${boxStyle} bg-[#007CAB] py-20`}> |
|
||||||
<h2 className={`${boxTextStyle}`}> |
|
||||||
LoL reeglid |
|
||||||
</h2> |
|
||||||
</div> |
|
||||||
</Link> |
|
||||||
|
|
||||||
{/* Minitourn. link coming soon*/} |
|
||||||
{/*<Link href="">*/}
|
|
||||||
<div |
|
||||||
className={`${boxStyle} bg-[#1F5673] py-16`}> |
|
||||||
<h2 className={`${boxTextStyle}`}> |
|
||||||
Miniturniiride reeglid |
|
||||||
</h2> |
|
||||||
</div> |
|
||||||
{/*</Link>*/} |
|
||||||
|
|
||||||
</div> |
|
||||||
</div> |
|
||||||
|
|
||||||
<SectionDivider /> |
|
||||||
|
|
||||||
</div> |
|
||||||
); |
|
||||||
} |
|
||||||
@ -1,184 +0,0 @@ |
|||||||
import { vipnagorgialla } from "@/components/Vipnagorgialla"; |
|
||||||
import Link from "next/link"; |
|
||||||
import Image from "next/image"; |
|
||||||
|
|
||||||
export default function Tourney() { |
|
||||||
const headingStyle = `text-3xl md:text-5xl lg:text-5xl ${vipnagorgialla.className} font-bold italic uppercase text-[#2A2C3F] dark:text-[#EEE5E5] -skew-x-2 md:-skew-x-5`; |
|
||||||
|
|
||||||
const SectionDivider = () => <hr className="border-t-[3px] border-[#1F5673]" />; |
|
||||||
|
|
||||||
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 text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 md:mt-16 mb-4 m-6 md:m-16`} |
|
||||||
> |
|
||||||
Turniirid |
|
||||||
</h1> |
|
||||||
|
|
||||||
{/*<p className="text-2xl text-[#2A2C3F] dark:text-[#EEE5E5]">*/} |
|
||||||
{/* Kui tahate oma oskusi proovile panna, siis vaadake siia tagasi! Rohkem*/} |
|
||||||
{/* infot lähiajal.*/} |
|
||||||
{/*</p>*/} |
|
||||||
<div className="flex flex-col gap-8 md:gap-16"> |
|
||||||
{/* CS2 turniir */} |
|
||||||
<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> |
|
||||||
<p className={"text-2xl mb-4 text-neutral-500"}> |
|
||||||
Toimumisaeg veel selgumisel |
|
||||||
</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! |
|
||||||
</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€. |
|
||||||
</p> |
|
||||||
<br /> |
|
||||||
|
|
||||||
<div className={"flex flex-row flex-wrap gap-8"}> |
|
||||||
<Link href="/reeglid/cs2" target="_blank"> |
|
||||||
<button |
|
||||||
className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic`} |
|
||||||
> |
|
||||||
LOE REEGLEID |
|
||||||
</button> |
|
||||||
</Link> |
|
||||||
<a 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> |
|
||||||
</a> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
<div className="hidden md:block"> |
|
||||||
<div className="-skew-x-2 md:-skew-x-5"> |
|
||||||
{/* Image needs to be the div that has the skew. Outside div needs to remain so that overflow wont occur*/} |
|
||||||
<Image |
|
||||||
src="/images/cs2_tournament_logo.png" |
|
||||||
alt="CS2 tournament" |
|
||||||
width={600} |
|
||||||
height={400} |
|
||||||
/> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
|
|
||||||
<SectionDivider /> |
|
||||||
|
|
||||||
|
|
||||||
{/* LoL turniir */} |
|
||||||
<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="hidden md:block"> |
|
||||||
<div className="-skew-x-2 md:-skew-x-5"> |
|
||||||
{/* Image needs to be the div that has the skew. Outside div needs to remain so that overflow wont occur*/} |
|
||||||
<Image |
|
||||||
src="/images/lol_tournament_logo.png" |
|
||||||
alt="LoL tournament" |
|
||||||
width={600} |
|
||||||
height={400} |
|
||||||
/> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
<div className="flex-auto text-right -skew-x-2 md:-skew-x-5"> |
|
||||||
<h2 className={`${headingStyle}`}> |
|
||||||
LoL turniir |
|
||||||
</h2> |
|
||||||
<p className={"text-2xl mb-4 text-neutral-500"}> |
|
||||||
Toimumisaeg veel selgumisel |
|
||||||
</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! |
|
||||||
</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€. |
|
||||||
</p> |
|
||||||
<br /> |
|
||||||
<div className="flex flex-row flex-wrap gap-4 md:gap-8 justify-end"> |
|
||||||
<Link href="/kodukord" target="_blank"> |
|
||||||
<button |
|
||||||
className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic`} |
|
||||||
> |
|
||||||
LOE REEGLEID |
|
||||||
</button> |
|
||||||
</Link> |
|
||||||
<a 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> |
|
||||||
</a> |
|
||||||
</div> |
|
||||||
|
|
||||||
|
|
||||||
</div> |
|
||||||
</div> |
|
||||||
|
|
||||||
<SectionDivider /> |
|
||||||
|
|
||||||
{/* Mini-turniirid */} |
|
||||||
<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> |
|
||||||
<p className={"text-2xl mb-4 text-neutral-500"}> |
|
||||||
Toimumisaeg veel selgumisel |
|
||||||
</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. |
|
||||||
</p> |
|
||||||
<br /> |
|
||||||
<p className="text-balance"> |
|
||||||
Auhinnafond on kõigi turniiride peale 1250€ ja reeglina saab rahalise auhinna miniturniiri võitja. |
|
||||||
</p> |
|
||||||
<br /> |
|
||||||
<div className="flex flex-row flex-wrap gap-4 md:gap-8"> |
|
||||||
<Link href="/kodukord" target="_blank"> |
|
||||||
<button |
|
||||||
className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic`} |
|
||||||
> |
|
||||||
LOE REEGLEID |
|
||||||
</button> |
|
||||||
</Link> |
|
||||||
<a 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> |
|
||||||
</a> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
<div className="hidden md:block"> |
|
||||||
<div className="-skew-x-2 md:-skew-x-5"> |
|
||||||
{/* Image needs to be the div that has the skew. Outside div needs to remain so that overflow wont occur*/} |
|
||||||
<Image |
|
||||||
src="/images/minitournament_logo.png" |
|
||||||
alt="mini tournaments" |
|
||||||
width={600} |
|
||||||
height={400} |
|
||||||
/> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
|
|
||||||
<SectionDivider /> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
); |
|
||||||
} |
|
||||||
@ -1,111 +1,134 @@ |
|||||||
import { SiDiscord, SiInstagram, SiFacebook } from "react-icons/si"; |
import { SiDiscord, SiInstagram, SiFacebook } from "react-icons/si"; |
||||||
import Image from "next/image"; |
import Image from "next/image"; |
||||||
|
import { useTranslations } from "next-intl"; |
||||||
|
|
||||||
// Fonts
|
// Fonts
|
||||||
import { vipnagorgialla } from "@/components/Vipnagorgialla"; |
import { vipnagorgialla } from "@/components/Vipnagorgialla"; |
||||||
|
|
||||||
const Footer = () => ( |
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"> |
const t = useTranslations(); |
||||||
<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"> |
return ( |
||||||
<Image |
<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"> |
||||||
src="/tipilan-white.svg" |
<div className="flex md:items-center gap-8 md:gap-0 justify-between flex-col md:flex-row"> |
||||||
width={250} |
<div className="flex flex-col items-start md:items-center"> |
||||||
height={36} |
<Image |
||||||
alt="TipiLAN Logo" |
src="/tipilan-white.svg" |
||||||
className="h-9 dark:hidden" |
width={250} |
||||||
/> |
height={36} |
||||||
<Image |
alt="TipiLAN Logo" |
||||||
src="/tipilan-dark.svg" |
className="h-9 dark:hidden" |
||||||
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> |
<Image |
||||||
<a |
src="/tipilan-dark.svg" |
||||||
href="https://facebook.com/tipilan.ee" |
width={250} |
||||||
target="_blank" |
height={36} |
||||||
className="mx-4" |
alt="TipiLAN Logo" |
||||||
rel="noopener noreferrer" |
className="h-9 not-dark:hidden" |
||||||
> |
|
||||||
<SiFacebook |
|
||||||
title="Facebook" |
|
||||||
size={"2em"} |
|
||||||
className="text-[#2A2C3F] dark:text-[#EEE5E5]" |
|
||||||
/> |
/> |
||||||
</a> |
</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] hover:text-[#007CAB] hover:dark:text-[#00A3E0] transition" |
||||||
|
/> |
||||||
|
</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] hover:text-[#007CAB] hover:dark:text-[#00A3E0] transition" |
||||||
|
/> |
||||||
|
</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] hover:text-[#007CAB] hover:dark:text-[#00A3E0] transition" |
||||||
|
/> |
||||||
|
</a> |
||||||
|
</div> |
||||||
</div> |
</div> |
||||||
</div> |
<div className="flex flex-col"> |
||||||
<div className="flex flex-col"> |
<h2 |
||||||
<h2 |
className={`text-3xl sm:text-4xl ${vipnagorgialla.className} font-bold italic uppercase text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 mb-4`} |
||||||
className={`text-3xl sm:text-4xl ${vipnagorgialla.className} font-bold italic uppercase text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 mb-4`} |
> |
||||||
> |
{t("footer.contact")} |
||||||
Kontakt |
</h2> |
||||||
</h2> |
<div className="flex flex-row justify-between gap-4 items-center"> |
||||||
<div className="flex flex-row justify-between gap-4 items-center"> |
<div> |
||||||
<div> |
<h3 className="text-xl font-bold">{t("footer.studentUnion")}</h3> |
||||||
<h3 className="text-xl font-bold">IT-teaduskonna üliõpilaskogu</h3> |
<div className="flex flex-col gap-2 mt-2"> |
||||||
<div className="flex flex-col gap-2 mt-2"> |
<div className="flex flex-row gap-2"> |
||||||
<div className="flex flex-row gap-2"> |
<span className="material-symbols-outlined !font-bold text-[#007CAB] dark:text-[#00A3E0]"> |
||||||
<span className="material-symbols-outlined !font-bold text-[#007CAB] dark:text-[#00A3E0]"> |
mail |
||||||
mail |
</span> |
||||||
</span> |
<a href="mailto:kontakt@ituk.ee" className="underline"> |
||||||
<a href="mailto:kontakt@ituk.ee" className="underline"> |
tipilan@ituk.ee |
||||||
tipilan@ituk.ee |
</a> |
||||||
</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> |
</div> |
||||||
<div className="flex flex-row gap-2"> |
<h3 className="text-xl font-bold pt-4"> |
||||||
<span className="material-symbols-outlined !font-bold text-[#007CAB] dark:text-[#00A3E0]"> |
{t("footer.organization")} |
||||||
phone |
</h3> |
||||||
</span> |
<div> |
||||||
<a href="tel:+37256931193" className="underline"> |
<p> |
||||||
+372 5693 1193 |
{t("footer.registrationCode")}:{" "} |
||||||
</a> |
<span className="font-semibold text-[#007CAB] dark:text-[#00A3E0]"> |
||||||
|
80391807 |
||||||
|
</span> |
||||||
|
</p> |
||||||
|
<p className="">ICO-210, Raja tn 4c, Tallinn, Harjumaa, 12616</p> |
||||||
</div> |
</div> |
||||||
</div> |
</div> |
||||||
<h3 className="text-xl font-bold pt-4">MTÜ For Tsükkel</h3> |
</div> |
||||||
<div> |
<div className="block align-middle text-center pt-16"> |
||||||
<p className="text-[#aaa]"> |
{t("footer.madeBy")}{" "} |
||||||
Registrikood:{" "} |
<a |
||||||
<span className="font-semibold text-[#007CAB] dark:text-[#00A3E0]"> |
target="_blank" |
||||||
80391807 |
href="https://lapikud.ee/" |
||||||
</span> |
className="text-[#E3983E] font-bold" |
||||||
</p> |
> |
||||||
<p className="text-[#aaa]"> |
MTÜ Lapikud |
||||||
ICO-210, Raja tn 4c, Tallinn, Harjumaa, 12616 |
</a>{" "} |
||||||
</p> |
{t("footer.withHelpFrom")}{" "} |
||||||
</div> |
<a |
||||||
|
target="_blank" |
||||||
|
href="https://ituk.ee/" |
||||||
|
className="bg-[#7B1642] font-bold not-dark:text-white" |
||||||
|
> |
||||||
|
MTÜ For Tsükkel/ITÜK |
||||||
|
</a> |
||||||
</div> |
</div> |
||||||
</div> |
</div> |
||||||
</div> |
</div> |
||||||
</div> |
); |
||||||
); |
}; |
||||||
|
|
||||||
export default Footer; |
export default Footer; |
||||||
|
|||||||
@ -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> |
||||||
|
); |
||||||
|
} |
||||||
@ -0,0 +1,7 @@ |
|||||||
|
import React from 'react'; |
||||||
|
|
||||||
|
export default function SectionDivider() { |
||||||
|
return ( |
||||||
|
<hr className="border-t-[3px] border-[#1F5673]" /> |
||||||
|
); |
||||||
|
}; |
||||||
@ -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; |
|
||||||
@ -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] md:hover:translate-x-2 transition duration-150" |
||||||
|
> |
||||||
|
{item.label} |
||||||
|
</Link> |
||||||
|
))} |
||||||
|
</div> |
||||||
|
</> |
||||||
|
</> |
||||||
|
); |
||||||
|
} |
||||||
@ -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 SidebarLayoutServer from "./SidebarLayoutServer"; |
||||||
|
|
||||||
import { useState } from "react"; |
|
||||||
import Header from "./Header"; |
|
||||||
import Sidebar from "./Sidebar"; |
|
||||||
|
|
||||||
const SidebarParent = () => { |
const SidebarParent = () => { |
||||||
const [isOpen, setIsOpen] = useState(false); |
return ( |
||||||
const toggleSidebar = () => setIsOpen(!isOpen); |
<div className="fixed w-screen top-0 z-9999"> |
||||||
|
<SidebarLayoutServer /> |
||||||
return ( |
</div> |
||||||
<div className="fixed w-screen top-0 z-9999"> |
); |
||||||
<Header isOpen={isOpen} toggleSidebar={toggleSidebar} /> |
|
||||||
<Sidebar isOpen={isOpen} toggleSidebar={toggleSidebar}/> |
|
||||||
</div> |
|
||||||
); |
|
||||||
}; |
}; |
||||||
|
|
||||||
// This component is responsible for rendering the sidebar and header together.
|
// 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.
|
// Server-side translations are handled by SidebarLayoutServer.
|
||||||
|
|
||||||
export default SidebarParent; |
export default SidebarParent; |
||||||
|
|||||||
@ -0,0 +1,13 @@ |
|||||||
|
import { cn } from "@/lib/utils" |
||||||
|
|
||||||
|
function Skeleton({ className, ...props }: React.ComponentProps<"div">) { |
||||||
|
return ( |
||||||
|
<div |
||||||
|
data-slot="skeleton" |
||||||
|
className={cn("bg-accent animate-pulse rounded-md", className)} |
||||||
|
{...props} |
||||||
|
/> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
export { Skeleton } |
||||||
@ -0,0 +1,30 @@ |
|||||||
|
Üritusel osalemise kodukord kehtib kõigile, nii külastajatele kui võistlejatele. Kodukorra rikkumisel jätab TipiLAN endale õiguse osaleja ürituselt eemaldada ning vajadusel teavitada politseid. Alaealise osaleja puhul teavitame raskema kodukorra eiramise puhul tema vanemaid või eestkostjaid. |
||||||
|
|
||||||
|
# Osaleja meelespea |
||||||
|
|
||||||
|
1. Tulles vaheta oma pilet käepaela vastu. |
||||||
|
2. Osaleja peab olema vähemalt 16-aastane. Pileti kontrollija võib küsida Sinult dokumenti. |
||||||
|
3. Üritust pildistatakse ning filmitakse ja ürituse sisu kajastatakse erinevates meediakanalites. |
||||||
|
4. Kui Sul on ette nähtud TipiLANi poolne majutus, anna sellest teada pileti käepaela vastu vahetamisel. |
||||||
|
5. Kui tuled oma arvutiga, juhendatakse Sind käepaela saades, kuhu saad selle üles panna. |
||||||
|
|
||||||
|
# Ürituse kodukord |
||||||
|
|
||||||
|
1. Osaleja kohustub käituma viisakalt ning väärikalt ja austama teisi üritusel osalejaid. |
||||||
|
2. TipiLAN ei tolereeri: |
||||||
|
2.1. Vihakõnet rahvusliku, rassilise, soolise, seksuaalse või religioosse kuuluvuse, puude, välimuse või vanuse kohta; ahistamist, ähvardavat, solvavat või agressiivset käitumist, sellele õhutamist või selle pooldamist |
||||||
|
2.2. See kehtib nii ürituse alal (IRL) kui ka üritusega seotud online-keskkondades. |
||||||
|
3. Osaleja kohustub käituma ürituse hoone, inventari ja sisustuse suhtes heaperemehelikult. Keelatud on lõhkuda, määrida või viia mujale esemeid, mis ei kuulu osalejale. |
||||||
|
3.1. Kui osalejal on ette nähtud korraldajapoolne majutus, siis majutusalal on osalejal kohustus olla vaikselt ning lubada kaaslastel puhata. |
||||||
|
3.2. Majutusalale ei või kaasa kutsuda isikuid, kellel ei ole seal majutust ette nähtud. |
||||||
|
4. TipiLAN ei vastuta osaleja isikliku vara eest. |
||||||
|
4.1. Korraldajapoolne majutusala on lukustatav ning kõrvalisi isikuid sinna ei lubata, kuid sellest sõltumata tasub oma väärisesemetel silma peal hoida. |
||||||
|
4.2. Kui on tekkinud kahtlus, et on toimunud vargus, tuleb sellest koheselt teavitada korraldajat. |
||||||
|
4.3. Kaotatud asjade leidmisel palume anda need korraldajale või viia need *lost & found’i* (Merchilauda). |
||||||
|
5. Ürituse alal on keelatud suitsetada ning kasutada vape’i. Selleks on õues ette nähtud suitsetamise kohad. |
||||||
|
6. Üritusele ei tohi kaasa võtta illegaalseid aineid või ravimeid, terariistu, tulirelvi, lõhke- või süüteained ning muid esemeid, mis võivad osalejatele või teistele viga teha. |
||||||
|
7. Alaealisel osalejal on keelatud tarbida alkoholi või kasutada nikotiini sisaldavaid tooteid. |
||||||
|
7.1. Olles baarist alkoholi ostmas, on osalejal baaritöötaja nõudmisel kohustus näidata isikuttõendavat dokumenti. |
||||||
|
8. Osaleja kohustub käituma alkoholi suhtes vastutustundlikult. |
||||||
|
9. Keelatud on igasugune hasartmäng nii raha kui muude hüvede peale. |
||||||
|
|
||||||
@ -1,33 +1,68 @@ |
|||||||
export type ScheduleItem = { |
export type ScheduleItem = { |
||||||
time?: string; // Aeg on ajutine praegu kuna pole 100% kindlalt paigas
|
time?: string; // Aeg on ajutine praegu kuna pole 100% kindlalt paigas
|
||||||
title: string; |
titleKey: string; |
||||||
location: string; |
locationKey: string; |
||||||
description?: string; |
description?: string; |
||||||
}; |
}; |
||||||
|
|
||||||
export const scheduleData: Record<string, ScheduleItem[]> = { |
export const scheduleData: Record<string, ScheduleItem[]> = { |
||||||
"24. oktoober": [ |
oct24: [ |
||||||
{ |
{ |
||||||
title: "League of Legends põhiturniir", |
titleKey: "schedule.events.doorsOpen", |
||||||
location: "Aula", |
locationKey: "schedule.locations.registrationSetup", |
||||||
time: "-", |
time: "17:00", |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
title: "Miniturniirid", |
titleKey: "schedule.events.mainTournamentsStart", |
||||||
location: "Tudengimaja", |
locationKey: "schedule.locations.auditorium", |
||||||
time: "-", |
time: "20:00", |
||||||
|
}, |
||||||
|
{ |
||||||
|
titleKey: "schedule.events.miniTournamentsKickoff", |
||||||
|
locationKey: "schedule.locations.studentHouse", |
||||||
|
time: "18:00", |
||||||
|
}, |
||||||
|
{ |
||||||
|
titleKey: "schedule.events.fightingGamesStart", |
||||||
|
locationKey: "schedule.locations.studentHouse", |
||||||
|
time: "18:30", |
||||||
|
}, |
||||||
|
{ |
||||||
|
titleKey: "schedule.events.doorsClose", |
||||||
|
locationKey: "schedule.locations.auditoriumAndStudentHouse", |
||||||
|
time: "*01:00", |
||||||
}, |
}, |
||||||
], |
], |
||||||
"25. oktoober": [ |
oct25: [ |
||||||
|
{ |
||||||
|
titleKey: "schedule.events.doorsOpen", |
||||||
|
locationKey: "schedule.locations.auditoriumAndStudentHouse", |
||||||
|
time: "10:00", |
||||||
|
}, |
||||||
|
{ |
||||||
|
titleKey: "schedule.events.miniTournamentsStart", |
||||||
|
locationKey: "schedule.locations.studentHouse", |
||||||
|
time: "11:00", |
||||||
|
}, |
||||||
|
{ |
||||||
|
titleKey: "schedule.events.granblue", |
||||||
|
locationKey: "schedule.locations.studentHouse", |
||||||
|
time: "11:30", |
||||||
|
}, |
||||||
|
{ |
||||||
|
titleKey: "schedule.events.mainTournamentsStart", |
||||||
|
locationKey: "schedule.locations.auditorium", |
||||||
|
time: "12:00", |
||||||
|
}, |
||||||
{ |
{ |
||||||
title: "Counter-Strike 2 põhiturniir", |
titleKey: "schedule.events.granTurismo", |
||||||
location: "Aula", |
locationKey: "schedule.locations.studentHouse", |
||||||
time: "-", |
time: "20:00", |
||||||
}, |
}, |
||||||
{ |
{ |
||||||
title: "Miniturniirid", |
titleKey: "schedule.events.doorsClose", |
||||||
location: "Tudengimaja", |
locationKey: "schedule.locations.auditoriumAndStudentHouse", |
||||||
time: "-", |
time: "*01:00", |
||||||
}, |
}, |
||||||
], |
], |
||||||
}; |
}; |
||||||
|
|||||||
@ -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, |
||||||
|
}; |
||||||
|
}); |
||||||
@ -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: "/houserules", |
||||||
|
}, |
||||||
|
"/messiala": { |
||||||
|
et: "/messiala", |
||||||
|
en: "/expo", |
||||||
|
}, |
||||||
|
"/piletid": { |
||||||
|
et: "/piletid", |
||||||
|
en: "/tickets", |
||||||
|
}, |
||||||
|
"/reeglid": { |
||||||
|
et: "/reeglid", |
||||||
|
en: "/gamerules", |
||||||
|
}, |
||||||
|
"/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); |
||||||
@ -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|.*\\..*).*)' |
||||||
|
] |
||||||
|
}; |
||||||
@ -0,0 +1,227 @@ |
|||||||
|
{ |
||||||
|
"navigation": { |
||||||
|
"home": "Home", |
||||||
|
"schedule": "Schedule", |
||||||
|
"admin": "Admin", |
||||||
|
"houserules": "House rules", |
||||||
|
"expo": "Expo", |
||||||
|
"tickets": "Tickets", |
||||||
|
"rules": "Game rules", |
||||||
|
"stream": "Stream", |
||||||
|
"tournaments": "Tournaments" |
||||||
|
}, |
||||||
|
"common": { |
||||||
|
"loading": "Loading...", |
||||||
|
"error": "Error", |
||||||
|
"success": "Success", |
||||||
|
"save": "Save", |
||||||
|
"cancel": "Cancel", |
||||||
|
"delete": "Delete", |
||||||
|
"edit": "Edit", |
||||||
|
"close": "Close", |
||||||
|
"next": "Next", |
||||||
|
"previous": "Previous", |
||||||
|
"search": "Search", |
||||||
|
"filter": "Filter", |
||||||
|
"reset": "Reset", |
||||||
|
"theme": { |
||||||
|
"light": "Light", |
||||||
|
"dark": "Dark", |
||||||
|
"system": "System" |
||||||
|
}, |
||||||
|
"language": { |
||||||
|
"et": "Eesti", |
||||||
|
"en": "English" |
||||||
|
} |
||||||
|
}, |
||||||
|
"home": { |
||||||
|
"title": "TipiLAN 2025", |
||||||
|
"subtitle": "Estonia's largest student-organized LAN event!", |
||||||
|
"welcome": "Welcome to TipiLAN 2025!", |
||||||
|
"description": "Join us at Estonia's largest student-organized LAN event. Games, competitions and much more await you!", |
||||||
|
"sections": { |
||||||
|
"schedule": { |
||||||
|
"description": "TipiLAN is packed with exciting tournaments, mini-competitions and much more." |
||||||
|
}, |
||||||
|
"tournaments": { |
||||||
|
"description": "TipiLAN features massive CS2 and LoL tournaments with a prize pool of €10,000." |
||||||
|
}, |
||||||
|
"expo": { |
||||||
|
"description": "The TipiLAN expo area hosts companies, additional activities and lectures." |
||||||
|
}, |
||||||
|
"reserveSpot": "Reserve your spot today!", |
||||||
|
"poweredBy": "TipiLAN is powered by..." |
||||||
|
} |
||||||
|
}, |
||||||
|
"tickets": { |
||||||
|
"title": "TICKETS AND REGISTRATION", |
||||||
|
"buyNow": "Buy now", |
||||||
|
"soldOut": "Sold out", |
||||||
|
"available": "Available", |
||||||
|
"price": "Price", |
||||||
|
"includes": "Includes", |
||||||
|
"computerParticipant": { |
||||||
|
"title": "Computer Participant", |
||||||
|
"price": "8€", |
||||||
|
"features": [ |
||||||
|
"Personal desk, power and internet connection", |
||||||
|
"Access to demo area", |
||||||
|
"Tournament spectating", |
||||||
|
"Ability to participate in mini-tournaments" |
||||||
|
] |
||||||
|
}, |
||||||
|
"competitor": { |
||||||
|
"title": "Competitor", |
||||||
|
"price": "12-15€", |
||||||
|
"features": [ |
||||||
|
"Ability to participate in the CS2 or LoL tournament", |
||||||
|
"Personal desk, power and internet connection", |
||||||
|
"Access to demo area", |
||||||
|
"Tournament spectating", |
||||||
|
"Ability to participate in mini-tournaments" |
||||||
|
] |
||||||
|
}, |
||||||
|
"visitor": { |
||||||
|
"title": "Visitor", |
||||||
|
"price": "6€", |
||||||
|
"features": [ |
||||||
|
"Access to demo area", |
||||||
|
"Tournament spectating", |
||||||
|
"Ability to participate in mini-tournaments" |
||||||
|
] |
||||||
|
}, |
||||||
|
"buyTicket": "BUY TICKETS" |
||||||
|
}, |
||||||
|
"tournaments": { |
||||||
|
"title": "Tournaments", |
||||||
|
"register": "Register", |
||||||
|
"participants": "Participants", |
||||||
|
"prizePool": "Prize pool", |
||||||
|
"schedule": "Schedule", |
||||||
|
"rules": "Rules", |
||||||
|
"cs2": { |
||||||
|
"title": "CS2 Tournament", |
||||||
|
"timing": "Timing to be announced", |
||||||
|
"description1": "TipiLAN hosts one of Estonia's largest CS2 tournaments with a significant prize pool this fall. Grab your friends and experience the adrenaline rush!", |
||||||
|
"description2": "The prize pool is €5,250, distributed among the TOP3 teams. Each team member receives €600, €300, or €150 based on their placement.", |
||||||
|
"readRules": "READ RULES", |
||||||
|
"buyTicket": "BUY TICKETS" |
||||||
|
}, |
||||||
|
"lol": { |
||||||
|
"title": "LoL Tournament", |
||||||
|
"timing": "Timing to be announced", |
||||||
|
"description1": "TipiLAN hosts one of Estonia's largest LoL tournaments with a significant prize pool this fall. Grab your friends and experience the adrenaline rush!", |
||||||
|
"description2": "The prize pool is €3,500, distributed among the TOP3 teams. Each team member receives €400, €200, or €100 based on their placement.", |
||||||
|
"readRules": "READ RULES", |
||||||
|
"buyTicket": "BUY TICKETS" |
||||||
|
}, |
||||||
|
"mini": { |
||||||
|
"title": "Mini-tournaments", |
||||||
|
"timing": "Timing to be announced", |
||||||
|
"description1": "TipiLAN hosts various fun and competitive mini-tournaments. Mini-tournaments take place in the following games: SimRacing, Tekken, FIFA, Minecraft Bedwars, Buckshot Roulette, LostGamer and many more.", |
||||||
|
"description2": "The total prize pool for all tournaments is €1,250 and typically the mini-tournament winner receives a cash prize.", |
||||||
|
"readRules": "READ RULES", |
||||||
|
"buyTicket": "BUY TICKETS" |
||||||
|
} |
||||||
|
}, |
||||||
|
"schedule": { |
||||||
|
"title": "Schedule", |
||||||
|
"day": "Day", |
||||||
|
"time": "Time", |
||||||
|
"event": "Event", |
||||||
|
"location": "Location", |
||||||
|
"oct24": "October 24th", |
||||||
|
"oct25": "October 25th", |
||||||
|
"events": { |
||||||
|
"doorsOpen": "Doors open", |
||||||
|
"mainTournamentsStart": "Main tournaments begin", |
||||||
|
"miniTournamentsKickoff": "Mini-tournaments kick-off", |
||||||
|
"fightingGamesStart": "Fighting game tournaments start", |
||||||
|
"doorsClose": "Doors close", |
||||||
|
"miniTournamentsStart": "Mini-tournaments begin", |
||||||
|
"granblue": "Granblue tournament", |
||||||
|
"granTurismo": "Gran Turismo tournament" |
||||||
|
}, |
||||||
|
"locations": { |
||||||
|
"registrationSetup": "Registration and setup in auditorium", |
||||||
|
"auditorium": "Auditorium", |
||||||
|
"studentHouse": "Student House (Tudengimaja)", |
||||||
|
"auditoriumAndStudentHouse": "Auditorium and Student House", |
||||||
|
"entranceHall": "Entrance Hall" |
||||||
|
} |
||||||
|
}, |
||||||
|
"stream": { |
||||||
|
"title": "Stream", |
||||||
|
"live": "Live", |
||||||
|
"offline": "Offline", |
||||||
|
"watchNow": "Watch now" |
||||||
|
}, |
||||||
|
"footer": { |
||||||
|
"copyright": "© 2025 TipiLAN. All rights reserved.", |
||||||
|
"contact": "Contact", |
||||||
|
"privacy": "Privacy", |
||||||
|
"terms": "Terms", |
||||||
|
"studentUnion": "IT Faculty Student Council", |
||||||
|
"organization": "MTÜ For Tsükkel", |
||||||
|
"registrationCode": "Registration code", |
||||||
|
"madeBy": "The TipiLAN website is made with love by", |
||||||
|
"withHelpFrom": "with the help of" |
||||||
|
}, |
||||||
|
"notFound": { |
||||||
|
"title": "404", |
||||||
|
"message": "We couldn't find this page." |
||||||
|
}, |
||||||
|
"expo": { |
||||||
|
"title": "Expo Area", |
||||||
|
"description": "The TipiLAN expo area hosts companies, additional activities and lectures.", |
||||||
|
"areas": { |
||||||
|
"bar": "Bar Area", |
||||||
|
"boardGames": "Board Games Area", |
||||||
|
"simRacing": "Red Bull Sim Racing", |
||||||
|
"fighting": "Fighting Games Area", |
||||||
|
"photobooth": "Photo booth", |
||||||
|
"ityk": "TalTech IT Faculty Student Council", |
||||||
|
"tartuyk": "Tartu University", |
||||||
|
"estoniagamedev": "Estonia Gamedev", |
||||||
|
"info": "Information booth", |
||||||
|
"tly": "Tallinn University", |
||||||
|
"ittk": "TalTech School of Information Technologies", |
||||||
|
"gameup": "GameUP!" |
||||||
|
}, |
||||||
|
"hide": "Hide walls", |
||||||
|
"show": "Show walls" |
||||||
|
}, |
||||||
|
"rules": { |
||||||
|
"title": "Rules", |
||||||
|
"houseRules": "House Rules", |
||||||
|
"cs2Rules": "CS2 Rules", |
||||||
|
"lolRules": "LoL Rules", |
||||||
|
"miniRules": "Mini-tournament Rules" |
||||||
|
}, |
||||||
|
"admin": { |
||||||
|
"title": "Admin", |
||||||
|
"users": "Users", |
||||||
|
"teams": "Teams", |
||||||
|
"success": { |
||||||
|
"title": "Operation was successful!", |
||||||
|
"description": "Database data has been updated." |
||||||
|
}, |
||||||
|
"sync": { |
||||||
|
"title": "Do you want to update the database?", |
||||||
|
"description1": "This will pull current data from Fienta and replace", |
||||||
|
"all": "ALL", |
||||||
|
"description2": "existing data in the database!", |
||||||
|
"warning": "If you're not sure, click \"Cancel\".", |
||||||
|
"update": "Update" |
||||||
|
}, |
||||||
|
"roles": { |
||||||
|
"captain": "Captain", |
||||||
|
"teammate": "Teammate" |
||||||
|
}, |
||||||
|
"table": { |
||||||
|
"name": "Name", |
||||||
|
"members": "Members", |
||||||
|
"noMembers": "No members" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,227 @@ |
|||||||
|
{ |
||||||
|
"navigation": { |
||||||
|
"home": "Avaleht", |
||||||
|
"schedule": "Ajakava", |
||||||
|
"admin": "Haldus", |
||||||
|
"houserules": "Kodukord", |
||||||
|
"expo": "Messiala", |
||||||
|
"tickets": "Piletid", |
||||||
|
"rules": "Reeglid", |
||||||
|
"stream": "Striim", |
||||||
|
"tournaments": "Turniirid" |
||||||
|
}, |
||||||
|
"common": { |
||||||
|
"loading": "Laadimine...", |
||||||
|
"error": "Viga", |
||||||
|
"success": "Õnnestus", |
||||||
|
"save": "Salvesta", |
||||||
|
"cancel": "Tühista", |
||||||
|
"delete": "Kustuta", |
||||||
|
"edit": "Muuda", |
||||||
|
"close": "Sulge", |
||||||
|
"next": "Järgmine", |
||||||
|
"previous": "Eelmine", |
||||||
|
"search": "Otsi", |
||||||
|
"filter": "Filtreeri", |
||||||
|
"reset": "Lähtesta", |
||||||
|
"theme": { |
||||||
|
"light": "Hele", |
||||||
|
"dark": "Tume", |
||||||
|
"system": "Süsteemipõhine" |
||||||
|
}, |
||||||
|
"language": { |
||||||
|
"et": "Eesti", |
||||||
|
"en": "English" |
||||||
|
} |
||||||
|
}, |
||||||
|
"home": { |
||||||
|
"title": "TipiLAN 2025", |
||||||
|
"subtitle": "Eesti suurim tudengite korraldatud LAN!", |
||||||
|
"welcome": "Tere tulemast TipiLAN 2025 sündmusele!", |
||||||
|
"description": "Liitu meiega Eesti suurimal tudengite korraldatud LAN-üritusel. Mängud, võistlused ja palju muud ootavad sind!", |
||||||
|
"sections": { |
||||||
|
"schedule": { |
||||||
|
"description": "TipiLAN on pungil põnevatest turniiridest, mini-võistlustest ja paljust muust." |
||||||
|
}, |
||||||
|
"tournaments": { |
||||||
|
"description": "TipiLANil toimuvad suurejoonelised CS2 ja LoL turniirid, mille auhinnafond on 10 000€." |
||||||
|
}, |
||||||
|
"expo": { |
||||||
|
"description": "TipiLANi messialal paiknevad ettevõtted, lisategevused ja toimuvad loengud." |
||||||
|
}, |
||||||
|
"reserveSpot": "Broneeri oma koht juba täna!", |
||||||
|
"poweredBy": "TipiLANi tõmbab käima..." |
||||||
|
} |
||||||
|
}, |
||||||
|
"tickets": { |
||||||
|
"title": "PILETID JA REGISTREERIMINE", |
||||||
|
"buyNow": "Osta nüüd", |
||||||
|
"soldOut": "Välja müüdud", |
||||||
|
"available": "Saadaval", |
||||||
|
"price": "Hind", |
||||||
|
"includes": "Sisaldab", |
||||||
|
"computerParticipant": { |
||||||
|
"title": "Arvutiga osaleja", |
||||||
|
"price": "8€", |
||||||
|
"features": [ |
||||||
|
"Isiklik laud, voolu- ja internetiühendus", |
||||||
|
"Ligipääs demoalale", |
||||||
|
"Turniiride pealt vaatamine", |
||||||
|
"Võimalus osaleda miniturniiridel" |
||||||
|
] |
||||||
|
}, |
||||||
|
"competitor": { |
||||||
|
"title": "Võistleja", |
||||||
|
"price": "12-15€", |
||||||
|
"features": [ |
||||||
|
"Võimalus osaleda CS2 või LoL turniiril", |
||||||
|
"Isiklik laud, voolu- ja internetiühendus", |
||||||
|
"Ligipääs demoalale", |
||||||
|
"Turniiride pealt vaatamine", |
||||||
|
"Võimalus osaleda miniturniiridel" |
||||||
|
] |
||||||
|
}, |
||||||
|
"visitor": { |
||||||
|
"title": "Külastaja", |
||||||
|
"price": "6€", |
||||||
|
"features": [ |
||||||
|
"Ligipääs demoalale", |
||||||
|
"Turniiride pealt vaatamine", |
||||||
|
"Võimalus osaleda miniturniiridel" |
||||||
|
] |
||||||
|
}, |
||||||
|
"buyTicket": "OSTA PILET" |
||||||
|
}, |
||||||
|
"tournaments": { |
||||||
|
"title": "Turniirid", |
||||||
|
"register": "Registreeru", |
||||||
|
"participants": "Osalejad", |
||||||
|
"prizePool": "Auhinnafond", |
||||||
|
"schedule": "Ajakava", |
||||||
|
"rules": "Reeglid", |
||||||
|
"cs2": { |
||||||
|
"title": "CS2 turniir", |
||||||
|
"timing": "Toimumisaeg veel selgumisel", |
||||||
|
"description1": "TipiLANil toimub Eesti ühe suurima auhinnafondiga CS2 turniire juba sel sügisel. Haara kaasa sõbrad ja saa osa adrenaliinirohkest kogemusest!", |
||||||
|
"description2": "Auhinnafond on suuruses 5250€, mis jaotatakse TOP3 meeskonna vahel ära. Iga tiimiliige saab vastavalt saavutatud kohale auhinnaks kas 600€, 300€ või 150€.", |
||||||
|
"readRules": "LOE REEGLEID", |
||||||
|
"buyTicket": "OSTA PILET" |
||||||
|
}, |
||||||
|
"lol": { |
||||||
|
"title": "LoL turniir", |
||||||
|
"timing": "Toimumisaeg veel selgumisel", |
||||||
|
"description1": "TipiLANil toimub Eesti ühe suurima auhinnafondiga LoL turniire juba sel sügisel. Haara kaasa sõbrad ja saa osa adrenaliinirohkest kogemusest!", |
||||||
|
"description2": "Auhinnafond on suuruses 3500€, mis jaotatakse TOP3 meeskonna vahel ära. Iga tiimiliige saab vastavalt saavutatud kohale auhinnaks kas 400€, 200€ või 100€.", |
||||||
|
"readRules": "LOE REEGLEID", |
||||||
|
"buyTicket": "OSTA PILET" |
||||||
|
}, |
||||||
|
"mini": { |
||||||
|
"title": "Miniturniirid", |
||||||
|
"timing": "Toimumisaeg veel selgumisel", |
||||||
|
"description1": "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.", |
||||||
|
"description2": "Auhinnafond on kõigi turniiride peale 1250€ ja reeglina saab rahalise auhinna miniturniiri võitja.", |
||||||
|
"readRules": "LOE REEGLEID", |
||||||
|
"buyTicket": "OSTA PILET" |
||||||
|
} |
||||||
|
}, |
||||||
|
"schedule": { |
||||||
|
"title": "Ajakava", |
||||||
|
"day": "Päev", |
||||||
|
"time": "Aeg", |
||||||
|
"event": "Sündmus", |
||||||
|
"location": "Asukoht", |
||||||
|
"oct24": "24. oktoober", |
||||||
|
"oct25": "25. oktoober", |
||||||
|
"events": { |
||||||
|
"doorsOpen": "Uksed avatakse", |
||||||
|
"mainTournamentsStart": "Põhiturniirid algavad", |
||||||
|
"miniTournamentsKickoff": "Miniturniiride kick-off", |
||||||
|
"fightingGamesStart": "Fighting games turniiride algus", |
||||||
|
"doorsClose": "Uksed suletakse", |
||||||
|
"miniTournamentsStart": "Miniturniirid algavad", |
||||||
|
"granblue": "Granblue turniir", |
||||||
|
"granTurismo": "Gran Turismo turniir" |
||||||
|
}, |
||||||
|
"locations": { |
||||||
|
"registrationSetup": "Registreerimine ja setup aulas", |
||||||
|
"auditorium": "Aula", |
||||||
|
"studentHouse": "Tudengimaja", |
||||||
|
"auditoriumAndStudentHouse": "Aula ja Tudengimaja", |
||||||
|
"entranceHall": "Fuajee" |
||||||
|
} |
||||||
|
}, |
||||||
|
"stream": { |
||||||
|
"title": "Striim", |
||||||
|
"live": "Otse-eetris", |
||||||
|
"offline": "Väljas", |
||||||
|
"watchNow": "Vaata nüüd" |
||||||
|
}, |
||||||
|
"footer": { |
||||||
|
"copyright": "© 2025 TipiLAN. Kõik õigused kaitstud.", |
||||||
|
"contact": "Kontakt", |
||||||
|
"privacy": "Privaatsus", |
||||||
|
"terms": "Tingimused", |
||||||
|
"studentUnion": "IT-teaduskonna üliõpilaskogu", |
||||||
|
"organization": "MTÜ For Tsükkel", |
||||||
|
"registrationCode": "Registrikood", |
||||||
|
"madeBy": "TipiLANi veebileht on tehtud armastusega", |
||||||
|
"withHelpFrom": "poolt, kellele oli abiks" |
||||||
|
}, |
||||||
|
"notFound": { |
||||||
|
"title": "404", |
||||||
|
"message": "Seda lehte me ei leidnud." |
||||||
|
}, |
||||||
|
"expo": { |
||||||
|
"title": "Messiala", |
||||||
|
"description": "TipiLANi messialal paiknevad ettevõtted, lisategevused ja toimuvad loengud.", |
||||||
|
"areas": { |
||||||
|
"bar": "Baariala", |
||||||
|
"boardGames": "Lauamängude ala", |
||||||
|
"simRacing": "Red Bull Sim Racing", |
||||||
|
"fighting": "Võitlusmängu ala", |
||||||
|
"photobooth": "Fotoboks", |
||||||
|
"ityk": "IT-teaduskonna üliõpilaskogu", |
||||||
|
"tartuyk": "Tartu Ülikool", |
||||||
|
"estoniagamedev": "Eesti Gamedev", |
||||||
|
"info": "Infoboks", |
||||||
|
"tly": "Tallinna Ülikool", |
||||||
|
"ittk": "TalTech IT-Teaduskond", |
||||||
|
"gameup": "GameUP!" |
||||||
|
}, |
||||||
|
"hide": "Peida seinad", |
||||||
|
"show": "Näita seinu" |
||||||
|
}, |
||||||
|
"rules": { |
||||||
|
"title": "Reeglid", |
||||||
|
"houseRules": "Kodukord", |
||||||
|
"cs2Rules": "CS2 Reeglid", |
||||||
|
"lolRules": "LoL Reeglid", |
||||||
|
"miniRules": "Miniturniiride Reeglid" |
||||||
|
}, |
||||||
|
"admin": { |
||||||
|
"title": "Haldus", |
||||||
|
"users": "Kasutajaid", |
||||||
|
"teams": "Meeskondasid", |
||||||
|
"success": { |
||||||
|
"title": "Toiming oli edukas!", |
||||||
|
"description": "Andmebaasi andmed on uuendatud." |
||||||
|
}, |
||||||
|
"sync": { |
||||||
|
"title": "Kas soovite värskendada andmebaasi?", |
||||||
|
"description1": "See tõmbab Fientast praegused andmed ning asendab", |
||||||
|
"all": "KÕIK", |
||||||
|
"description2": "olemasolevad andmed andmebaasis!", |
||||||
|
"warning": "Kui sa ei ole kindel, vajuta \"Tühista\".", |
||||||
|
"update": "Värskenda" |
||||||
|
}, |
||||||
|
"roles": { |
||||||
|
"captain": "Kapten", |
||||||
|
"teammate": "Meeskonnaliige" |
||||||
|
}, |
||||||
|
"table": { |
||||||
|
"name": "Nimi", |
||||||
|
"members": "Liikmed", |
||||||
|
"noMembers": "Liikmeid puuduvad" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue