mirror of https://github.com/Lapikud/tipilan
parent
f0be2711ce
commit
5a7f4d87ee
5 changed files with 705 additions and 676 deletions
@ -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> |
); |
||||||
); |
|
||||||
} |
} |
||||||
|
|||||||
@ -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­TREERIMINE |
> |
||||||
</h1> |
PILETID JA REGIS­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/> |
||||||
|
</> |
||||||
|
); |
||||||
} |
} |
||||||
|
|||||||
Loading…
Reference in new issue