diff --git a/SerpentRace_Frontend/package-lock.json b/SerpentRace_Frontend/package-lock.json index ba9a282a..caa2c59d 100644 --- a/SerpentRace_Frontend/package-lock.json +++ b/SerpentRace_Frontend/package-lock.json @@ -9,8 +9,10 @@ "version": "0.0.0", "dependencies": { "@tailwindcss/vite": "^4.1.7", + "framer-motion": "^12.12.1", "react": "^19.1.0", "react-dom": "^19.1.0", + "react-router-dom": "^7.6.0", "tailwindcss": "^4.1.7" }, "devDependencies": { @@ -2584,6 +2586,33 @@ "node": ">= 0.6" } }, + "node_modules/framer-motion": { + "version": "12.12.1", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.12.1.tgz", + "integrity": "sha512-PFw4/GCREHI2suK/NlPSUxd+x6Rkp80uQsfCRFSOQNrm5pZif7eGtmG1VaD/UF1fW9tRBy5AaS77StatB3OJDg==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.12.1", + "motion-utils": "^12.12.1", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/fresh": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", @@ -3344,6 +3373,21 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/motion-dom": { + "version": "12.12.1", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.12.1.tgz", + "integrity": "sha512-GXq/uUbZBEiFFE+K1Z/sxdPdadMdfJ/jmBALDfIuHGi0NmtealLOfH9FqT+6aNPgVx8ilq0DtYmyQlo6Uj9LKQ==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.12.1" + } + }, + "node_modules/motion-utils": { + "version": "12.12.1", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.12.1.tgz", + "integrity": "sha512-f9qiqUHm7hWSLlNW8gS9pisnsN7CRFRD58vNjptKdsqFLpkVnX00TNeD6Q0d27V9KzT7ySFyK1TZ/DShfVOv6w==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -3705,6 +3749,53 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.0.tgz", + "integrity": "sha512-GGufuHIVCJDbnIAXP3P9Sxzq3UUsddG3rrI3ut1q6m0FI6vxVBF3JoPQ38+W/blslLH4a5Yutp8drkEpXoddGQ==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.6.0.tgz", + "integrity": "sha512-DYgm6RDEuKdopSyGOWZGtDfSm7Aofb8CCzgkliTjtu/eDuB0gcsv6qdFhhi8HdtmA+KHkt5MfZ5K2PdzjugYsA==", + "license": "MIT", + "dependencies": { + "react-router": "7.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/react-router/node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -3854,6 +3945,12 @@ "node": ">= 18" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -4072,6 +4169,12 @@ "node": ">=0.6" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/SerpentRace_Frontend/package.json b/SerpentRace_Frontend/package.json index b844061e..913ca42c 100644 --- a/SerpentRace_Frontend/package.json +++ b/SerpentRace_Frontend/package.json @@ -11,8 +11,10 @@ }, "dependencies": { "@tailwindcss/vite": "^4.1.7", + "framer-motion": "^12.12.1", "react": "^19.1.0", "react-dom": "^19.1.0", + "react-router-dom": "^7.6.0", "tailwindcss": "^4.1.7" }, "devDependencies": { diff --git a/SerpentRace_Frontend/src/App.css b/SerpentRace_Frontend/src/App.css new file mode 100644 index 00000000..e69de29b diff --git a/SerpentRace_Frontend/src/App.jsx b/SerpentRace_Frontend/src/App.jsx index 4410e2f8..77fb4478 100644 --- a/SerpentRace_Frontend/src/App.jsx +++ b/SerpentRace_Frontend/src/App.jsx @@ -1,26 +1,47 @@ -import { useState } from 'react' +import { useState, useEffect } from "react"; +import { BrowserRouter as Router, Route, Routes } from "react-router-dom"; +import AuthRegister from "./pages/Auth/AuthRegister"; +import AuthLogin from "./pages/Auth/AuthLogin"; +import EmailVerification from "./pages/Auth/EmailVerification"; +import Test from "./pages/Testing/Test"; + function App() { - const [count, setCount] = useState(0) + const [isMobile, setIsMobile] = useState(false); + + useEffect(() => { + const handleResize = () => { + setIsMobile(window.innerWidth <= 1280); + }; + + handleResize(); + window.addEventListener("resize", handleResize); + + return () => window.removeEventListener("resize", handleResize); + }, []); + + // if (isMobile) { + // return ( + // + // + // } /> + // } /> + // } /> + // + // + // ); + // } return ( - <> -
-

primary

-

secondary

-

accent

-

background

-

surface

-

card

-

text

-

text-muted

-

text-inverse

-

success

-

warning

-

error

-
- - ) + + + } /> + } /> + } /> + } /> + + + ); } -export default App +export default App; \ No newline at end of file diff --git a/SerpentRace_Frontend/src/assets/SerpentRace_Animation/Path.module.css b/SerpentRace_Frontend/src/assets/SerpentRace_Animation/Path.module.css new file mode 100755 index 00000000..442bd5cf --- /dev/null +++ b/SerpentRace_Frontend/src/assets/SerpentRace_Animation/Path.module.css @@ -0,0 +1,95 @@ +.animation { + animation: fill 0.5s ease forwards 2.9s; +} + +.path0 { + stroke-dasharray: 603.0596923828125; + stroke-dashoffset: 603.0596923828125; + animation: draw 3s ease-in-out forwards; + animation-delay: 0.45s; +} + +.path1 { + stroke-dasharray: 503.0904846191406; + stroke-dashoffset: 503.0904846191406; + animation: draw 3s ease-in-out forwards; + animation-delay: 0.5s; +} + +.path2 { + stroke-dasharray: 625.779541015625; + stroke-dashoffset: 625.779541015625; + animation: draw 3s ease-in-out forwards; + animation-delay: 0.45s; +} + +.path3 { + stroke-dasharray: 714.129638671875; + stroke-dashoffset: 714.129638671875; + animation: draw 3s ease-in-out forwards; + animation-delay: 0.4s; +} + +.path4 { + stroke-dasharray: 427.98114013671875; + stroke-dashoffset: 427.98114013671875; + animation: draw 3s ease-in-out forwards; + animation-delay: 0.35s; +} + +.path5 { + stroke-dasharray: 593.7645263671875; + stroke-dashoffset: 593.7645263671875; + animation: draw 3s ease-in-out forwards; + animation-delay: 0.3s; +} + +.path6 { + stroke-dasharray: 603.0399780273438; + stroke-dashoffset: 603.0399780273438; + animation: draw 3s ease-in-out forwards; + animation-delay: 0.25s; +} + +.path7 { + stroke-dasharray: 731.757568359375; + stroke-dashoffset: 731.757568359375; + animation: draw 3s ease-in-out forwards; + animation-delay: 0.2s; +} + +.path8 { + stroke-dasharray: 382.3065185546875; + stroke-dashoffset: 382.3065185546875; + animation: draw 3s ease-in-out forwards; + animation-delay: 0.2s; +} + +.path9 { + stroke-dasharray: 603.0382690429688; + stroke-dashoffset: 603.0382690429688; + animation: draw 3s ease-in-out forwards; + animation-delay: 0.15s; +} + +.path10 { + stroke-dasharray: 652.2447509765625; + stroke-dashoffset: 652.2447509765625; + animation: draw 3s ease-in-out forwards; + animation-delay: 0.1s; +} + +@keyframes draw { + to { + stroke-dashoffset: 0; + } +} + +@keyframes fill { + from { + fill: transparent; + } + to { + fill: #ffffff; + } +} diff --git a/SerpentRace_Frontend/src/assets/SerpentRace_Animation/SerpentRace_Animation.jsx b/SerpentRace_Frontend/src/assets/SerpentRace_Animation/SerpentRace_Animation.jsx new file mode 100755 index 00000000..adafd242 --- /dev/null +++ b/SerpentRace_Frontend/src/assets/SerpentRace_Animation/SerpentRace_Animation.jsx @@ -0,0 +1,31 @@ +import styles from "./Path.module.css"; +import React, { useRef } from "react"; + +const Animation = ({ sizePercentage = 100 }) => { + const width = (1253 * sizePercentage) / 100; + const height = (136 * sizePercentage) / 100; + + // 11 path-hoz refs + const pathRefs = Array.from({ length: 11 }, () => useRef(null)); + + return ( +
+ {/* prettier-ignore */} + + + + + + + + + + + + + +
+ ); +}; + +export default Animation; diff --git a/SerpentRace_Frontend/src/assets/backgrounds/Background.jsx b/SerpentRace_Frontend/src/assets/backgrounds/Background.jsx new file mode 100755 index 00000000..0dd11f97 --- /dev/null +++ b/SerpentRace_Frontend/src/assets/backgrounds/Background.jsx @@ -0,0 +1,102 @@ +import React, { useEffect, useState } from "react"; +import { motion } from "framer-motion"; + +const Background = () => { + const [gridSize, setGridSize] = useState({ cols: 12, rows: 6 }); + const [mousePos, setMousePos] = useState({ x: 0, y: 0 }); + const [path, setPath] = useState([]); + + useEffect(() => { + const updateGrid = () => { + const width = window.innerWidth; + const height = window.innerHeight; + const cols = Math.max(8, Math.floor(width / 100)); // 100px-es cellák + const rows = Math.max(5, Math.floor(height / 100)); // 100px-es cellák + setGridSize({ cols, rows }); + }; + + const handleMouseMove = (e) => { + setMousePos({ x: e.clientX, y: e.clientY }); + }; + + updateGrid(); + window.addEventListener("resize", updateGrid); + window.addEventListener("mousemove", handleMouseMove); + + return () => { + window.removeEventListener("resize", updateGrid); + window.removeEventListener("mousemove", handleMouseMove); + }; + }, []); + + useEffect(() => { + const interval = setInterval(() => { + const newCol = Math.floor(Math.random() * gridSize.cols); + const newRow = Math.floor(Math.random() * gridSize.rows); + setPath((prevPath) => { + const newPath = [...prevPath, { col: newCol, row: newRow, opacity: 1 }]; // Új pont hozzáadása + if (newPath.length > 10) { + newPath.shift(); // Max 10 pont az útvonalon + } + return newPath; + }); + }, 500); // Új pont hozzáadása minden 500ms-ben + + // Az útvonal pontjainak fokozatos eltűnése + const fadeInterval = setInterval(() => { + setPath((prevPath) => + prevPath.map((point) => ({ + ...point, + opacity: Math.max(0, point.opacity - 0.05), // Fokozatosan csökkentjük az opacity-t + })).filter((point) => point.opacity > 0) // Eltávolítjuk a teljesen eltűnt pontokat + ); + }, 100); // Opacity frissítése minden 100ms-ben + + return () => { + clearInterval(interval); + clearInterval(fadeInterval); + }; + }, [gridSize]); + + return ( +
+
+ {[...Array(gridSize.cols * gridSize.rows)].map((_, i) => { + const col = i % gridSize.cols; + const row = Math.floor(i / gridSize.cols); + const cellX = (col + 0.5) * (window.innerWidth / gridSize.cols); + const cellY = (row + 0.5) * (window.innerHeight / gridSize.rows); + + const dx = cellX - mousePos.x; + const dy = cellY - mousePos.y; + const distance = Math.sqrt(dx * dx + dy * dy); + const distanceFactor = Math.max(0, 1 - distance / 300); // Egér hatótávolsága + + // Az útvonalban lévő pontok opacitása + const pathPoint = path.find((p) => p.col === col && p.row === row); + const pathOpacity = pathPoint ? pathPoint.opacity : 0; + + return ( + + ); + })} +
+
+ ); +}; + +export default Background; \ No newline at end of file diff --git a/SerpentRace_Frontend/src/assets/pictures/Logo.jsx b/SerpentRace_Frontend/src/assets/pictures/Logo.jsx new file mode 100644 index 00000000..6f3fb92c --- /dev/null +++ b/SerpentRace_Frontend/src/assets/pictures/Logo.jsx @@ -0,0 +1,15 @@ +import React from 'react'; +import logo from './Logo.png'; + +const Logo = ({ size = 100 }) => ( + Logo +); + +export default Logo; + diff --git a/SerpentRace_Frontend/src/assets/pictures/Logo.png b/SerpentRace_Frontend/src/assets/pictures/Logo.png new file mode 100644 index 00000000..480d8c5d Binary files /dev/null and b/SerpentRace_Frontend/src/assets/pictures/Logo.png differ diff --git a/SerpentRace_Frontend/src/components/Buttons/Button.jsx b/SerpentRace_Frontend/src/components/Buttons/Button.jsx new file mode 100755 index 00000000..d3bc9ed5 --- /dev/null +++ b/SerpentRace_Frontend/src/components/Buttons/Button.jsx @@ -0,0 +1,18 @@ +// src/components/Inputs/InputBox.jsx +// Gomb komponens + +import { motion } from "framer-motion"; + +export default function Button({ text, type, onClick }) { + return ( + + {text} + + ); +} diff --git a/SerpentRace_Frontend/src/components/Inputs/InputBox.jsx b/SerpentRace_Frontend/src/components/Inputs/InputBox.jsx new file mode 100755 index 00000000..1a907e19 --- /dev/null +++ b/SerpentRace_Frontend/src/components/Inputs/InputBox.jsx @@ -0,0 +1,16 @@ +// src/components/Inputs/InputBox.jsx +// InputBox komponens + +export default function InputBox({ type, placeholder, value, onChange }) { + return ( +
+ +
+ ); + } diff --git a/SerpentRace_Frontend/src/index.css b/SerpentRace_Frontend/src/index.css index 9cc9c166..fbef2090 100644 --- a/SerpentRace_Frontend/src/index.css +++ b/SerpentRace_Frontend/src/index.css @@ -3,11 +3,18 @@ @theme { /* Fő színek */ --color-night: #0d0d0f; - --color-mint: #5fa985; --color-battleship-gray: #8d8e83; --color-gunmetal: #222d2f; --color-eerie-black: #181d23; + --color-mint: #5fa985; + --color-mint-dark: #1e3328; + --color-mint-darker: #0f2019; + + /* Gombok */ + --color-button-primary: #5fa985; + --color-button-hover: #4b7e65; + /* Funkcionális színek */ --color-primary: #5fa985; --color-secondary: #8d8e83; diff --git a/SerpentRace_Frontend/src/pages/Auth/AuthCard.jsx b/SerpentRace_Frontend/src/pages/Auth/AuthCard.jsx new file mode 100755 index 00000000..e3656e24 --- /dev/null +++ b/SerpentRace_Frontend/src/pages/Auth/AuthCard.jsx @@ -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 ( + + {/* Bal oldali kép és szöveg */} +
+ +
+ +

+ Lépj be és légy a legjobb! +

+
+ + {/* Jobb oldali űrlap */} +
+ + {isRegistering ? : } + + setIsRegistering(!isRegistering)} + > + {isRegistering + ? "Már van fiókod? Jelentkezz be itt!" + : "Nincs még fiókod? Regisztrálj itt!"} + +
+ + ); +} diff --git a/SerpentRace_Frontend/src/pages/Auth/AuthLogin.jsx b/SerpentRace_Frontend/src/pages/Auth/AuthLogin.jsx new file mode 100755 index 00000000..6b3eda73 --- /dev/null +++ b/SerpentRace_Frontend/src/pages/Auth/AuthLogin.jsx @@ -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 ( +
+ + + +
+ ); +} diff --git a/SerpentRace_Frontend/src/pages/Auth/AuthRegister.jsx b/SerpentRace_Frontend/src/pages/Auth/AuthRegister.jsx new file mode 100755 index 00000000..e4716f57 --- /dev/null +++ b/SerpentRace_Frontend/src/pages/Auth/AuthRegister.jsx @@ -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 ( +
+ + +
+ ); +} diff --git a/SerpentRace_Frontend/src/pages/Auth/LoginForm.jsx b/SerpentRace_Frontend/src/pages/Auth/LoginForm.jsx new file mode 100755 index 00000000..bafbac6c --- /dev/null +++ b/SerpentRace_Frontend/src/pages/Auth/LoginForm.jsx @@ -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 ( + +

Bejelentkezés

+ {error && ( +
{error}
+ )} +
+ setEmail(e.target.value)} + /> + setPassword(e.target.value)} + /> +