Merge pull request #44 from Lapikud/43-bottom-section-divider-on-all-pages

43 bottom section divider on all pages
pull/45/head
Renkar 4 months ago committed by GitHub
commit 94be9056c4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 125
      src/app/ajakava/page.tsx
  2. 79
      src/app/kodukord/page.tsx
  3. 911
      src/app/messiala/page.tsx
  4. 189
      src/app/piletid/page.tsx
  5. 77
      src/app/reeglid/[slug]/page.tsx
  6. 3
      src/app/reeglid/page.tsx
  7. 6
      src/app/turniirid/page.tsx
  8. 7
      src/components/SectionDivider.tsx

@ -1,72 +1,77 @@
"use client"; "use client";
import { useState } from "react"; import {useState} from "react";
import { vipnagorgialla } from "@/components/Vipnagorgialla"; import {vipnagorgialla} from "@/components/Vipnagorgialla";
import { scheduleData } from "@/data/timetable"; import {scheduleData} from "@/data/timetable";
import SectionDivider from "@/components/SectionDivider";
const tabs = Object.keys(scheduleData); const tabs = Object.keys(scheduleData);
export default function Timetable() { export default function Timetable() {
const [activeTab, setActiveTab] = useState(tabs[0]); const [activeTab, setActiveTab] = useState(tabs[0]);
const schedule = scheduleData[activeTab]; const schedule = scheduleData[activeTab];
return ( return (
<div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16"> <div>
<h1 <div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16">
className={`text-5xl sm:text-6xl ${vipnagorgialla.className} font-bold italic uppercase text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 md:mt-16 mb-8`} <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> Ajakava
</h1>
{/* Tab menu */} {/* Tab menu */}
<div className="flex space-x-4 mb-8"> <div className="flex space-x-4 mb-8">
{tabs.map((tab) => ( {tabs.map((tab) => (
<button <button
key={tab} key={tab}
onClick={() => setActiveTab(tab)} onClick={() => setActiveTab(tab)}
className={`${vipnagorgialla.className} uppercase italic px-4 py-2 text-lg font-semibold ${ className={`${vipnagorgialla.className} uppercase italic px-4 py-2 text-lg font-semibold ${
activeTab === tab activeTab === tab
? "bg-[#00A3E0] text-white" ? "bg-[#00A3E0] text-white"
: "bg-[#007CAB] dark:bg-[#007CAB] text-[#EEE5E5] hover:bg-[#00A3E0] dark:hover:bg-[#007CAB]" : "bg-[#007CAB] dark:bg-[#007CAB] text-[#EEE5E5] hover:bg-[#00A3E0] dark:hover:bg-[#007CAB]"
} transition-colors`} } transition-colors`}
> >
{tab} {tab}
</button> </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> </div>
)}
{item.location && ( {/* Schedule entries */}
<div className="text-2xl text-[#938BA1] dark:text-[#938BA1]"> <div className="space-y-6">
{item.location} {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>
)}
</div> </div>
</div>
))} <SectionDivider/>
</div> </div>
</div> );
);
} }

@ -3,48 +3,53 @@ import fs from "node:fs";
import path from "node:path"; import path from "node:path";
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm"; import remarkGfm from "remark-gfm";
import { vipnagorgialla } from "@/components/Vipnagorgialla"; import {vipnagorgialla} from "@/components/Vipnagorgialla";
import SectionDivider from "@/components/SectionDivider";
export const runtime = "nodejs"; // ensure fs is available (not Edge) export const runtime = "nodejs"; // ensure fs is available (not Edge)
export const dynamic = "force-static"; // read at build time export const dynamic = "force-static"; // read at build time
export default function Page() { export default function Page() {
const filePath = path.join(process.cwd(), "src/data", "kodukord.md"); const filePath = path.join(process.cwd(), "src/data", "kodukord.md");
const content = fs.readFileSync(filePath, "utf8"); const content = fs.readFileSync(filePath, "utf8");
return ( return (
<div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16"> <div>
{/* Page title (separate from markdown headings) */} <div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16">
<h1 {/* Page title (separate from markdown headings) */}
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`} <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> Kodukord
</h1>
<div className="prose prose-lg dark:prose-invert max-w-none"> <div className="prose prose-lg dark:prose-invert max-w-none">
<ReactMarkdown <ReactMarkdown
remarkPlugins={[remarkGfm]} remarkPlugins={[remarkGfm]}
components={{ components={{
h1: ({node, ...props}) => ( h1: ({node, ...props}) => (
<h1 className="text-3xl md:text-4xl font-bold my-4" {...props} /> <h1 className="text-3xl md:text-4xl font-bold my-4" {...props} />
), ),
h2: ({node, ...props}) => ( h2: ({node, ...props}) => (
<h2 className="text-2xl md:text-3xl font-semibold my-3" {...props} /> <h2 className="text-2xl md:text-3xl font-semibold my-3" {...props} />
), ),
ol: ({node, ...props}) => ( ol: ({node, ...props}) => (
<ol className="list-decimal ml-6 md:text-xl" {...props} /> <ol className="list-decimal ml-6 md:text-xl" {...props} />
), ),
ul: ({node, ...props}) => ( ul: ({node, ...props}) => (
<ul className="list-disc ml-6 md:text-xl" {...props} /> <ul className="list-disc ml-6 md:text-xl" {...props} />
), ),
p: ({node, ...props}) => ( p: ({node, ...props}) => (
<p className="md:text-xl" {...props} /> <p className="md:text-xl" {...props} />
), ),
}} }}
> >
{content} {content}
</ReactMarkdown> </ReactMarkdown>
</div> </div>
</div> </div>
);
<SectionDivider/>
</div>
);
} }

@ -1,480 +1,485 @@
"use client"; "use client";
import { vipnagorgialla } from "@/components/Vipnagorgialla"; import {vipnagorgialla} from "@/components/Vipnagorgialla";
import * as THREE from "three"; import * as THREE from "three";
import { useEffect, useRef, useState } from "react"; import {useEffect, useRef, useState} from "react";
import { EyeClosed, Eye } from "lucide-react"; import {EyeClosed, Eye} from "lucide-react";
import SectionDivider from "@/components/SectionDivider";
// Define interface for the ref with toggle function // Define interface for the ref with toggle function
interface MountRefCurrent extends HTMLDivElement { interface MountRefCurrent extends HTMLDivElement {
toggleDividers?: (show: boolean) => void; toggleDividers?: (show: boolean) => void;
} }
export default function Expo() { export default function Expo() {
const mountRef = useRef<MountRefCurrent | null>(null); const mountRef = useRef<MountRefCurrent | null>(null);
const [hoveredRoom, setHoveredRoom] = useState<string | null>(null); const [hoveredRoom, setHoveredRoom] = useState<string | null>(null);
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 }); const [mousePosition, setMousePosition] = useState({x: 0, y: 0});
const [showDividers, setShowDividers] = useState<boolean>(true); const [showDividers, setShowDividers] = useState<boolean>(true);
useEffect(() => { useEffect(() => {
if (!mountRef.current) return; if (!mountRef.current) return;
// Copy ref to variable to avoid stale closure in cleanup // Copy ref to variable to avoid stale closure in cleanup
const mountElement = mountRef.current; const mountElement = mountRef.current;
let dividersRef: THREE.Mesh[] = []; let dividersRef: THREE.Mesh[] = [];
// Scene setup // Scene setup
const scene = new THREE.Scene(); const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0e0f19); scene.background = new THREE.Color(0x0e0f19);
// Get responsive dimensions // Get responsive dimensions
const getResponsiveDimensions = () => { const getResponsiveDimensions = () => {
const container = mountRef.current; const container = mountRef.current;
if (!container) return { width: 800, height: 600 }; if (!container) return {width: 800, height: 600};
const containerWidth = container.offsetWidth; const containerWidth = container.offsetWidth;
const maxWidth = Math.min(containerWidth, 800); const maxWidth = Math.min(containerWidth, 800);
const width = Math.max(maxWidth, 300); // Minimum width const width = Math.max(maxWidth, 300); // Minimum width
const height = (width * 600) / 800; // Maintain aspect ratio const height = (width * 600) / 800; // Maintain aspect ratio
return { width, height }; return {width, height};
}; };
const { width, height } = getResponsiveDimensions(); const {width, height} = getResponsiveDimensions();
// Isometric camera setup with responsive sizing // Isometric camera setup with responsive sizing
const aspect = width / height; const aspect = width / height;
const baseFrustumSize = 14; const baseFrustumSize = 14;
const frustumSize = width < 600 ? baseFrustumSize * 0.8 : baseFrustumSize; // Smaller frustum for mobile const frustumSize = width < 600 ? baseFrustumSize * 0.8 : baseFrustumSize; // Smaller frustum for mobile
const camera = new THREE.OrthographicCamera( const camera = new THREE.OrthographicCamera(
(frustumSize * aspect) / -2, (frustumSize * aspect) / -2,
(frustumSize * aspect) / 2, (frustumSize * aspect) / 2,
frustumSize / 2, frustumSize / 2,
frustumSize / -2, frustumSize / -2,
1, 1,
1000, 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);
}); // Position camera for isometric view
camera.position.set(10, 10, 14);
if (intersects.length > 0) { camera.lookAt(-1.4, 0, 0);
const hoveredMesh = intersects[0].object as THREE.Mesh;
const roomInfo = roomData.find((r) => r.mesh === hoveredMesh); // Renderer
const renderer = new THREE.WebGLRenderer({antialias: true});
if (roomInfo) { renderer.setSize(width, height);
// Apply hover effects renderer.shadowMap.enabled = true;
(hoveredMesh.material as THREE.MeshLambertMaterial).color.setHex( renderer.shadowMap.type = THREE.PCFSoftShadowMap;
0xffffff, mountElement.appendChild(renderer.domElement);
);
hoveredMesh.scale.multiplyScalar(1.02); // Raycaster for mouse interactions
setHoveredRoom(roomInfo.name); 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);
} }
} else { }, [showDividers]);
setHoveredRoom(null);
} return (
}; <div>
<div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16">
// Add mouse event listener <h1
renderer.domElement.addEventListener("mousemove", onMouseMove); 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`}
>
// Animation loop Messiala
const animate = () => { </h1>
requestAnimationFrame(animate); <div className="mb-6">
<h2 className="text-2xl text-[#2A2C3F] dark:text-[#EEE5E5] mb-3">
// Gentle floating animation for rooms Tudengimaja
rooms.forEach((room, index) => { </h2>
const originalY = 0.25; // height / 2 for the room height of 0.5 <div className="flex flex-wrap gap-4 pb-4">
const baseY = originalY + Math.sin(Date.now() * 0.001 + index) * 0.05; <div className="flex items-center gap-2">
<div
// Maintain current scale while updating Y position className="w-4 h-4 border border-gray-300"
room.position.y = baseY; style={{backgroundColor: "#ff6b35"}}
}); ></div>
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
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 Mänguklubi
</span> </span>
</div> </div>
<div className="items-center gap-2 hidden"> <div className="items-center gap-2 hidden">
<div <div
className="w-4 h-4 border border-gray-300" className="w-4 h-4 border border-gray-300"
style={{ backgroundColor: "#4ecdc4" }} style={{backgroundColor: "#4ecdc4"}}
></div> ></div>
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]"> <span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
Baariala Baariala
</span> </span>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div <div
className="w-4 h-4 border border-gray-300" className="w-4 h-4 border border-gray-300"
style={{ backgroundColor: "#ffe66d" }} style={{backgroundColor: "#ffe66d"}}
></div> ></div>
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]"> <span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
EVAL EVAL
</span> </span>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div <div
className="w-4 h-4 border border-gray-300" className="w-4 h-4 border border-gray-300"
style={{ backgroundColor: "#e74c3c" }} style={{backgroundColor: "#e74c3c"}}
></div> ></div>
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]"> <span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
Redbull Redbull
</span> </span>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div <div
className="w-4 h-4 border border-gray-300" className="w-4 h-4 border border-gray-300"
style={{ backgroundColor: "#9b59b6" }} style={{backgroundColor: "#9b59b6"}}
></div> ></div>
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]"> <span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
Võitlusmängu ala Võitlusmängu ala
</span> </span>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div <div
className="w-4 h-4 border border-gray-300" className="w-4 h-4 border border-gray-300"
style={{ backgroundColor: "#3498db" }} style={{backgroundColor: "#3498db"}}
></div> ></div>
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]"> <span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
Sony Sony
</span> </span>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div <div
className="w-4 h-4 border border-gray-300" className="w-4 h-4 border border-gray-300"
style={{ backgroundColor: "#2ecc71" }} style={{backgroundColor: "#2ecc71"}}
></div> ></div>
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]"> <span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
Chillimisala Chillimisala
</span> </span>
</div> </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>
<SectionDivider />
</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,96 +1,105 @@
import { vipnagorgialla } from "@/components/Vipnagorgialla"; import {vipnagorgialla} from "@/components/Vipnagorgialla";
import Link from "next/link"; import Link from "next/link";
import SectionDivider from "@/components/SectionDivider";
export default function Tickets() { export default function Tickets() {
return ( return (
<div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16"> <div>
<h1 <div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16">
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`} <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&shy;TREERIMINE >
</h1> PILETID JA REGIS&shy;TREERIMINE
<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"> </h1>
<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]"> <div
<h2 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">
className={`text-6xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-2`} <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]">
8 <h2
</h2> className={`text-6xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-2`}
<h3 >
className={`text-3xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-4`} 8
> </h2>
Arvutiga osaleja <h3
</h3> className={`text-3xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-4`}
<ul className="pl-4 mb-8 list-[square] marker:text-[#1F5673]"> >
<li className="text-xl italic"> Arvutiga osaleja
Isiklik laud, voolu- ja internetiühendus </h3>
</li> <ul className="pl-4 mb-8 list-[square] marker:text-[#1F5673]">
<li className="text-xl">Ligipääs demoalale</li> <li className="text-xl italic">
<li className="text-xl">Turniiride pealt vaatamine</li> Isiklik laud, voolu- ja internetiühendus
<li className="text-xl">Võimalus osaleda miniturniiridel</li> </li>
</ul> <li className="text-xl">Ligipääs demoalale</li>
<Link href="https://fienta.com/et/tipilan" target="_blank"> <li className="text-xl">Turniiride pealt vaatamine</li>
<button <li className="text-xl">Võimalus osaleda miniturniiridel</li>
className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic`} </ul>
> <Link href="https://fienta.com/et/tipilan" target="_blank">
OSTA PILET <button
</button> className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic`}
</Link> >
</div> OSTA PILET
<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]"> </button>
<h2 </Link>
className={`text-6xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-2`} </div>
> <div
12-15 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> <h2
<h3 className={`text-6xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-2`}
className={`text-3xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-4`} >
> 12-15
Võistleja </h2>
</h3> <h3
<ul className="pl-4 mb-8 list-[square] marker:text-[#007CAB]"> className={`text-3xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-4`}
<li className="text-xl">Võimalus osaleda CS2 või LoL turniiril</li> >
<li className="text-xl"> Võistleja
Isiklik laud, voolu- ja internetiühendus </h3>
</li> <ul className="pl-4 mb-8 list-[square] marker:text-[#007CAB]">
<li className="text-xl">Ligipääs demoalale</li> <li className="text-xl">Võimalus osaleda CS2 või LoL turniiril</li>
<li className="text-xl">Turniiride pealt vaatamine</li> <li className="text-xl">
<li className="text-xl">Võimalus osaleda miniturniiridel</li> Isiklik laud, voolu- ja internetiühendus
</ul> </li>
<Link href="https://fienta.com/et/tipilan" target="_blank"> <li className="text-xl">Ligipääs demoalale</li>
<button <li className="text-xl">Turniiride pealt vaatamine</li>
className={`px-4 py-2 bg-[#007CAB] cursor-pointer ${vipnagorgialla.className} font-bold italic`} <li className="text-xl">Võimalus osaleda miniturniiridel</li>
> </ul>
OSTA PILET <Link href="https://fienta.com/et/tipilan" target="_blank">
</button> <button
</Link> className={`px-4 py-2 bg-[#007CAB] cursor-pointer ${vipnagorgialla.className} font-bold italic`}
</div> >
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]"> <div
<h2 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]">
className={`text-6xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-2`} <h2
> className={`text-6xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-2`}
6 >
</h2> 6
<h3 </h2>
className={`text-3xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-4`} <h3
> className={`text-3xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-4`}
Külastaja >
</h3> Külastaja
<ul className="pl-4 mb-8 list-[square] marker:text-[#1F5673]"> </h3>
<li className="text-xl">Ligipääs demoalale</li> <ul className="pl-4 mb-8 list-[square] marker:text-[#1F5673]">
<li className="text-xl">Turniiride pealt vaatamine</li> <li className="text-xl">Ligipääs demoalale</li>
<li className="text-xl">Võimalus osaleda miniturniiridel</li> <li className="text-xl">Turniiride pealt vaatamine</li>
</ul> <li className="text-xl">Võimalus osaleda miniturniiridel</li>
<Link href="https://fienta.com/et/tipilan" target="_blank"> </ul>
<button <Link href="https://fienta.com/et/tipilan" target="_blank">
className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic`} <button
> className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic`}
OSTA PILET >
</button> OSTA PILET
</Link> </button>
</Link>
</div>
</div>
</div>
<SectionDivider />
</div> </div>
</div> );
</div>
);
} }

