mirror of
https://github.com/Lapikud/tipilan.git
synced 2026-03-23 21:34:21 +00:00
Merge pull request #44 from Lapikud/43-bottom-section-divider-on-all-pages
43 bottom section divider on all pages
This commit is contained in:
@@ -1,72 +1,77 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import {useState} from "react";
|
||||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
import {vipnagorgialla} from "@/components/Vipnagorgialla";
|
||||||
import { scheduleData } from "@/data/timetable";
|
import {scheduleData} from "@/data/timetable";
|
||||||
|
import SectionDivider from "@/components/SectionDivider";
|
||||||
|
|
||||||
const tabs = Object.keys(scheduleData);
|
const tabs = Object.keys(scheduleData);
|
||||||
|
|
||||||
export default function Timetable() {
|
export default function Timetable() {
|
||||||
const [activeTab, setActiveTab] = useState(tabs[0]);
|
const [activeTab, setActiveTab] = useState(tabs[0]);
|
||||||
const schedule = scheduleData[activeTab];
|
const schedule = scheduleData[activeTab];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16">
|
<div>
|
||||||
<h1
|
<div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16">
|
||||||
className={`text-5xl sm:text-6xl ${vipnagorgialla.className} font-bold italic uppercase text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 md:mt-16 mb-8`}
|
<h1
|
||||||
>
|
className={`text-5xl sm:text-6xl ${vipnagorgialla.className} font-bold italic uppercase text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 md:mt-16 mb-8`}
|
||||||
Ajakava
|
>
|
||||||
</h1>
|
Ajakava
|
||||||
|
</h1>
|
||||||
|
|
||||||
{/* Tab menu */}
|
{/* Tab menu */}
|
||||||
<div className="flex space-x-4 mb-8">
|
<div className="flex space-x-4 mb-8">
|
||||||
{tabs.map((tab) => (
|
{tabs.map((tab) => (
|
||||||
<button
|
<button
|
||||||
key={tab}
|
key={tab}
|
||||||
onClick={() => setActiveTab(tab)}
|
onClick={() => setActiveTab(tab)}
|
||||||
className={`${vipnagorgialla.className} uppercase italic px-4 py-2 text-lg font-semibold ${
|
className={`${vipnagorgialla.className} uppercase italic px-4 py-2 text-lg font-semibold ${
|
||||||
activeTab === tab
|
activeTab === tab
|
||||||
? "bg-[#00A3E0] text-white"
|
? "bg-[#00A3E0] text-white"
|
||||||
: "bg-[#007CAB] dark:bg-[#007CAB] text-[#EEE5E5] hover:bg-[#00A3E0] dark:hover:bg-[#007CAB]"
|
: "bg-[#007CAB] dark:bg-[#007CAB] text-[#EEE5E5] hover:bg-[#00A3E0] dark:hover:bg-[#007CAB]"
|
||||||
} transition-colors`}
|
} transition-colors`}
|
||||||
>
|
>
|
||||||
{tab}
|
{tab}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Schedule entries */}
|
|
||||||
<div className="space-y-6">
|
|
||||||
{schedule.map((item, idx) => (
|
|
||||||
<div
|
|
||||||
key={idx}
|
|
||||||
className="border-l-3 border-[#007CAB] pl-4 flex flex-row gap-12"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={` ${vipnagorgialla.className} text-[#00A3E0] text-5xl font-bold italic`}
|
|
||||||
>
|
|
||||||
{item.time}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
className={`${vipnagorgialla.className} text-4xl italic font-bold text-[#2A2C3F] dark:text-[#EEE5E5]`}
|
|
||||||
>
|
|
||||||
{item.title}
|
|
||||||
</div>
|
|
||||||
{item.description && (
|
|
||||||
<div className="text-2xl text-[#938BA1] dark:text-[#938BA1]">
|
|
||||||
{item.description}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
{item.location && (
|
{/* Schedule entries */}
|
||||||
<div className="text-2xl text-[#938BA1] dark:text-[#938BA1]">
|
<div className="space-y-6">
|
||||||
{item.location}
|
{schedule.map((item, idx) => (
|
||||||
|
<div
|
||||||
|
key={idx}
|
||||||
|
className="border-l-3 border-[#007CAB] pl-4 flex flex-row gap-12"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={` ${vipnagorgialla.className} text-[#00A3E0] text-5xl font-bold italic`}
|
||||||
|
>
|
||||||
|
{item.time}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
className={`${vipnagorgialla.className} text-4xl italic font-bold text-[#2A2C3F] dark:text-[#EEE5E5]`}
|
||||||
|
>
|
||||||
|
{item.title}
|
||||||
|
</div>
|
||||||
|
{item.description && (
|
||||||
|
<div className="text-2xl text-[#938BA1] dark:text-[#938BA1]">
|
||||||
|
{item.description}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{item.location && (
|
||||||
|
<div className="text-2xl text-[#938BA1] dark:text-[#938BA1]">
|
||||||
|
{item.location}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
))}
|
<SectionDivider/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,48 +3,53 @@ import fs from "node:fs";
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import ReactMarkdown from "react-markdown";
|
import ReactMarkdown from "react-markdown";
|
||||||
import remarkGfm from "remark-gfm";
|
import remarkGfm from "remark-gfm";
|
||||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
import {vipnagorgialla} from "@/components/Vipnagorgialla";
|
||||||
|
import SectionDivider from "@/components/SectionDivider";
|
||||||
|
|
||||||
export const runtime = "nodejs"; // ensure fs is available (not Edge)
|
export const runtime = "nodejs"; // ensure fs is available (not Edge)
|
||||||
export const dynamic = "force-static"; // read at build time
|
export const dynamic = "force-static"; // read at build time
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const filePath = path.join(process.cwd(), "src/data", "kodukord.md");
|
const filePath = path.join(process.cwd(), "src/data", "kodukord.md");
|
||||||
const content = fs.readFileSync(filePath, "utf8");
|
const content = fs.readFileSync(filePath, "utf8");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16">
|
<div>
|
||||||
{/* Page title (separate from markdown headings) */}
|
<div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16">
|
||||||
<h1
|
{/* Page title (separate from markdown headings) */}
|
||||||
className={`text-4xl md:text-5xl lg:text-6xl ${vipnagorgialla.className} font-bold italic text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 md:mt-16 mb-4 uppercase`}
|
<h1
|
||||||
>
|
className={`text-4xl md:text-5xl lg:text-6xl ${vipnagorgialla.className} font-bold italic text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 md:mt-16 mb-4 uppercase`}
|
||||||
Kodukord
|
>
|
||||||
</h1>
|
Kodukord
|
||||||
|
</h1>
|
||||||
|
|
||||||
<div className="prose prose-lg dark:prose-invert max-w-none">
|
<div className="prose prose-lg dark:prose-invert max-w-none">
|
||||||
<ReactMarkdown
|
<ReactMarkdown
|
||||||
remarkPlugins={[remarkGfm]}
|
remarkPlugins={[remarkGfm]}
|
||||||
components={{
|
components={{
|
||||||
h1: ({node, ...props}) => (
|
h1: ({node, ...props}) => (
|
||||||
<h1 className="text-3xl md:text-4xl font-bold my-4" {...props} />
|
<h1 className="text-3xl md:text-4xl font-bold my-4" {...props} />
|
||||||
),
|
),
|
||||||
h2: ({node, ...props}) => (
|
h2: ({node, ...props}) => (
|
||||||
<h2 className="text-2xl md:text-3xl font-semibold my-3" {...props} />
|
<h2 className="text-2xl md:text-3xl font-semibold my-3" {...props} />
|
||||||
),
|
),
|
||||||
ol: ({node, ...props}) => (
|
ol: ({node, ...props}) => (
|
||||||
<ol className="list-decimal ml-6 md:text-xl" {...props} />
|
<ol className="list-decimal ml-6 md:text-xl" {...props} />
|
||||||
),
|
),
|
||||||
ul: ({node, ...props}) => (
|
ul: ({node, ...props}) => (
|
||||||
<ul className="list-disc ml-6 md:text-xl" {...props} />
|
<ul className="list-disc ml-6 md:text-xl" {...props} />
|
||||||
),
|
),
|
||||||
p: ({node, ...props}) => (
|
p: ({node, ...props}) => (
|
||||||
<p className="md:text-xl" {...props} />
|
<p className="md:text-xl" {...props} />
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</ReactMarkdown>
|
</ReactMarkdown>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
|
<SectionDivider/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,480 +1,485 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
import {vipnagorgialla} from "@/components/Vipnagorgialla";
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import {useEffect, useRef, useState} from "react";
|
||||||
import { EyeClosed, Eye } from "lucide-react";
|
import {EyeClosed, Eye} from "lucide-react";
|
||||||
|
import SectionDivider from "@/components/SectionDivider";
|
||||||
|
|
||||||
// Define interface for the ref with toggle function
|
// Define interface for the ref with toggle function
|
||||||
interface MountRefCurrent extends HTMLDivElement {
|
interface MountRefCurrent extends HTMLDivElement {
|
||||||
toggleDividers?: (show: boolean) => void;
|
toggleDividers?: (show: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Expo() {
|
export default function Expo() {
|
||||||
const mountRef = useRef<MountRefCurrent | null>(null);
|
const mountRef = useRef<MountRefCurrent | null>(null);
|
||||||
const [hoveredRoom, setHoveredRoom] = useState<string | null>(null);
|
const [hoveredRoom, setHoveredRoom] = useState<string | null>(null);
|
||||||
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
|
const [mousePosition, setMousePosition] = useState({x: 0, y: 0});
|
||||||
const [showDividers, setShowDividers] = useState<boolean>(true);
|
const [showDividers, setShowDividers] = useState<boolean>(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!mountRef.current) return;
|
if (!mountRef.current) return;
|
||||||
|
|
||||||
// Copy ref to variable to avoid stale closure in cleanup
|
// Copy ref to variable to avoid stale closure in cleanup
|
||||||
const mountElement = mountRef.current;
|
const mountElement = mountRef.current;
|
||||||
let dividersRef: THREE.Mesh[] = [];
|
let dividersRef: THREE.Mesh[] = [];
|
||||||
|
|
||||||
// Scene setup
|
// Scene setup
|
||||||
const scene = new THREE.Scene();
|
const scene = new THREE.Scene();
|
||||||
scene.background = new THREE.Color(0x0e0f19);
|
scene.background = new THREE.Color(0x0e0f19);
|
||||||
|
|
||||||
// Get responsive dimensions
|
// Get responsive dimensions
|
||||||
const getResponsiveDimensions = () => {
|
const getResponsiveDimensions = () => {
|
||||||
const container = mountRef.current;
|
const container = mountRef.current;
|
||||||
if (!container) return { width: 800, height: 600 };
|
if (!container) return {width: 800, height: 600};
|
||||||
|
|
||||||
const containerWidth = container.offsetWidth;
|
const containerWidth = container.offsetWidth;
|
||||||
const maxWidth = Math.min(containerWidth, 800);
|
const maxWidth = Math.min(containerWidth, 800);
|
||||||
const width = Math.max(maxWidth, 300); // Minimum width
|
const width = Math.max(maxWidth, 300); // Minimum width
|
||||||
const height = (width * 600) / 800; // Maintain aspect ratio
|
const height = (width * 600) / 800; // Maintain aspect ratio
|
||||||
|
|
||||||
return { width, height };
|
return {width, height};
|
||||||
};
|
};
|
||||||
|
|
||||||
const { width, height } = getResponsiveDimensions();
|
const {width, height} = getResponsiveDimensions();
|
||||||
|
|
||||||
// Isometric camera setup with responsive sizing
|
// Isometric camera setup with responsive sizing
|
||||||
const aspect = width / height;
|
const aspect = width / height;
|
||||||
const baseFrustumSize = 14;
|
const baseFrustumSize = 14;
|
||||||
const frustumSize = width < 600 ? baseFrustumSize * 0.8 : baseFrustumSize; // Smaller frustum for mobile
|
const frustumSize = width < 600 ? baseFrustumSize * 0.8 : baseFrustumSize; // Smaller frustum for mobile
|
||||||
const camera = new THREE.OrthographicCamera(
|
const camera = new THREE.OrthographicCamera(
|
||||||
(frustumSize * aspect) / -2,
|
(frustumSize * aspect) / -2,
|
||||||
(frustumSize * aspect) / 2,
|
(frustumSize * aspect) / 2,
|
||||||
frustumSize / 2,
|
frustumSize / 2,
|
||||||
frustumSize / -2,
|
frustumSize / -2,
|
||||||
1,
|
1,
|
||||||
1000,
|
1000,
|
||||||
);
|
|
||||||
|
|
||||||
// Position camera for isometric view
|
|
||||||
camera.position.set(10, 10, 14);
|
|
||||||
camera.lookAt(-1.4, 0, 0);
|
|
||||||
|
|
||||||
// Renderer
|
|
||||||
const renderer = new THREE.WebGLRenderer({ antialias: true });
|
|
||||||
renderer.setSize(width, height);
|
|
||||||
renderer.shadowMap.enabled = true;
|
|
||||||
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
|
||||||
mountElement.appendChild(renderer.domElement);
|
|
||||||
|
|
||||||
// Raycaster for mouse interactions
|
|
||||||
const raycaster = new THREE.Raycaster();
|
|
||||||
const mouse = new THREE.Vector2();
|
|
||||||
|
|
||||||
// Lighting
|
|
||||||
const ambientLight = new THREE.AmbientLight(0x404040, 1.2);
|
|
||||||
scene.add(ambientLight);
|
|
||||||
|
|
||||||
const directionalLight = new THREE.DirectionalLight(0xffffff, 1.5);
|
|
||||||
directionalLight.position.set(10, 10, 5);
|
|
||||||
directionalLight.castShadow = false;
|
|
||||||
directionalLight.shadow.mapSize.width = 2048;
|
|
||||||
directionalLight.shadow.mapSize.height = 2048;
|
|
||||||
scene.add(directionalLight);
|
|
||||||
|
|
||||||
// Room colors and names
|
|
||||||
const roomColors = [
|
|
||||||
0xff6b35, // Orange - Mänguklubi
|
|
||||||
0x4ecdc4, // Turquoise - Baariala
|
|
||||||
0xffe66d, // Yellow - EVAL
|
|
||||||
0xe74c3c, // Red - Redbull
|
|
||||||
0x9b59b6, // Purple - Võitlusmängu ala
|
|
||||||
0x3498db, // Blue - Sony
|
|
||||||
0x2ecc71, // Green - Chillimisala
|
|
||||||
];
|
|
||||||
|
|
||||||
const roomNames = [
|
|
||||||
"Mänguklubi",
|
|
||||||
"Baariala",
|
|
||||||
"EVAL",
|
|
||||||
"Redbull",
|
|
||||||
"Võitlusmängu ala",
|
|
||||||
"Sony",
|
|
||||||
"Chillimisala",
|
|
||||||
];
|
|
||||||
|
|
||||||
// Create individual rooms as rectangles with custom positions
|
|
||||||
const rooms: THREE.Mesh[] = [];
|
|
||||||
const roomData: Array<{
|
|
||||||
mesh: THREE.Mesh;
|
|
||||||
name: string;
|
|
||||||
originalColor: number;
|
|
||||||
originalScale: THREE.Vector3;
|
|
||||||
}> = [];
|
|
||||||
const dividers: THREE.Mesh[] = [];
|
|
||||||
|
|
||||||
// Define rooms with custom positions, sizes and colors
|
|
||||||
const roomDefinitions = [
|
|
||||||
{
|
|
||||||
width: 7,
|
|
||||||
height: 0.7,
|
|
||||||
depth: 3,
|
|
||||||
x: 2.5,
|
|
||||||
z: 4,
|
|
||||||
color: roomColors[0],
|
|
||||||
name: roomNames[0],
|
|
||||||
}, // Mänguklubi
|
|
||||||
// {
|
|
||||||
// width: 2.5,
|
|
||||||
// height: 0.7,
|
|
||||||
// depth: 0.7,
|
|
||||||
// x: 1,
|
|
||||||
// z: 0,
|
|
||||||
// color: roomColors[1],
|
|
||||||
// name: roomNames[1],
|
|
||||||
// }, // Baariala
|
|
||||||
{
|
|
||||||
width: 1.8,
|
|
||||||
height: 0.7,
|
|
||||||
depth: 1.5,
|
|
||||||
x: 2.5,
|
|
||||||
z: -3.5,
|
|
||||||
color: roomColors[2],
|
|
||||||
name: roomNames[2],
|
|
||||||
}, // EVAL
|
|
||||||
{
|
|
||||||
width: 2.2,
|
|
||||||
height: 0.7,
|
|
||||||
depth: 4.5,
|
|
||||||
x: 5,
|
|
||||||
z: -2,
|
|
||||||
color: roomColors[3],
|
|
||||||
name: roomNames[3],
|
|
||||||
}, // Redbull
|
|
||||||
{
|
|
||||||
width: 3,
|
|
||||||
height: 0.7,
|
|
||||||
depth: 1.3,
|
|
||||||
x: 0,
|
|
||||||
z: -3.5,
|
|
||||||
color: roomColors[4],
|
|
||||||
name: roomNames[4],
|
|
||||||
}, // Võitlusmängu ala
|
|
||||||
{
|
|
||||||
width: 1.8,
|
|
||||||
height: 0.7,
|
|
||||||
depth: 1.5,
|
|
||||||
x: -2.55,
|
|
||||||
z: -3.5,
|
|
||||||
color: roomColors[5],
|
|
||||||
name: roomNames[5],
|
|
||||||
}, // Sony
|
|
||||||
{
|
|
||||||
width: 4,
|
|
||||||
height: 0.7,
|
|
||||||
depth: 4,
|
|
||||||
x: -5.5,
|
|
||||||
z: -2.3,
|
|
||||||
color: roomColors[6],
|
|
||||||
name: roomNames[6],
|
|
||||||
}, // Chillimisala
|
|
||||||
];
|
|
||||||
|
|
||||||
roomDefinitions.forEach((roomDef) => {
|
|
||||||
const geometry = new THREE.BoxGeometry(
|
|
||||||
roomDef.width,
|
|
||||||
roomDef.height,
|
|
||||||
roomDef.depth,
|
|
||||||
);
|
|
||||||
const material = new THREE.MeshLambertMaterial({
|
|
||||||
color: roomDef.color,
|
|
||||||
});
|
|
||||||
|
|
||||||
const room = new THREE.Mesh(geometry, material);
|
|
||||||
room.position.set(roomDef.x, roomDef.height / 2, roomDef.z);
|
|
||||||
room.castShadow = true;
|
|
||||||
room.receiveShadow = true;
|
|
||||||
room.userData = { name: roomDef.name, originalColor: roomDef.color };
|
|
||||||
|
|
||||||
scene.add(room);
|
|
||||||
rooms.push(room);
|
|
||||||
roomData.push({
|
|
||||||
mesh: room,
|
|
||||||
name: roomDef.name,
|
|
||||||
originalColor: roomDef.color,
|
|
||||||
originalScale: room.scale.clone(),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create toggleable room dividers
|
|
||||||
const createTogglableDivider = (
|
|
||||||
width: number,
|
|
||||||
height: number,
|
|
||||||
depth: number,
|
|
||||||
x: number,
|
|
||||||
z: number,
|
|
||||||
) => {
|
|
||||||
const wallGeometry = new THREE.BoxGeometry(width, height, depth);
|
|
||||||
const wallMaterial = new THREE.MeshLambertMaterial({
|
|
||||||
color: 0x555555,
|
|
||||||
transparent: true,
|
|
||||||
opacity: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
const wall = new THREE.Mesh(wallGeometry, wallMaterial);
|
|
||||||
wall.position.set(x, height / 2, z);
|
|
||||||
wall.visible = false;
|
|
||||||
scene.add(wall);
|
|
||||||
dividers.push(wall);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add strategic dividers between major areas
|
|
||||||
createTogglableDivider(10, 2, 2, -2.5, 1.5); // Wall between main entrance
|
|
||||||
createTogglableDivider(2, 2, 2, 5.5, 1.5); // Wall right next to Mänguklubi & Redbull
|
|
||||||
|
|
||||||
// Store dividers reference for later access
|
|
||||||
dividersRef = [...dividers];
|
|
||||||
|
|
||||||
// Ground plane
|
|
||||||
const groundGeometry = new THREE.PlaneGeometry(14, 10.5);
|
|
||||||
const groundMaterial = new THREE.MeshLambertMaterial({ color: 0xcccccc });
|
|
||||||
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
|
|
||||||
ground.rotation.x = -Math.PI / 2;
|
|
||||||
ground.position.x = -1.1;
|
|
||||||
ground.position.y = -0.5;
|
|
||||||
ground.receiveShadow = true;
|
|
||||||
scene.add(ground);
|
|
||||||
|
|
||||||
// Second ground plane
|
|
||||||
const groundGeometry2 = new THREE.PlaneGeometry(2, 7);
|
|
||||||
const groundMaterial2 = new THREE.MeshLambertMaterial({
|
|
||||||
color: 0xcccccc,
|
|
||||||
});
|
|
||||||
const ground2 = new THREE.Mesh(groundGeometry2, groundMaterial2);
|
|
||||||
ground2.rotation.x = -Math.PI / 2;
|
|
||||||
ground2.position.x = -12.2;
|
|
||||||
ground2.position.y = -5;
|
|
||||||
ground2.receiveShadow = true;
|
|
||||||
scene.add(ground2);
|
|
||||||
|
|
||||||
// Resize handler
|
|
||||||
const handleResize = () => {
|
|
||||||
const { width: newWidth, height: newHeight } = getResponsiveDimensions();
|
|
||||||
|
|
||||||
// Update camera
|
|
||||||
const newAspect = newWidth / newHeight;
|
|
||||||
const newFrustumSize =
|
|
||||||
newWidth < 600 ? baseFrustumSize * 0.8 : baseFrustumSize;
|
|
||||||
|
|
||||||
camera.left = (newFrustumSize * newAspect) / -2;
|
|
||||||
camera.right = (newFrustumSize * newAspect) / 2;
|
|
||||||
camera.top = newFrustumSize / 2;
|
|
||||||
camera.bottom = newFrustumSize / -2;
|
|
||||||
camera.updateProjectionMatrix();
|
|
||||||
|
|
||||||
// Update renderer
|
|
||||||
renderer.setSize(newWidth, newHeight);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add resize event listener
|
|
||||||
window.addEventListener("resize", handleResize);
|
|
||||||
|
|
||||||
// Mouse event handlers
|
|
||||||
const onMouseMove = (event: MouseEvent) => {
|
|
||||||
const rect = renderer.domElement.getBoundingClientRect();
|
|
||||||
mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
|
|
||||||
mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
|
|
||||||
|
|
||||||
// Update mouse position for tooltip
|
|
||||||
setMousePosition({ x: event.clientX, y: event.clientY });
|
|
||||||
|
|
||||||
// Update raycaster
|
|
||||||
raycaster.setFromCamera(mouse, camera);
|
|
||||||
const intersects = raycaster.intersectObjects(rooms);
|
|
||||||
|
|
||||||
// Reset all rooms to original state
|
|
||||||
roomData.forEach(({ mesh, originalColor, originalScale }) => {
|
|
||||||
(mesh.material as THREE.MeshLambertMaterial).color.setHex(
|
|
||||||
originalColor,
|
|
||||||
);
|
);
|
||||||
mesh.scale.copy(originalScale);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (intersects.length > 0) {
|
// Position camera for isometric view
|
||||||
const hoveredMesh = intersects[0].object as THREE.Mesh;
|
camera.position.set(10, 10, 14);
|
||||||
const roomInfo = roomData.find((r) => r.mesh === hoveredMesh);
|
camera.lookAt(-1.4, 0, 0);
|
||||||
|
|
||||||
if (roomInfo) {
|
// Renderer
|
||||||
// Apply hover effects
|
const renderer = new THREE.WebGLRenderer({antialias: true});
|
||||||
(hoveredMesh.material as THREE.MeshLambertMaterial).color.setHex(
|
renderer.setSize(width, height);
|
||||||
0xffffff,
|
renderer.shadowMap.enabled = true;
|
||||||
);
|
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
||||||
hoveredMesh.scale.multiplyScalar(1.02);
|
mountElement.appendChild(renderer.domElement);
|
||||||
setHoveredRoom(roomInfo.name);
|
|
||||||
|
// 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 {
|
}, [showDividers]);
|
||||||
setHoveredRoom(null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add mouse event listener
|
return (
|
||||||
renderer.domElement.addEventListener("mousemove", onMouseMove);
|
<div>
|
||||||
|
<div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16">
|
||||||
// Animation loop
|
<h1
|
||||||
const animate = () => {
|
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`}
|
||||||
requestAnimationFrame(animate);
|
>
|
||||||
|
Messiala
|
||||||
// Gentle floating animation for rooms
|
</h1>
|
||||||
rooms.forEach((room, index) => {
|
<div className="mb-6">
|
||||||
const originalY = 0.25; // height / 2 for the room height of 0.5
|
<h2 className="text-2xl text-[#2A2C3F] dark:text-[#EEE5E5] mb-3">
|
||||||
const baseY = originalY + Math.sin(Date.now() * 0.001 + index) * 0.05;
|
Tudengimaja
|
||||||
|
</h2>
|
||||||
// Maintain current scale while updating Y position
|
<div className="flex flex-wrap gap-4 pb-4">
|
||||||
room.position.y = baseY;
|
<div className="flex items-center gap-2">
|
||||||
});
|
<div
|
||||||
|
className="w-4 h-4 border border-gray-300"
|
||||||
renderer.render(scene, camera);
|
style={{backgroundColor: "#ff6b35"}}
|
||||||
};
|
></div>
|
||||||
|
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
|
||||||
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>
|
||||||
<div className="flex flex-col lg:flex-row gap-8 items-start">
|
<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 className="flex-shrink-0 border-3 border-[#1F5673] w-full max-w-[800px] relative">
|
||||||
<div ref={mountRef} className="w-full" />
|
<div ref={mountRef} className="w-full"/>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowDividers(!showDividers)}
|
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`}
|
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 ? (
|
{showDividers ? (
|
||||||
<EyeClosed className="w-6 h-6 mr-2" />
|
<EyeClosed className="w-6 h-6 mr-2"/>
|
||||||
) : (
|
) : (
|
||||||
<Eye className="w-6 h-6 mr-2" />
|
<Eye className="w-6 h-6 mr-2"/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{showDividers ? "Peida" : "Näita"}
|
{showDividers ? "Peida" : "Näita"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Tooltip */}
|
{/* Tooltip */}
|
||||||
{hoveredRoom && (
|
{hoveredRoom && (
|
||||||
<div
|
<div
|
||||||
className="fixed bg-black bg-opacity-80 text-white px-3 py-2 rounded-lg text-sm pointer-events-none z-50"
|
className="fixed bg-black bg-opacity-80 text-white px-3 py-2 rounded-lg text-sm pointer-events-none z-50"
|
||||||
style={{
|
style={{
|
||||||
left: mousePosition.x + 10,
|
left: mousePosition.x + 10,
|
||||||
top: mousePosition.y - 10,
|
top: mousePosition.y - 10,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{hoveredRoom}
|
{hoveredRoom}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
|
<SectionDivider />
|
||||||
|
</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
|
<div
|
||||||
className={`mx-auto px-8 font-worksans
|
className={`mx-auto px-8 font-worksans
|
||||||
[&_ol]:ml-6
|
[&_ol]:ml-6
|
||||||
[&_ol_ol]:ml-10
|
[&_ol_ol]:ml-10
|
||||||
[&_ol_ol_ol]:ml-14
|
[&_ol_ol_ol]:ml-14
|
||||||
[&_h2]:font-bold
|
[&_h2]:font-bold
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<ReactMarkdown>{file}</ReactMarkdown>
|
<ReactMarkdown>{file}</ReactMarkdown>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</div>
|
||||||
);
|
|
||||||
|
<SectionDivider/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import {vipnagorgialla} from "@/components/Vipnagorgialla";
|
import {vipnagorgialla} from "@/components/Vipnagorgialla";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import SectionDivider from "@/components/SectionDivider";
|
||||||
|
|
||||||
export default function RulesMenu() {
|
export default function RulesMenu() {
|
||||||
const headingStyle = `text-5xl sm:text-6xl ${vipnagorgialla.className} font-bold italic text-[#2A2C3F] dark:text-[#EEE5E5]`;
|
const headingStyle = `text-5xl sm:text-6xl ${vipnagorgialla.className} font-bold italic text-[#2A2C3F] dark:text-[#EEE5E5]`;
|
||||||
@@ -8,7 +9,7 @@ export default function RulesMenu() {
|
|||||||
|
|
||||||
const boxTextStyle = `text-3xl ${vipnagorgialla.className} font-bold uppercase text-[#EEE5E5] pb-2`;
|
const boxTextStyle = `text-3xl ${vipnagorgialla.className} font-bold uppercase text-[#EEE5E5] pb-2`;
|
||||||
|
|
||||||
const SectionDivider = () => <div className="border-b-[3px] border-[#1F5673] w-full"/>;
|
// const SectionDivider = () => <div className="border-b-[3px] border-[#1F5673] w-full"/>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
import SectionDivider from "@/components/SectionDivider";
|
||||||
|
|
||||||
export default function Tourney() {
|
export default function Tourney() {
|
||||||
const headingStyle = `text-3xl md:text-5xl lg:text-5xl ${vipnagorgialla.className} font-bold uppercase text-[#2A2C3F] dark:text-[#EEE5E5] -skew-x-2 md:-skew-x-5`;
|
const headingStyle = `text-3xl md:text-5xl lg:text-5xl ${vipnagorgialla.className} font-bold uppercase text-[#2A2C3F] dark:text-[#EEE5E5] -skew-x-2 md:-skew-x-5`;
|
||||||
|
|
||||||
const SectionDivider = () => <hr className="border-t-[3px] border-[#1F5673]" />;
|
// const SectionDivider = () => <hr className="border-t-[3px] border-[#1F5673]" />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col min-h-[90vh] mt-16">
|
<div className="flex flex-col min-h-[90vh] mt-16">
|
||||||
@@ -75,7 +76,6 @@ export default function Tourney() {
|
|||||||
|
|
||||||
<SectionDivider />
|
<SectionDivider />
|
||||||
|
|
||||||
|
|
||||||
{/* LoL turniir */}
|
{/* LoL turniir */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-16 items-center mx-8 md:mx-16 lg:mx-32 xl:mx-48">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-16 items-center mx-8 md:mx-16 lg:mx-32 xl:mx-48">
|
||||||
<div className="hidden md:block">
|
<div className="hidden md:block">
|
||||||
|
|||||||
7
src/components/SectionDivider.tsx
Normal file
7
src/components/SectionDivider.tsx
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default function SectionDivider() {
|
||||||
|
return (
|
||||||
|
<hr className="border-t-[3px] border-[#1F5673]" />
|
||||||
|
);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user