mirror of https://github.com/Lapikud/tipilan
Merge pull request #44 from Lapikud/43-bottom-section-divider-on-all-pages
43 bottom section divider on all pagespull/45/head
commit
94be9056c4
8 changed files with 717 additions and 680 deletions
@ -1,72 +1,77 @@ |
||||
"use client"; |
||||
|
||||
import { useState } from "react"; |
||||
import { vipnagorgialla } from "@/components/Vipnagorgialla"; |
||||
import { scheduleData } from "@/data/timetable"; |
||||
import {useState} from "react"; |
||||
import {vipnagorgialla} from "@/components/Vipnagorgialla"; |
||||
import {scheduleData} from "@/data/timetable"; |
||||
import SectionDivider from "@/components/SectionDivider"; |
||||
|
||||
const tabs = Object.keys(scheduleData); |
||||
|
||||
export default function Timetable() { |
||||
const [activeTab, setActiveTab] = useState(tabs[0]); |
||||
const schedule = scheduleData[activeTab]; |
||||
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> |
||||
return ( |
||||
<div> |
||||
<div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16"> |
||||
<h1 |
||||
className={`text-5xl sm:text-6xl ${vipnagorgialla.className} font-bold italic uppercase text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 md:mt-16 mb-8`} |
||||
> |
||||
Ajakava |
||||
</h1> |
||||
|
||||
{/* Tab menu */} |
||||
<div className="flex space-x-4 mb-8"> |
||||
{tabs.map((tab) => ( |
||||
<button |
||||
key={tab} |
||||
onClick={() => setActiveTab(tab)} |
||||
className={`${vipnagorgialla.className} 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} |
||||
{/* 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> |
||||
)} |
||||
{item.location && ( |
||||
<div className="text-2xl text-[#938BA1] dark:text-[#938BA1]"> |
||||
{item.location} |
||||
|
||||
{/* 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> |
||||
</div> |
||||
))} |
||||
</div> |
||||
</div> |
||||
); |
||||
|
||||
<SectionDivider/> |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
@ -1,480 +1,485 @@ |
||||
"use client"; |
||||
|
||||
import { vipnagorgialla } from "@/components/Vipnagorgialla"; |
||||
import {vipnagorgialla} from "@/components/Vipnagorgialla"; |
||||
import * as THREE from "three"; |
||||
import { useEffect, useRef, useState } from "react"; |
||||
import { EyeClosed, Eye } from "lucide-react"; |
||||
import {useEffect, useRef, useState} from "react"; |
||||
import {EyeClosed, Eye} from "lucide-react"; |
||||
import SectionDivider from "@/components/SectionDivider"; |
||||
|
||||
// Define interface for the ref with toggle function
|
||||
interface MountRefCurrent extends HTMLDivElement { |
||||
toggleDividers?: (show: boolean) => void; |
||||
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, |
||||
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, |
||||
); |
||||
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); |
||||
|
||||
// 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); |
||||
} |
||||
} 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]"> |
||||
}, [showDividers]); |
||||
|
||||
return ( |
||||
<div> |
||||
<div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16"> |
||||
<h1 |
||||
className={`text-4xl md:text-5xl lg:text-6xl ${vipnagorgialla.className} font-bold italic text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 md:mt-16 mb-4 uppercase`} |
||||
> |
||||
Messiala |
||||
</h1> |
||||
<div className="mb-6"> |
||||
<h2 className="text-2xl text-[#2A2C3F] dark:text-[#EEE5E5] mb-3"> |
||||
Tudengimaja |
||||
</h2> |
||||
<div className="flex flex-wrap gap-4 pb-4"> |
||||
<div className="flex items-center gap-2"> |
||||
<div |
||||
className="w-4 h-4 border border-gray-300" |
||||
style={{backgroundColor: "#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]"> |
||||
</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]"> |
||||
</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]"> |
||||
</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]"> |
||||
</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]"> |
||||
</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]"> |
||||
</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> |
||||
<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 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 SectionDivider from "@/components/SectionDivider"; |
||||
|
||||
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> |
||||
return ( |
||||
<div> |
||||
<div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16"> |
||||
<h1 |
||||
className={`text-4xl md:text-5xl lg:text-6xl ${vipnagorgialla.className} font-bold italic text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 md:mt-16 mb-4`} |
||||
> |
||||
PILETID JA REGIS­TREERIMINE |
||||
</h1> |
||||
<div |
||||
className="flex justify-center lg:items-center flex-col lg:flex-row gap-8 md:gap-12 flex-grow mb-16 md:mt-8 lg:mt-0"> |
||||
<div |
||||
className="bg-[#007CAB] -skew-x-2 md:-skew-x-5 text-white px-8 md:px-12 py-16 hover:scale-103 transition-all duration-150 w-full md:w-xl lg:w-[400px]"> |
||||
<h2 |
||||
className={`text-6xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-2`} |
||||
> |
||||
8€ |
||||
</h2> |
||||
<h3 |
||||
className={`text-3xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-4`} |
||||
> |
||||
Arvutiga osaleja |
||||
</h3> |
||||
<ul className="pl-4 mb-8 list-[square] marker:text-[#1F5673]"> |
||||
<li className="text-xl italic"> |
||||
Isiklik laud, voolu- ja internetiühendus |
||||
</li> |
||||
<li className="text-xl">Ligipääs demoalale</li> |
||||
<li className="text-xl">Turniiride pealt vaatamine</li> |
||||
<li className="text-xl">Võimalus osaleda miniturniiridel</li> |
||||
</ul> |
||||
<Link href="https://fienta.com/et/tipilan" target="_blank"> |
||||
<button |
||||
className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic`} |
||||
> |
||||
OSTA PILET |
||||
</button> |
||||
</Link> |
||||
</div> |
||||
<div |
||||
className="bg-[#1F5673] -skew-x-2 md:-skew-x-5 text-white px-8 md:px-12 py-16 hover:scale-103 transition-all duration-150 w-full md:w-xl lg:w-[400px]"> |
||||
<h2 |
||||
className={`text-6xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-2`} |
||||
> |
||||
12-15€ |
||||
</h2> |
||||
<h3 |
||||
className={`text-3xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-4`} |
||||
> |
||||
Võistleja |
||||
</h3> |
||||
<ul className="pl-4 mb-8 list-[square] marker:text-[#007CAB]"> |
||||
<li className="text-xl">Võimalus osaleda CS2 või LoL turniiril</li> |
||||
<li className="text-xl"> |
||||
Isiklik laud, voolu- ja internetiühendus |
||||
</li> |
||||
<li className="text-xl">Ligipääs demoalale</li> |
||||
<li className="text-xl">Turniiride pealt vaatamine</li> |
||||
<li className="text-xl">Võimalus osaleda miniturniiridel</li> |
||||
</ul> |
||||
<Link href="https://fienta.com/et/tipilan" target="_blank"> |
||||
<button |
||||
className={`px-4 py-2 bg-[#007CAB] cursor-pointer ${vipnagorgialla.className} font-bold italic`} |
||||
> |
||||
OSTA PILET |
||||
</button> |
||||
</Link> |
||||
</div> |
||||
|
||||
<div |
||||
className="bg-[#007CAB] -skew-x-2 md:-skew-x-5 text-white px-8 md:px-12 py-16 hover:scale-103 transition-all duration-150 w-full md:w-xl lg:w-[400px]"> |
||||
<h2 |
||||
className={`text-6xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-2`} |
||||
> |
||||
6€ |
||||
</h2> |
||||
<h3 |
||||
className={`text-3xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-4`} |
||||
> |
||||
Külastaja |
||||
</h3> |
||||
<ul className="pl-4 mb-8 list-[square] marker:text-[#1F5673]"> |
||||
<li className="text-xl">Ligipääs demoalale</li> |
||||
<li className="text-xl">Turniiride pealt vaatamine</li> |
||||
<li className="text-xl">Võimalus osaleda miniturniiridel</li> |
||||
</ul> |
||||
<Link href="https://fienta.com/et/tipilan" target="_blank"> |
||||
<button |
||||
className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic`} |
||||
> |
||||
OSTA PILET |
||||
</button> |
||||
</Link> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
<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> |
||||
<SectionDivider /> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
); |
||||
); |
||||
} |
||||
|
||||
@ -1,44 +1,49 @@ |
||||
import { vipnagorgialla } from "@/components/Vipnagorgialla"; |
||||
import {vipnagorgialla} from "@/components/Vipnagorgialla"; |
||||
import path from "node:path"; |
||||
import fs from "node:fs/promises"; |
||||
import ReactMarkdown from "react-markdown"; |
||||
import SectionDivider from "@/components/SectionDivider"; |
||||
|
||||
type Props = { |
||||
params: Promise<{ slug: string }>; |
||||
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> |
||||
</> |
||||
); |
||||
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 ( |
||||
<> |
||||
<div className="mb-16"> |
||||
<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> |
||||
</div> |
||||
|
||||
<SectionDivider/> |
||||
</> |
||||
); |
||||
} |
||||
|
||||
@ -0,0 +1,7 @@ |
||||
import React from 'react'; |
||||
|
||||
export default function SectionDivider() { |
||||
return ( |
||||
<hr className="border-t-[3px] border-[#1F5673]" /> |
||||
); |
||||
}; |
||||
Loading…
Reference in new issue