@ -1,44 +1,49 @@
import { vipnagorgialla } from "@/components/Vipnagorgialla"; import {vipnagorgialla} from "@/components/Vipnagorgialla";
import path from "node:path"; import path from "node:path";
import fs from "node:fs/promises"; import fs from "node:fs/promises";
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
import SectionDivider from "@/components/SectionDivider";
type Props = { type Props = {
params: Promise<{ slug: string }>; params: Promise<{ slug: string }>;
}; };
export default async function RulePage({ params }: Props) { export default async function RulePage({params}: Props) {
const { slug } = await params; const {slug} = await params;
const filePath = path.join(process.cwd(), "src/data/rules", `${slug}.md`); const filePath = path.join(process.cwd(), "src/data/rules", `${slug}.md`);
let file: string; let file: string;
try { try {
file = await fs.readFile(filePath, "utf8"); file = await fs.readFile(filePath, "utf8");
} catch { } catch {
file = `# ${slug.toUpperCase()} REEGLID\n\nSisu hetkel puudub.`; file = `# ${slug.toUpperCase()} REEGLID\n\nSisu hetkel puudub.`;
} }
const data = { title: undefined as string | undefined }; const data = {title: undefined as string | undefined};
return ( return (
<> <>
<h1 <div className="mb-16">
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`} <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> {data.title || `${slug.toUpperCase()} REEGLID`}
</h1>
<div
className={`mx-auto px-8 font-worksans <div
[&_ol]:ml-6 className={`mx-auto px-8 font-worksans
[&_ol_ol]:ml-10 [&_ol]:ml-6
[&_ol_ol_ol]:ml-14 [&_ol_ol]:ml-10
[&_h2]:font-bold [&_ol_ol_ol]:ml-14
`} [&_h2]:font-bold
> `}
<ReactMarkdown>{file}</ReactMarkdown> >
</div> <ReactMarkdown>{file}</ReactMarkdown>
</> </div>
); </div>
<SectionDivider/>
</>
);
} }

@ -1,5 +1,6 @@
import {vipnagorgialla} from "@/components/Vipnagorgialla"; import {vipnagorgialla} from "@/components/Vipnagorgialla";
import Link from "next/link"; import Link from "next/link";
import SectionDivider from "@/components/SectionDivider";
export default function RulesMenu() { export default function RulesMenu() {
const headingStyle = `text-5xl sm:text-6xl ${vipnagorgialla.className} font-bold italic text-[#2A2C3F] dark:text-[#EEE5E5]`; const headingStyle = `text-5xl sm:text-6xl ${vipnagorgialla.className} font-bold italic text-[#2A2C3F] dark:text-[#EEE5E5]`;
@ -8,7 +9,7 @@ export default function RulesMenu() {
const boxTextStyle = `text-3xl ${vipnagorgialla.className} font-bold uppercase text-[#EEE5E5] pb-2`; const boxTextStyle = `text-3xl ${vipnagorgialla.className} font-bold uppercase text-[#EEE5E5] pb-2`;
const SectionDivider = () => <div className="border-b-[3px] border-[#1F5673] w-full"/>; // const SectionDivider = () => <div className="border-b-[3px] border-[#1F5673] w-full"/>;
return ( return (
<div> <div>

@ -1,11 +1,12 @@
import { vipnagorgialla } from "@/components/Vipnagorgialla"; import { vipnagorgialla } from "@/components/Vipnagorgialla";
import Link from "next/link"; import Link from "next/link";
import Image from "next/image"; import Image from "next/image";
import SectionDivider from "@/components/SectionDivider";
export default function Tourney() { export default function Tourney() {
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`; 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`;
const SectionDivider = () => <hr className="border-t-[3px] border-[#1F5673]" />; // const SectionDivider = () => <hr className="border-t-[3px] border-[#1F5673]" />;
return ( return (
<div className="flex flex-col min-h-[90vh] mt-16"> <div className="flex flex-col min-h-[90vh] mt-16">
@ -74,8 +75,7 @@ export default function Tourney() {
</div> </div>
<SectionDivider /> <SectionDivider />
{/* LoL turniir */} {/* 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="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="hidden md:block">

@ -0,0 +1,7 @@
import React from 'react';
export default function SectionDivider() {
return (
<hr className="border-t-[3px] border-[#1F5673]" />
);
};
Loading…
Cancel
Save