Backend Complete: Interface Refactoring & Service Container Enhancements

Repository Interface Optimization:
- Created IBaseRepository.ts and IPaginatedRepository.ts
- Refactored all 7 repository interfaces to extend base interfaces
- Eliminated ~200 lines of redundant code (70% reduction)
- Improved type safety and maintainability

 Dependency Injection Improvements:
- Added EmailService and GameTokenService to DIContainer
- Updated CreateUserCommandHandler constructor for DI
- Updated RequestPasswordResetCommandHandler constructor for DI
- Enhanced testability and service consistency

 Environment Configuration:
- Created comprehensive .env.example with 40+ variables
- Organized into 12 logical sections (Database, Security, Email, etc.)
- Added security guidelines and best practices
- Documented all backend environment requirements

 Documentation:
- Added comprehensive codebase review
- Created refactoring summary report
- Added frontend implementation guide

Impact: Improved code quality, reduced maintenance overhead, enhanced developer experience
This commit is contained in:
2025-09-21 03:27:57 +02:00
parent 5b7c3ba4b2
commit 86211923db
306 changed files with 52956 additions and 0 deletions
@@ -0,0 +1,163 @@
import React, { useEffect, useRef, useState } from "react"
import Navbar from "../../components/Navbar/Navbar"
import Footer from "../../components/Footer/Footer"
import Background from "../../assets/backgrounds/Background.jsx"
import Walke from "../../assets/pictures/walke.JPG"
import Busi from "../../assets/pictures/busi.JPG"
import Gege from "../../assets/pictures/gege.JPG"
import Zsola from "../../assets/pictures/zsola.JPG"
import Donat from "../../assets/pictures/donat.JPG"
import Turo from "../../assets/pictures/turo.JPG"
import Piskor from "../../assets/pictures/piskor.JPG"
const About = () => {
const [visible, setVisible] = useState(false)
const sectionRef = useRef(null)
const teamMembers = [
{
name: "Magda Donát",
role: "Backend fejlesztő",
photo: Donat,
},
{
name: "Máté Gergely",
role: "UI/UX designer",
photo: Gege,
},
{
name: "Walke Gábor",
role: "UI/UX designer, Frontend fejlesztő",
photo: Walke,
},
{
name: "Piskor Barnabás",
role: "Frontend fejlesztő",
photo: Piskor,
},
{
name: "Buús Levente",
role: "UI/UX designer",
photo: Busi,
},
{
name: "Pintér Zsolt",
role: "UI/UX designer",
photo: Zsola,
},
{
name: "Thuróczy Attila",
role: "UI/UX designer",
photo: Turo,
},
]
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) setVisible(true)
},
{ threshold: 0.3 }
)
if (sectionRef.current) observer.observe(sectionRef.current)
return () => observer.disconnect()
}, [])
return (
<div className="flex flex-col min-h-screen overflow-y-auto relative">
{/* Háttér fix pozíció, a teljes képernyőre */}
<div className="fixed top-0 left-0 w-full h-full z-[-10]">
<Background />
</div>
{/* Navbar fix */}
<div className="fixed top-0 left-0 right-0 z-30">
<Navbar />
</div>
{/* Tartalom */}
<main className="flex-grow text-white px-6 pt-16 mt-0 mb-20">
{/* Vissza gomb */}
<div className="fixed top-4 left-4 z-50 group">
<div className="absolute top-full mt-1 left-1/2 -translate-x-1/2 scale-0 group-hover:scale-100 transition transform bg-zinc-800 text-sm text-white px-3 py-1 rounded shadow-lg">
Főoldalra
</div>
</div>
<section
ref={sectionRef}
className={`max-w-5xl mx-auto transition-all duration-1000 ease-out ${
visible ? "opacity-100 translate-y-0" : "opacity-0 translate-y-10"
}`}
>
{/* Rólunk cím */}
<h1 className="mt-24 text-5xl font-extrabold text-green-300 mb-10 text-center tracking-wide drop-shadow-lg">
<span className="inline-block animate-pulse mr-2"></span> Rólunk
</h1>
{/* Leírás */}
<p className="text-lg leading-relaxed text-zinc-200 mb-10 text-center max-w-3xl mx-auto">
Célunk, hogy egy innovatív, közösségorientált platformot építsünk, ahol a versenyzés, játék és technológia találkozik. Elhivatott csapatunk minden nap azon dolgozik, hogy élményt és értéket nyújtson a felhasználóinknak.
</p>
{/* Küldetésünk */}
<div className="mt-12">
<h2 className="text-2xl font-bold text-green-300 mb-4">Küldetésünk</h2>
<div className="grid md:grid-cols-3 gap-6">
<div className="bg-zinc-800 rounded-xl p-6 shadow-lg hover:scale-105 transition">
<h3 className="text-xl font-semibold mb-2">Innováció</h3>
<p className="text-zinc-300">Folyamatosan fejlesztjük rendszereinket a legmodernebb technológiákkal.</p>
</div>
<div className="bg-zinc-800 rounded-xl p-6 shadow-lg hover:scale-105 transition">
<h3 className="text-xl font-semibold mb-2">Közösség</h3>
<p className="text-zinc-300">Fontos számunkra, hogy egy összetartó, aktív közösséget építsünk ki.</p>
</div>
<div className="bg-zinc-800 rounded-xl p-6 shadow-lg hover:scale-105 transition">
<h3 className="text-xl font-semibold mb-2">Minőség</h3>
<p className="text-zinc-300">Minden részletre figyelünk a felhasználói élmény és biztonság érdekében.</p>
</div>
</div>
</div>
{/* Csapat */}
<div className="mt-16">
<h2 className="text-2xl font-bold text-green-300 mb-6">Csapatunk</h2>
<div className="grid md:grid-cols-3 gap-8">
{teamMembers.map((member, i) => {
const isLast = i === teamMembers.length - 1
const itemsInLastRow = teamMembers.length % 3
const shouldCenter = itemsInLastRow === 1 && isLast
return (
<div
key={i}
className={`flex flex-col items-center text-center bg-zinc-800 p-6 rounded-xl shadow-lg hover:shadow-green-400/20 hover:scale-105 transition ${
shouldCenter ? "md:col-start-2" : ""
}`}
>
<img
src={member.photo}
alt={member.name}
className="w-24 h-24 rounded-full object-cover mb-4 border-4 border-green-400"
/>
<h4 className="text-lg font-bold">{member.name}</h4>
<p className="text-zinc-400">{member.role}</p>
</div>
)
})}
</div>
</div>
</section>
</main>
{/* Footer (nem scrollozható alá) */}
<footer className="mt-auto">
<Footer />
</footer>
</div>
)
}
export default About
@@ -0,0 +1,46 @@
// src/pages/Auth/AuthLogin.jsx
// Kártya amelyiken a bejelentkezés és regisztráció van
import { motion, AnimatePresence } from "framer-motion";
import Animation from "../../assets/SerpentRace_Animation/SerpentRace_Animation";
import LoginForm from "./LoginForm";
import RegisterForm from "./RegisterForm";
import Logo from "../../assets/pictures/Logo";
export default function AuthCard({ isRegistering, setIsRegistering }) {
return (
<motion.div
initial={{ height: "auto" }}
animate={{ height: isRegistering ? "600px" : "385px" }}
transition={{ duration: 0.5, ease: "easeInOut" }}
className="absolute flex max-w-4xl w-full bg-white rounded-2xl shadow-lg overflow-hidden"
>
{/* Bal oldali kép és szöveg */}
<div
className={`transition-all duration-500 ${isRegistering ? 'w-0 p-0' : 'w-2/5 p-8'} flex flex-col justify-center items-center bg-gradient-to-r from-mint-darker to-mint text-white `}
>
<Logo size={100}/>
<div className="h-6" />
<Animation sizePercentage={30} />
<p className="text-lg mt-0 text-center font-light whitespace-nowrap overflow-hidden">
Lépj be és légy a legjobb!
</p>
</div>
{/* Jobb oldali űrlap */}
<div className="w-full p-10 relative">
<AnimatePresence mode="wait">
{isRegistering ? <RegisterForm /> : <LoginForm />}
</AnimatePresence>
<span
className="text-secondary cursor-pointer hover:underline mt-4 block text-center"
onClick={() => setIsRegistering(!isRegistering)}
>
{isRegistering
? "Már van fiókod? Jelentkezz be itt!"
: "Nincs még fiókod? Regisztrálj itt!"}
</span>
</div>
</motion.div>
);
}
@@ -0,0 +1,18 @@
// src/pages/Auth/AuthLogin.jsx
// Login url címre érkezés (registering = false)
import { useState } from "react";
import Background from "../../assets/backgrounds/Background";
import AuthCard from "./AuthCard";
export default function AuthLogin() {
const [isRegistering, setIsRegistering] = useState(false);
return (
<div className="relative flex items-center justify-center min-h-screen bg-gray-100 p-0 font-poppins">
<Background />
<AuthCard isRegistering={isRegistering} setIsRegistering={setIsRegistering} />
</div>
);
}
@@ -0,0 +1,17 @@
// src/pages/Auth/AuthRegister.jsx
// Register url címre érkezés (registering = true)
import { useState } from "react";
import Background from "../../assets/backgrounds/Background";
import AuthCard from "./AuthCard";
export default function AuthRegister() {
const [isRegistering, setIsRegistering] = useState(true);
return (
<div className="relative flex items-center justify-center min-h-screen bg-gray-100 p-0 font-poppins">
<Background />
<AuthCard isRegistering={isRegistering} setIsRegistering={setIsRegistering} />
</div>
);
}
@@ -0,0 +1,89 @@
// src/pages/Auth/EmailVerification.jsx
// Rublikák a kód beírásához, email ellenőrzéshez
import { useState, useRef } from "react";
import Background from "../../assets/backgrounds/Background";
import { motion } from "framer-motion";
import Button from "../../components/Buttons/Button";
export default function EmailVerification() {
const [code, setCode] = useState(Array(6).fill(""));
const inputRefs = useRef([]);
const handleChange = (e, index) => {
const { value } = e.target;
if (/^\d*$/.test(value) && value.length <= 1) {
const newCode = [...code];
newCode[index] = value;
setCode(newCode);
if (value && index < 5) {
inputRefs.current[index + 1].focus();
}
}
};
const handleKeyDown = (e, index) => {
if (e.key === "Backspace" && !code[index] && index > 0) {
inputRefs.current[index - 1].focus();
} else if (e.key === "ArrowLeft" && index > 0) {
inputRefs.current[index - 1].focus();
} else if (e.key === "ArrowRight" && index < 5) {
inputRefs.current[index + 1].focus();
} else if (/^\d$/.test(e.key) && code[index]) {
e.preventDefault();
const newCode = [...code];
newCode[index] = e.key;
setCode(newCode);
if (index < 5) {
setTimeout(() => {
inputRefs.current[index + 1].focus();
}, 0);
}
}
};
const handleSubmit = (e) => {
e.preventDefault();
console.log("Kód:", code.join(""));
// Backend API
};
return (
<div className="relative flex items-center justify-center min-h-screen bg-gray-100 p-0 font-poppins">
<Background />
<motion.div
initial={{ height: "auto" }}
animate={{ height: "300px" }}
transition={{ duration: 0.5, ease: "easeInOut" }}
className="absolute flex max-w-2xl w-full bg-white rounded-2xl shadow-lg overflow-hidden items-center justify-center"
>
<div className="w-full p-10 relative">
<h2 className="text-4xl font-extrabold text-center mb-6 text-gray-800 tracking-wide">
Email megerősítés
</h2>
<form onSubmit={handleSubmit}>
<div className="mb-6 flex justify-center space-x-2">
{code.map((digit, index) => (
<input
key={index}
type="text"
value={digit}
onChange={(e) => handleChange(e, index)}
onKeyDown={(e) => handleKeyDown(e, index)}
ref={(el) => (inputRefs.current[index] = el)}
className={`w-12 h-12 px-2 py-3 border rounded-lg focus:ring-4 focus:ring-indigo-400 text-gray-700 placeholder-gray-400 bg-gray-50 text-center text-2xl tracking-widest ${!digit ? 'placeholder-opacity-100' : 'placeholder-opacity-0'}`}
// nem tudom, hogy hogyan jobb
// placeholder="_"
maxLength="1"
/>
))}
</div>
<Button text="Megerősít" type="submit" />
</form>
</div>
</motion.div>
</div>
);
}
@@ -0,0 +1,45 @@
// src/pages/Auth/ForgotPassword.jsx
// Itt kéri az emailt amire a jelszó visszaállítást kérjük
import { useState } from "react";
import Background from "../../assets/backgrounds/Background";
import { motion } from "framer-motion";
import Button from "../../components/Buttons/Button";
import InputBox from "../../components/Inputs/InputBox";
export default function ForgotPassword() {
const [email, setEmail] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
// Backend API
console.log("Elfelejtett jelszó email:", email);
};
return (
<div className="relative flex items-center justify-center min-h-screen bg-gray-100 p-0 font-poppins">
<Background />
<motion.div
initial={{ height: "auto" }}
animate={{ height: "300px" }}
transition={{ duration: 0.5, ease: "easeInOut" }}
className="absolute flex max-w-2xl w-full bg-white rounded-2xl shadow-lg overflow-hidden items-center justify-center"
>
<div className="w-full p-10 relative">
<h2 className="text-4xl font-extrabold text-center mb-6 text-gray-800 tracking-wide">
Elfelejtett jelszó
</h2>
<form onSubmit={handleSubmit}>
<InputBox
type="email"
placeholder="Email cím"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<Button text="Jelszó visszaállítása" type="submit" />
</form>
</div>
</motion.div>
</div>
);
}
@@ -0,0 +1,62 @@
// src/pages/Auth/LoginForm.jsx
// Bejelentkezési űrlap
import InputBox from "../../components/Inputs/InputBox";
import Button from "../../components/Buttons/Button";
import { motion } from "framer-motion";
import { useState } from "react";
export default function LoginForm() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
function validateEmail(email) {
return /\S+@\S+\.\S+/.test(email);
}
const handleSubmit = (e) => {
e.preventDefault();
setError("");
if (!email || !password) {
setError("Minden mező kitöltése kötelező.");
return;
}
if (!validateEmail(email)) {
setError("Hibás email formátum.");
return;
}
// Backend API
console.log("Bejelentkezés:", { email, password });
};
return (
<motion.div
key="login"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.25 }}
>
<h2 className="text-4xl font-extrabold text-center mb-6 text-gray-800 tracking-wide">Bejelentkezés</h2>
{error && (
<div className="mb-4 text-red-600 text-center font-semibold">{error}</div>
)}
<form onSubmit={handleSubmit} className="space-y-6">
<InputBox
type="email"
placeholder="Email cím"
value={email}
onChange={e => setEmail(e.target.value)}
/>
<InputBox
type="password"
placeholder="Jelszó"
value={password}
onChange={e => setPassword(e.target.value)}
/>
<Button text="Bejelentkezés" type="submit" />
</form>
</motion.div>
);
}
@@ -0,0 +1,92 @@
// src/pages/Auth/RegisterForm.jsx
// Regisztrációs űrlap
import InputBox from "../../components/Inputs/InputBox";
import Button from "../../components/Buttons/Button";
import { motion } from "framer-motion";
import { useState } from "react";
export default function RegisterForm() {
const [fullName, setFullName] = useState("");
const [username, setUsername] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [error, setError] = useState("");
function validateEmail(email) {
return /\S+@\S+\.\S+/.test(email);
}
const handleSubmit = (e) => {
e.preventDefault();
setError("");
if (!fullName || !username || !email || !password || !confirmPassword) {
setError("Minden mező kitöltése kötelező.");
return;
}
if (!validateEmail(email)) {
setError("Hibás email formátum.");
return;
}
if (password.length < 6) {
setError("A jelszónak legalább 6 karakter hosszúnak kell lennie.");
return;
}
if (password !== confirmPassword) {
setError("A jelszavak nem egyeznek.");
return;
}
// Backend API
console.log("Regisztráció:", { fullName, username, email, password });
};
return (
<motion.div
key="register"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.25 }}
>
<h2 className="text-4xl font-extrabold text-center mb-6 text-gray-800 tracking-wide">Regisztráció</h2>
{error && (
<div className="mb-4 text-red-600 text-center font-semibold">{error}</div>
)}
<form onSubmit={handleSubmit} className="space-y-6">
<InputBox
type="text"
placeholder="Teljes név"
value={fullName}
onChange={e => setFullName(e.target.value)}
/>
<InputBox
type="text"
placeholder="Felhasználónév"
value={username}
onChange={e => setUsername(e.target.value)}
/>
<InputBox
type="email"
placeholder="Email cím"
value={email}
onChange={e => setEmail(e.target.value)}
/>
<InputBox
type="password"
placeholder="Jelszó"
value={password}
onChange={e => setPassword(e.target.value)}
/>
<InputBox
type="password"
placeholder="Jelszó megerősítése"
value={confirmPassword}
onChange={e => setConfirmPassword(e.target.value)}
/>
<Button text="Regisztráció" type="submit" />
</form>
</motion.div>
);
}
@@ -0,0 +1,61 @@
// src/pages/Auth/ResetPassword.jsx
// Új jelszó megadása
import { useState } from "react";
import Background from "../../assets/backgrounds/Background";
import { motion } from "framer-motion";
import Button from "../../components/Buttons/Button";
import InputBox from "../../components/Inputs/InputBox";
export default function ResetPassword() {
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [error, setError] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
if (password !== confirmPassword) {
setError("A jelszavak nem egyeznek.");
return;
}
setError("");
// Backend API
console.log("Új jelszó:", password);
};
return (
<div className="relative flex items-center justify-center min-h-screen bg-gray-100 p-0 font-poppins">
<Background />
<motion.div
initial={{ height: "auto" }}
animate={{ height: "350px" }}
transition={{ duration: 0.5, ease: "easeInOut" }}
className="absolute flex max-w-2xl w-full bg-white rounded-2xl shadow-lg overflow-hidden items-center justify-center"
>
<div className="w-full p-10 relative">
<h2 className="text-4xl font-extrabold text-center mb-6 text-gray-800 tracking-wide">
Új jelszó megadása
</h2>
<form onSubmit={handleSubmit}>
<InputBox
type="password"
placeholder="Új jelszó"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<InputBox
type="password"
placeholder="Új jelszó megerősítése"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
/>
{error && (
<div className="text-red-500 text-sm mb-2">{error}</div>
)}
<Button text="Jelszó beállítása" type="submit" />
</form>
</div>
</motion.div>
</div>
);
}
@@ -0,0 +1,226 @@
import React from "react"
import Navbar from "../../components/Navbar/Navbar.jsx"
import Footer from "../../components/Footer/Footer.jsx"
import Background from "../../assets/backgrounds/Background"
import {
FaBuilding,
FaEnvelope,
FaHandshake,
FaPalette,
FaTags,
FaUserCheck,
FaDollarSign,
FaChartLine,
FaVideo,
FaHandsHelping,
FaTrophy,
FaChartBar,
FaUsers,
FaHeadset,
} from "react-icons/fa"
const Card = ({ icon, label, description, targetId, className }) => {
const handleClick = () => {
const section = document.getElementById(targetId)
if (section) {
section.scrollIntoView({ behavior: "smooth" })
}
}
return (
<div
onClick={handleClick}
className={`cursor-pointer hover:scale-105 transition-all duration-300 border border-gray-400 shadow-lg rounded-2xl p-8 w-72 text-center animate-fadeInUp ${className}`}
>
<div className="text-5xl mb-4 flex justify-center">{icon}</div>
<h3 className="text-2xl font-bold mb-2">{label}</h3>
<p className="text-white/90 text-sm">{description}</p>
</div>
)
}
const SectionContainer = ({ id, title, children }) => {
return (
<section
id={id}
className="mt-20 mb-28 px-4 md:px-0 opacity-100 animate-fadeInUp"
>
<div className="text-center mb-12">
<h2 className="text-4xl font-bold border-b-4 inline-block border-emerald-400 pb-2 text-white">
{title}
</h2>
</div>
{children}
</section>
)
}
const CompanyHub = () => {
return (
<div className="relative min-h-screen text-white">
{/* Background fixed behind everything */}
<div className="fixed inset-0 -z-10">
<Background />
</div>
<div className="relative z-10 flex flex-col min-h-screen">
<Navbar />
<main className="flex-grow relative px-4 py-8 md:px-12 md:py-16 overflow-y-auto scroll-smooth">
<div className="flex justify-center gap-6 mt-8 flex-wrap">
<Card
icon={<FaBuilding />}
label="Mit nyújtunk"
description="Játékosított tanulási platform cégeknek."
targetId="intro"
className="bg-gradient-to-br from-pink-500 via-purple-600 to-purple-800"
/>
<Card
icon={<FaEnvelope />}
label="Kapcsolat"
description="Lépj kapcsolatba velünk vagy kérj ajánlatot!"
targetId="contact"
className="bg-gradient-to-br from-blue-700 to-blue-500"
/>
<Card
icon={<FaHandshake />}
label="Csatlakozás"
description="Legyél partnerünk, és fejlődj velünk!"
targetId="join"
className="bg-gradient-to-br from-green-700 to-green-500"
/>
</div>
{/* Mit nyújtunk */}
<SectionContainer id="intro" title="Mit nyújtunk cégeknek">
<div className="grid grid-cols-1 md:grid-cols-3 gap-10">
{/* Egyénre szabás */}
<div className="bg-white/5 rounded-3xl border border-gray-700 shadow-lg p-10 flex flex-col gap-8">
<h3 className="text-2xl font-extrabold flex items-center gap-4 text-emerald-400">
<FaPalette className="text-4xl" />
Egyénre szabás
</h3>
<ul className="list-disc ml-6 space-y-4 text-white/90 text-lg">
<li className="flex items-center gap-4">
<FaUserCheck className="text-green-400 text-2xl" />
Testreszabható design és színek
</li>
<li className="flex items-center gap-4">
<FaTrophy className="text-yellow-400 text-2xl" />
Egyedi badge és jutalmazási rendszer
</li>
<li className="flex items-center gap-4">
<FaChartBar className="text-blue-400 text-2xl" />
Fejlődési útvonalak
</li>
</ul>
</div>
{/* Árazás */}
<div className="bg-white/5 rounded-3xl border border-gray-700 shadow-lg p-10 flex flex-col gap-8">
<h3 className="text-2xl font-extrabold flex items-center gap-4 text-emerald-400">
<FaDollarSign className="text-4xl" />
Árazás
</h3>
<ul className="list-disc ml-6 space-y-4 text-white/90 text-lg">
<li className="flex items-center gap-4">
<FaUsers className="text-purple-400 text-2xl" />
Kedvezményes csomagok KKV-knak
</li>
<li className="flex items-center gap-4">
<FaChartLine className="text-indigo-400 text-2xl" />
Testreszabott megoldások
</li>
<li className="flex items-center gap-4">
<FaTags className="text-pink-400 text-2xl" />
Nincs rejtett költség
</li>
</ul>
</div>
{/* Demó videó */}
<div className="bg-white/5 rounded-3xl border border-gray-700 shadow-lg p-10 flex flex-col gap-8">
<h3 className="text-2xl font-extrabold flex items-center gap-4 text-emerald-400">
<FaVideo className="text-4xl" />
Csapatunk videó
</h3>
<video controls className="w-full rounded-xl shadow-xl">
<source src="/demo.mp4" type="video/mp4" />
A böngésződ nem támogatja a videó lejátszását.
</video>
</div>
</div>
</SectionContainer>
{/* Contact + Join Section */}
<section className="grid md:grid-cols-2 gap-10 max-w-6xl mx-auto mt-20 mb-28 px-4 md:px-0">
{/* Contact */}
<div
id="contact"
className="bg-white/10 p-8 rounded-xl border border-gray-500 shadow-lg"
>
<h2 className="text-3xl font-bold mb-6 text-center border-b-4 border-emerald-400 pb-2 text-white">
Kapcsolatfelvétel cégeknek
</h2>
<form className="grid gap-6 md:grid-cols-2">
<input
type="text"
placeholder="Cég neve"
className="bg-white/20 text-white placeholder-white px-5 py-4 rounded-lg focus:outline-none focus:ring-2 focus:ring-emerald-400 col-span-2"
/>
<input
type="email"
placeholder="Email"
className="bg-white/20 text-white placeholder-white px-5 py-4 rounded-lg focus:outline-none focus:ring-2 focus:ring-emerald-400 col-span-2 md:col-span-1"
/>
<textarea
placeholder="Üzenet"
rows="6"
className="bg-white/20 text-white placeholder-white px-5 py-4 rounded-lg focus:outline-none focus:ring-2 focus:ring-emerald-400 col-span-2"
/>
<button
type="submit"
className="bg-emerald-500 hover:bg-emerald-400 text-white px-8 py-4 rounded-lg font-bold col-span-2 md:col-span-1 transition"
>
Küldés
</button>
</form>
</div>
{/* Join */}
<div
id="join"
className="bg-white/10 p-8 rounded-xl border border-gray-500 shadow-lg"
>
<h2 className="text-3xl font-bold mb-6 text-center border-b-4 border-emerald-400 pb-2 text-white">
Csatlakozz partnerként
</h2>
<ul className="list-disc space-y-4 ml-8 text-base text-white/90">
<li className="flex gap-3 items-center">
<FaHandsHelping className="text-green-400 text-xl flex-shrink-0" />
Gamification a vállalati kultúrában
</li>
<li className="flex gap-3 items-center">
<FaChartBar className="text-blue-400 text-xl flex-shrink-0" />
Teljesítménymérés játékosan
</li>
<li className="flex gap-3 items-center">
<FaUserCheck className="text-yellow-400 text-xl flex-shrink-0" />
Személyre szabott riportok
</li>
<li className="flex gap-3 items-center">
<FaHeadset className="text-pink-400 text-xl flex-shrink-0" />
Dedikált ügyfélszolgálat
</li>
</ul>
</div>
</section>
</main>
<Footer />
</div>
</div>
)
}
export default CompanyHub
@@ -0,0 +1,16 @@
// src/pages/Decks/DeckManagerPage.jsx
// Deck Management Page (with Navbar, no Footer)
import DeckManager from "../../components/Landingpage/DeckManager.jsx"
import Navbar from "../../components/Navbar/Navbar.jsx"
export default function DeckManagerPage() {
return (
<div className="w-full min-h-screen bg-background flex flex-col relative overflow-x-hidden">
<Navbar />
<main className="flex-1 flex flex-col items-center justify-start min-h-0">
<DeckManager />
</main>
</div>
)
}
@@ -0,0 +1,235 @@
import React, { useState } from "react"
import { getVerticalOffset } from "../../utils/randomUtils"
import Dice from "../../utils/dice/Dice"
const GameScreen = () => {
const boardRows = 5
const boardCols = 20
const totalCells = boardRows * boardCols
const cellSize = 40
const cellMargin = 2.5
const rowSpacing = 70 // Extra spacing between rows
const topOffset = rowSpacing * 0.5 // Increase topOffset for more spacing
const bottomOffset = rowSpacing * 0.5 // Increase bottomOffset for more spacing
const boardWidthPx = boardCols * (cellSize + cellMargin * 2)
const boardHeightPx =
boardRows * (cellSize + cellMargin * 2 + rowSpacing) + topOffset + bottomOffset - rowSpacing
// Generate a snake-like path with vertical spacing and vertical offsets
const generateWindingPath = () => {
const path = []
let currentNum = 1
for (let row = 0; row < boardRows && currentNum <= totalCells; row++) {
// Calculate the y position with extra row spacing
const baseYPosition = topOffset + row * (cellSize + cellMargin * 2 + rowSpacing)
// If row number is even, go right; if odd, go left
if (row % 2 === 0) {
// Left to right
for (let col = 0; col < boardCols && currentNum <= totalCells; col++) {
path.push({
number: currentNum++,
x: col * (cellSize + cellMargin * 2),
y: baseYPosition + getVerticalOffset(currentNum - 1),
type: getFieldType(currentNum - 1),
})
}
} else {
// Right to left
for (let col = boardCols - 1; col >= 0 && currentNum <= totalCells; col--) {
path.push({
number: currentNum++,
x: col * (cellSize + cellMargin * 2),
y: baseYPosition + getVerticalOffset(currentNum - 1),
type: getFieldType(currentNum - 1),
})
}
}
}
return path
}
const getFieldType = (count) => {
if (count % 17 === 0) return "clover"
if (count % 13 === 0) return "bad"
if ((count + 5) % 13 === 0) return "good"
return "regular"
}
const [path, setPath] = useState(generateWindingPath())
const [players, setPlayers] = useState([
{ id: 1, name: "Béla", position: 34, score: 25, color: "bg-blue-600", emoji: "🐍" },
{ id: 2, name: "Juci", position: 50, score: 30, color: "bg-green-600", emoji: "🐢" },
{ id: 3, name: "Kati", position: 70, score: 15, color: "bg-purple-600", emoji: "🐇" },
{ id: 3, name: "Fürtös", position: 68, score: 14, color: "bg-yellow-600", emoji: "😂" },
])
// Sort players by position in descending order
const sortedPlayers = [...players].sort((a, b) => b.position - a.position)
// Handle dice roll
const handleDiceRoll = (value) => {
console.log("Rolled:", value)
// You can add logic here to move the current player based on the dice value
}
console.log("Generated path length:", path.length)
const getFieldStyle = (type) => {
switch (type) {
case "clover":
return "bg-teal-700 border-teal-500 shadow-teal-800"
case "bad":
return "bg-red-800 border-red-600 shadow-red-900"
case "good":
return "bg-blue-800 border-blue-600 shadow-blue-900"
default:
return "bg-gray-800 border-gray-600 shadow-gray-900"
}
}
const getPlayerPosition = (playerPosition) => {
const field = path.find((p) => p.number === playerPosition)
return field ? { top: `${field.y}px`, left: `${field.x}px` } : { top: 0, left: 0 }
}
// Function to get medal style based on rank
const getMedalStyle = (rank) => {
switch (rank) {
case 1:
return "bg-yellow-400 text-yellow-900 border-yellow-500 shadow-yellow-600"
case 2:
return "bg-gray-400 text-gray-900 border-gray-500 shadow-gray-600"
case 3:
return "bg-orange-500 text-orange-900 border-orange-600 shadow-orange-700"
default:
return "bg-gray-700 text-gray-300 border-gray-600 shadow-gray-800"
}
}
return (
<div className="p-4 bg-gradient-to-br from-gray-900 via-gray-800 to-teal-900 min-h-screen flex items-center justify-center">
<div className="w-full">
<div className="flex flex-col md:flex-row gap-6 justify-center">
{/* Game Board */}
<div className="relative bg-gray-800 p-6 rounded-2xl shadow-xl border border-teal-700 flex flex-col items-center justify-center overflow-hidden">
{/* Háttér */}
<div className="absolute w-full h-full opacity-10 pointer-events-none overflow-hidden">
{[...Array(35)].map((_, i) => (
// Sajat pulse effect! => node_modules/tailwindcss/index.css:
// --animate-pulse8: pulse 6s cubic-bezier(0.4, 0.2, 0.6, 1) infinite;
<div
key={i}
className="absolute rounded-full bg-teal-600 animate-pulse8"
style={{
width: Math.random() * 120 + 40 + "px",
height: Math.random() * 120 + 40 + "px",
top: Math.random() * 100 + "%",
left: Math.random() * 100 + "%",
transform: "translate(-50%, -50%)",
}}
></div>
))}
</div>
<div className="relative" style={{ height: `${boardHeightPx}px`, width: `${boardWidthPx}px` }}>
{/* Mezők */}
{path.map((field) => (
<div
key={field.number}
className={`absolute w-10 h-10 border-2 flex items-center justify-center transition-all shadow-md hover:scale-110 ${getFieldStyle(
field.type
)}`}
style={{
top: `${field.y}px`,
left: `${field.x}px`,
width: `${cellSize}px`,
height: `${cellSize}px`,
margin: `${cellMargin}px`,
}}
>
<span className="text-gray-300 text-sm font-bold">{field.number}</span>
</div>
))}
{/* Player tokens */}
{players.map((player) => (
<div
key={player.id}
className={`absolute w-6 h-6 ${player.color} rounded-full border-2 border-white shadow-lg flex items-center justify-center text-white text-xs font-bold z-10 animate-bounce`}
style={{
...getPlayerPosition(player.position),
transform: "translate(18px, 18px)",
}}
>
{player.emoji}
</div>
))}
</div>
{/* Game information */}
{/* <div className="bg-white rounded-xl p-2 shadow-lg border border-indigo-100 max-w-3xl mx-auto mt-4 z-10">
<p className="text-gray-600 text-sm text-center">
<span className="inline-flex items-center mx-2"><span className="w-3 h-3 bg-white border border-gray-300 rounded-full mr-1"></span> Sima</span>
<span className="inline-flex items-center mx-2"><span className="w-3 h-3 bg-green-200 border border-green-500 rounded-full mr-1"></span> Lóhere</span>
<span className="inline-flex items-center mx-2"><span className="w-3 h-3 bg-red-200 border border-red-500 rounded-full mr-1"></span> Rossz</span>
<span className="inline-flex items-center mx-2"><span className="w-3 h-3 bg-blue-200 border border-blue-500 rounded-full mr-1"></span> Jó</span>
</p>
</div> */}
</div>
{/* Right sidebar */}
<div className="flex-1 max-w-md">
<div className="bg-gray-800 rounded-xl p-4 shadow-lg mb-4 border border-teal-700">
<h2 className="text-xl font-semibold mb-3 text-teal-300">Játékosok</h2>
{sortedPlayers.map((player, index) => (
<div
key={player.id}
className="flex items-center mb-3 p-2 bg-gray-900 rounded-lg hover:bg-gray-800 transition-colors"
>
<div
className={`w-8 h-8 ${player.color} rounded-full mr-3 flex items-center justify-center text-white text-sm font-bold shadow-md`}
>
{player.emoji}
</div>
<div className="flex-1">
<div className="font-medium text-sm text-gray-300 flex items-center">
{player.name}
<span
className={`ml-2 px-2 py-1 rounded-full border text-xs font-bold shadow-md ${getMedalStyle(
index + 1
)}`}
>
{index + 1 === 1
? "🥇 1st"
: index + 1 === 2
? "🥈 2nd"
: index + 1 === 3
? "🥉 3rd"
: `${index + 1}th`}
</span>
</div>
<div className="text-xs text-gray-500">
Pozíció: {player.position} Pontszám: {player.score}
</div>
</div>
</div>
))}
</div>
{/* Dice Container */}
<div className="bg-gray-800 rounded-xl p-4 shadow-lg border border-teal-700 text-center">
<h2 className="text-xl font-semibold mb-3 text-teal-300">Dobókocka</h2>
<p className="text-gray-300 text-sm mb-4">Kattints a kockára dobáshoz!</p>
<Dice onRoll={handleDiceRoll} />
</div>
</div>
</div>
</div>
</div>
)
}
export default GameScreen
@@ -0,0 +1,35 @@
// src/pages/Home/Home.jsx
// Régi PlayMenu-s oldal, "Home" néven
import { useState } from "react"
import Navbar from "../../components/Navbar/Navbar"
import Footer from "../../components/Footer/Footer.jsx"
import Background from "../../assets/backgrounds/Background.jsx"
import PlayMenu from "../../components/Landingpage/PlayMenu.jsx"
export default function Home() {
// Dummy callbackok és user példa
const handleJoinGame = (code) => {
alert(`Csatlakozás játékhoz: ${code}`)
}
const handleCreateGame = () => {
alert("Új játék létrehozása")
}
const user = { name: "Teszt Elek" }
return (
<div className="w-full min-h-screen flex flex-col relative overflow-x-hidden">
<div className="fixed inset-0 -z-10 pointer-events-none">
<Background />
</div>
<div className="fixed top-0 left-0 right-0 z-30">
<Navbar />
</div>
<main className="flex-1 flex flex-col items-center justify-start py-15 min-h-0 mt-[64px]">
<PlayMenu onJoinGame={handleJoinGame} onCreateGame={handleCreateGame} user={user} />
{/* Ide jöhetnek további szekciók, ha szeretnél még tartalmat */}
</main>
<Footer />
</div>
)
}
@@ -0,0 +1,35 @@
// src/pages/Home/Home.jsx
// Régi PlayMenu-s oldal, "Home" néven
import { useState } from "react"
import Navbar from "../../components/Navbar/Navbar.jsx"
import Footer from "../../components/Footer/Footer.jsx"
import Background from "../../assets/backgrounds/Background.jsx"
import PlayMenu from "../../components/Landingpage/PlayMenu.jsx"
export default function Home() {
// Dummy callbackok és user példa
const handleJoinGame = (code) => {
alert(`Csatlakozás játékhoz: ${code}`)
}
const handleCreateGame = () => {
alert("Új játék létrehozása")
}
const user = { name: "Teszt Elek" }
return (
<div className="w-full min-h-screen flex flex-col relative overflow-x-hidden">
<div className="fixed inset-0 -z-10 pointer-events-none">
<Background />
</div>
<div className="fixed top-0 left-0 right-0 z-30">
<Navbar />
</div>
<main className="flex-1 flex flex-col items-center justify-start py-15 min-h-0 mt-[64px]">
<PlayMenu onJoinGame={handleJoinGame} onCreateGame={handleCreateGame} user={user} />
{/* Ide jöhetnek további szekciók, ha szeretnél még tartalmat */}
</main>
<Footer />
</div>
)
}
@@ -0,0 +1,36 @@
// src/pages/Landing/Landingpage.jsx
// Főoldal - Landing Page
import { useState } from "react"
import Navbar from "../../components/Navbar/Navbar"
import Footer from "../../components/Footer/Footer.jsx"
import Background from "../../assets/backgrounds/Background.jsx"
import LandingPage from "../../components/Landingpage/LandingPage.jsx"
export default function LandingPageMain() {
// Navigációs callbackok
const handleNavigateToPlay = () => {
// Itt lehet navigálni a játék oldalra
alert("Navigáció a játék oldalra")
}
const handleNavigateToAuth = () => {
// Itt lehet navigálni a bejelentkezés oldalra
alert("Navigáció a bejelentkezés oldalra")
}
return (
<div className="w-full min-h-screen flex flex-col relative overflow-x-hidden">
<div className="fixed inset-0 -z-10 pointer-events-none">
<Background />
</div>
<div className="fixed top-0 left-0 right-0 z-30">
<Navbar />
</div>
<main className="flex-1 flex flex-col items-center justify-start py-15 min-h-0 mt-[64px]">
<LandingPage onNavigateToPlay={handleNavigateToPlay} onNavigateToAuth={handleNavigateToAuth} />
</main>
<Footer />
</div>
)
}
@@ -0,0 +1,78 @@
import { useState } from "react"
import Button from "../../components/Buttons/Button"
import InputBox from "../../components/Inputs/InputBox"
import PopUp from "../../components/PopUp/PopUp"
import Logo from "../../assets/pictures/Logo.jsx"
import Navbar from "../../components/Navbar/Navbar"
import Footer from "../../components/Footer/Footer.jsx"
import UserProfile from "../../components/Userdetails/Userdetails.jsx"
import CompanyHub from "../Companies/Companies.jsx"
import RatingSet from "../../components/PopUp/RatingSet" // <- statisztikai komponens
export default function Test() {
const [showRegistrationPopup, setShowRegistrationPopup] = useState(false)
const [showPreviewPopup, setShowPreviewPopup] = useState(false) // <- új state
const [inputValue, setInputValue] = useState("")
return (
<div className="w-full h-screen flex flex-col">
<Navbar />
<UserProfile />
<CompanyHub />
<div className="flex-1 flex flex-col items-center justify-center space-y-6">
<InputBox
placeholder="E-mail cím"
type="text"
width="w-1/2"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<div className="flex flex-col sm:flex-row sm:space-x-4 space-y-4 sm:space-y-0 w-1/2">
<Button
text="Regisztráció"
type="button"
width="w-full"
onClick={() => setShowRegistrationPopup(true)}
/>
<Button
text="Előnézet"
type="button"
width="w-full"
onClick={() => setShowPreviewPopup(true)} // <- másik state trigger
/>
</div>
{/* Regisztrációs popup */}
{showRegistrationPopup && (
<PopUp onClose={() => setShowRegistrationPopup(false)}>
<div className="flex flex-col items-center space-y-4">
{/* <Logo size={120} /> */}
<h1 className="text-2xl font-bold text-center">Sikeres regisztráció!</h1>
<p className="text-center text-gray-600">
Kérjük, erősítsd meg az e-mail címedet!
<br />
Egy megerősítő linket küldtünk az általad megadott e-mail címre
<span className="font-semibold text-black"> {inputValue}</span>.
</p>
<p className="text-center text-sm text-gray-500">
Ha nem kaptad meg a levelet, ellenőrizd a spam mappádat is!
</p>
<Button text="Bezár" type="button" width="w-24" onClick={() => setShowRegistrationPopup(false)} />
</div>
</PopUp>
)}
{/* Előnézeti popup (RatingSet) */}
{showPreviewPopup && (
<PopUp onClose={() => setShowPreviewPopup(false)}>
<RatingSet />
</PopUp>
)}
</div>
<Footer />
</div>
)
}