7 Commits

Author SHA1 Message Date
Walke d23328763b footer fix meg landingon 2025-10-15 15:24:06 +02:00
mategergely33 1db1776217 Merge pull request 'Registration redirect frontend fix' (#41) from registration into main
Reviewed-on: #41
2025-09-30 11:39:46 +00:00
Walke 87dc8ffff4 Registration redirect frontend fix 2025-09-29 21:53:52 +02:00
Donat 04a87b8293 Merge pull request 'last_bugfix' (#40) from merge_branch into main
Reviewed-on: #40
2025-09-29 18:36:57 +00:00
Donat a25807aca1 last_bugfix 2025-09-29 20:36:35 +02:00
Donat 9e88eba43f Merge pull request 'bugfix' (#39) from merge_branch into main
Reviewed-on: #39
2025-09-29 11:46:04 +00:00
Donat 14a94ea03f bugfix 2025-09-29 13:45:25 +02:00
15 changed files with 288 additions and 209 deletions
+2 -1
View File
@@ -1,5 +1,6 @@
/* build-hook-start *//*00001*/try { require('c:\\Users\\magdo\\.vscode\\extensions\\wallabyjs.console-ninja-1.0.475\\out\\buildHook\\index.js').default({tool: 'jest', checkSum: '205eed3d62795e076a6692BlVLVB1RDABVWgJcB1QHWAIOD1FW', mode: 'build'}); } catch(cjsError) { try { import('file:///c:/Users/magdo/.vscode/extensions/wallabyjs.console-ninja-1.0.475/out/buildHook/index.js').then(m => m.default.default({tool: 'jest', checkSum: '205eed3d62795e076a6692BlVLVB1RDABVWgJcB1QHWAIOD1FW', mode: 'build'})).catch(esmError => {}) } catch(esmError) {}}/* build-hook-end */
/* build-hook-start *//*00001*/try { require('c:\\Users\\magdo\\.vscode\\extensions\\wallabyjs.console-ninja-1.0.475\\out\\buildHook\\index.js').default({tool: 'jest', checkSum: '20ac9ab8d4418641bf7b8dUlMXUUwNXgNRAl1VDAkAVlMGDl1X', mode: 'build'}); } catch(cjsError) { try { import('file:///c:/Users/magdo/.vscode/extensions/wallabyjs.console-ninja-1.0.475/out/buildHook/index.js').then(m => m.default.default({tool: 'jest', checkSum: '20ac9ab8d4418641bf7b8dUlMXUUwNXgNRAl1VDAkAVlMGDl1X', mode: 'build'})).catch(esmError => {}) } catch(esmError) {}}/* build-hook-end */
/*!
* /**
* * Copyright (c) Meta Platforms, Inc. and affiliates.
+2 -1
View File
@@ -1,5 +1,6 @@
#!/usr/bin/env node
/* build-hook-start *//*00001*/try { require('c:\\Users\\magdo\\.vscode\\extensions\\wallabyjs.console-ninja-1.0.475\\out\\buildHook\\index.js').default({tool: 'jest', checkSum: '205eed3d62795e076a6692BlVLVB1RDABVWgJcB1QHWAIOD1FW', mode: 'build'}); } catch(cjsError) { try { import('file:///c:/Users/magdo/.vscode/extensions/wallabyjs.console-ninja-1.0.475/out/buildHook/index.js').then(m => m.default.default({tool: 'jest', checkSum: '205eed3d62795e076a6692BlVLVB1RDABVWgJcB1QHWAIOD1FW', mode: 'build'})).catch(esmError => {}) } catch(esmError) {}}/* build-hook-end */
/* build-hook-start *//*00001*/try { require('c:\\Users\\magdo\\.vscode\\extensions\\wallabyjs.console-ninja-1.0.475\\out\\buildHook\\index.js').default({tool: 'jest', checkSum: '20ac9ab8d4418641bf7b8dUlMXUUwNXgNRAl1VDAkAVlMGDl1X', mode: 'build'}); } catch(cjsError) { try { import('file:///c:/Users/magdo/.vscode/extensions/wallabyjs.console-ninja-1.0.475/out/buildHook/index.js').then(m => m.default.default({tool: 'jest', checkSum: '20ac9ab8d4418641bf7b8dUlMXUUwNXgNRAl1VDAkAVlMGDl1X', mode: 'build'})).catch(esmError => {}) } catch(esmError) {}}/* build-hook-end */
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
@@ -6,6 +6,5 @@ export interface CreateUserCommand {
lname: string;
code?: string;
orgid?: string;
type: string;
phone?: string;
}
@@ -7,7 +7,6 @@ export interface UpdateUserCommand {
fname?: string;
lname?: string;
code?: string;
type?: string;
phone?: string;
state?: number;
}
+1 -1
View File
@@ -83,7 +83,7 @@ services:
POSTGRES_INITDB_ARGS: "--encoding=UTF-8"
volumes:
- postgres_dev_data:/var/lib/postgresql/data
- ./sql_dump_with_test_data.sql:/docker-entrypoint-initdb.d/init.sql:ro
- ./sql_schema_only.sql:/docker-entrypoint-initdb.d/init.sql:ro
networks:
- serpentrace-network
healthcheck:
+1 -1
View File
@@ -116,7 +116,7 @@ services:
POSTGRES_INITDB_ARGS: "--encoding=UTF-8"
volumes:
- postgres_dev_data:/var/lib/postgresql/data
- ./sql_dump_with_test_data.sql:/docker-entrypoint-initdb.d/init.sql:ro
- ./sql_schema_only.sql:/docker-entrypoint-initdb.d/init.sql:ro
networks:
- serpentrace-network
healthcheck:
@@ -48,7 +48,6 @@ CREATE TABLE "Users" (
"lname" character varying(100) NOT NULL,
"token" character varying(255),
"TokenExpires" TIMESTAMP,
"type" character varying(50) NOT NULL,
"phone" character varying(20),
"state" integer NOT NULL DEFAULT 0,
"regdate" TIMESTAMP NOT NULL DEFAULT now(),
@@ -154,11 +153,11 @@ INSERT INTO "Organizations" ("id", "name", "contactfname", "contactlname", "cont
('33333333-3333-3333-3333-333333333333', 'Healthcare Corp', 'Michael', 'Brown', '+1-555-0003', 'michael.brown@healthcorp.com', 0, '2024-03-10 14:20:00', '2024-03-10 14:20:00', NULL, 0, 10);
-- Users Test Data
INSERT INTO "Users" ("id", "orgid", "username", "password", "email", "fname", "lname", "token", "TokenExpires", "type", "phone", "state", "regdate", "updatedate", "Orglogindate") VALUES
INSERT INTO "Users" ("id", "orgid", "username", "password", "email", "fname", "lname", "token", "TokenExpires", "phone", "state", "regdate", "updatedate", "Orglogindate") VALUES
-- Regular users
('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', NULL, 'john_doe', '$2b$10$dPXxS9Byg7AbB.fngFtNWel1llS1nHJlQrTO4zQToy7vVitS9mr96', 'john.doe@email.com', 'John', 'Doe', NULL, NULL, 'personal', '+1-555-1001', 1, '2024-01-20 11:00:00', '2024-01-20 11:00:00', NULL),
('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', '11111111-1111-1111-1111-111111111111', 'jane_premium', '$2b$10$dPXxS9Byg7AbB.fngFtNWel1llS1nHJlQrTO4zQToy7vVitS9mr96', 'jane.smith@email.com', 'Jane', 'Smith', NULL, NULL, 'premium', '+1-555-1002', 2, '2024-01-25 12:30:00', '2024-01-25 12:30:00', '2024-01-25 12:30:00'),
('cccccccc-cccc-cccc-cccc-cccccccccccc', '22222222-2222-2222-2222-222222222222', 'teacher_bob', '$2b$10$dPXxS9Byg7AbB.fngFtNWel1llS1nHJlQrTO4zQToy7vVitS9mr96', 'bob.teacher@eduinst.edu', 'Bob', 'Teacher', NULL, NULL, 'premium', '+1-555-1003', 2, '2024-02-05 09:15:00', '2024-02-05 09:15:00', '2024-02-05 09:15:00'),
('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', NULL, 'john_doe', '$2b$10$dPXxS9Byg7AbB.fngFtNWel1llS1nHJlQrTO4zQToy7vVitS9mr96', 'john.doe@email.com', 'John', 'Doe', NULL, NULL, '+1-555-1001', 1, '2024-01-20 11:00:00', '2024-01-20 11:00:00', NULL),
('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', '11111111-1111-1111-1111-111111111111', 'jane_premium', '$2b$10$dPXxS9Byg7AbB.fngFtNWel1llS1nHJlQrTO4zQToy7vVitS9mr96', 'jane.smith@email.com', 'Jane', 'Smith', NULL, NULL, '+1-555-1002', 2, '2024-01-25 12:30:00', '2024-01-25 12:30:00', '2024-01-25 12:30:00'),
('cccccccc-cccc-cccc-cccc-cccccccccccc', '22222222-2222-2222-2222-222222222222', 'teacher_bob', '$2b$10$dPXxS9Byg7AbB.fngFtNWel1llS1nHJlQrTO4zQToy7vVitS9mr96', 'bob.teacher@eduinst.edu', 'Bob', 'Teacher', NULL, NULL, '+1-555-1003', 2, '2024-02-05 09:15:00', '2024-02-05 09:15:00', '2024-02-05 09:15:00'),
-- Admin user
('dddddddd-dddd-dddd-dddd-dddddddddddd', NULL, 'admin_user', '$2b$10$dPXxS9Byg7AbB.fngFtNWel1llS1nHJlQrTO4zQToy7vVitS9mr96', 'admin@serpentrace.com', 'Admin', 'User', NULL, NULL, 'admin', '+1-555-9999', 5, '2024-01-01 08:00:00', '2024-01-01 08:00:00', NULL),
-- Unverified user
+46 -38
View File
@@ -1,67 +1,75 @@
import axios from 'axios';
import axios from "axios"
export const API_CONFIG = {
baseURL: import.meta.env.VITE_API_URL+'/api',
wsURL: 'http://localhost:3000',
baseURL: import.meta.env.VITE_API_URL + "/api",
wsURL: "http://localhost:3000",
timeout: 10000,
retryAttempts: 3
};
retryAttempts: 3,
}
const apiClient = axios.create({
baseURL: API_CONFIG.baseURL,
timeout: API_CONFIG.timeout,
withCredentials: true, // Important for cookie-based auth
headers: {
'Content-Type': 'application/json'
}
});
"Content-Type": "application/json",
},
})
// Add request interceptor for debugging
apiClient.interceptors.request.use(
(config) => {
console.log('Request URL:', config.url);
console.log('Request headers:', config.headers);
console.log('Current cookies:', document.cookie);
return config;
console.log("Request URL:", config.url)
console.log("Request headers:", config.headers)
console.log("Current cookies:", document.cookie)
return config
},
(error) => {
return Promise.reject(error);
return Promise.reject(error)
}
);
)
// Add response interceptor for debugging cookies
apiClient.interceptors.response.use(
(response) => {
console.log('Response status:', response.status);
console.log('Response headers:', response.headers);
console.log('Set-Cookie headers:', response.headers['set-cookie']);
console.log('Cookies after response:', document.cookie);
return response;
console.log("Response status:", response.status)
console.log("Response headers:", response.headers)
console.log("Set-Cookie headers:", response.headers["set-cookie"])
console.log("Cookies after response:", document.cookie)
return response
},
(error) => {
console.error('API Error:', error.response?.data || error.message);
return Promise.reject(error);
console.error("API Error:", error.response?.data || error.message)
return Promise.reject(error)
}
);
)
//login
export const login = async (username, password) => {
try {
const response = await apiClient.post('/users/login', { username, password });
return response.data;
} catch (error) {
throw error;
}
};
try {
const response = await apiClient.post("/users/login", { username, password })
return response.data
} catch (error) {
throw error
}
}
//register
export const register = async (username, email, password, fname, lname, phone) => {
try {
const response = await apiClient.post('/users/create', { username, email, password, fname, lname, phone });
return response.data;
} catch (error) {
throw error;
}
};
try {
const response = await apiClient.post("/users/create", { username, email, password, fname, lname, phone })
return { ...response.data, status: response.status }
} catch (error) {
throw error
}
}
//verify email
export const verifyEmail = async (token) => {
try {
const response = await apiClient.get(`/users/verify-email/${token}`)
return response.data
} catch (error) {
throw error
}
}
@@ -1,43 +1,17 @@
import React, { useEffect, useRef, useState } from "react"
import React from "react"
import { Link } from "react-router-dom"
import Logo from "../../assets/pictures/Logo"
const ArrowUpIcon = () => <span style={{ fontSize: "1.25rem" }}></span>
const Footer = () => {
const [isVisible, setIsVisible] = useState(false)
const footerRef = useRef(null)
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
setIsVisible(entry.isIntersecting)
},
{ threshold: 0.3 }
)
if (footerRef.current) {
observer.observe(footerRef.current)
}
return () => {
if (footerRef.current) {
observer.unobserve(footerRef.current)
}
}
}, [])
const scrollToTop = () => {
window.scrollTo({ top: 0, behavior: "smooth" })
}
return (
<footer
ref={footerRef}
className={`relative bg-zinc-900 text-white border-t-2 border-zinc-800 mt-auto py-8 transition-all duration-700 ease-out ${
isVisible ? "opacity-100 scale-100 translate-y-0" : "opacity-0 scale-95 translate-y-10"
}`}
className="relative bg-zinc-900 text-white border-t-2 border-zinc-800 mt-auto py-8"
style={{ transformOrigin: "bottom center" }}
>
<style>
@@ -51,10 +25,7 @@ const Footer = () => {
<div className="max-w-6xl mx-auto flex flex-wrap justify-between items-start gap-8 px-4">
{/* Logó */}
<div className="flex flex-col items-center footer-animate">
<a
href="/"
className="transition-transform duration-500 hover:scale-105 hover:brightness-125"
>
<a href="/" className="transition-transform duration-500 hover:scale-105 hover:brightness-125">
<Logo size={100} />
</a>
<span className="font-extrabold text-xl mt-2 tracking-wide">SerpentRace</span>
@@ -65,11 +36,15 @@ const Footer = () => {
<span className="text-lg font-semibold text-green-400 underline underline-offset-4 mb-2 drop-shadow-sm">
Oldalak
</span>
<a href="/" className="hover:underline hover:text-green-400 transition">Főoldal</a>
<a href="/" className="hover:underline hover:text-green-400 transition">
Főoldal
</a>
<a href="/about" className="hover:underline hover:text-green-400 transition">
Rólunk
</a>
<a href="/contact" className="hover:underline hover:text-green-400 transition">Kapcsolat</a>
<a href="/contact" className="hover:underline hover:text-green-400 transition">
Kapcsolat
</a>
</div>
{/* Közösség */}
@@ -77,8 +52,22 @@ const Footer = () => {
<span className="text-lg font-semibold text-green-400 underline underline-offset-4 mb-2 drop-shadow-sm">
Közösség
</span>
<a href="https://discord.gg/" target="_blank" rel="noopener noreferrer" className="hover:underline hover:text-green-400 transition">Discord</a>
<a href="https://github.com/" target="_blank" rel="noopener noreferrer" className="hover:underline hover:text-green-400 transition">GitHub</a>
<a
href="https://discord.gg/"
target="_blank"
rel="noopener noreferrer"
className="hover:underline hover:text-green-400 transition"
>
Discord
</a>
<a
href="https://github.com/"
target="_blank"
rel="noopener noreferrer"
className="hover:underline hover:text-green-400 transition"
>
GitHub
</a>
</div>
{/* Elérhetőség */}
@@ -96,15 +85,13 @@ const Footer = () => {
</div>
{/* Scroll to top */}
{isVisible && (
<button
onClick={scrollToTop}
className="fixed bottom-6 right-6 bg-green-500 hover:bg-green-600 text-white p-3 rounded-full shadow-lg transition transform hover:scale-110"
aria-label="Ugrás az oldal tetejére"
>
<ArrowUpIcon />
</button>
)}
<button
onClick={scrollToTop}
className="fixed bottom-6 right-6 bg-green-500 hover:bg-green-600 text-white p-3 rounded-full shadow-lg transition transform hover:scale-110"
aria-label="Ugrás az oldal tetejére"
>
<ArrowUpIcon />
</button>
</footer>
)
}
@@ -6,7 +6,13 @@ import ButtonGreen from "../Buttons/ButtonGreen.jsx"
import { FaUsers, FaPaintBrush, FaHeadset } from "react-icons/fa"
import { motion } from "framer-motion"
const LandingPage = ({ onNavigateToPlay, onNavigateToAuth }) => {
import { useNavigate } from "react-router-dom"
const LandingPage = ({ onNavigateToPlay }) => {
const navigate = useNavigate()
const handleNavigateToAuth = () => {
navigate("/register")
}
return (
<div className="w-full">
{/* Hero Section */}
@@ -56,7 +62,7 @@ const LandingPage = ({ onNavigateToPlay, onNavigateToAuth }) => {
transition={{ duration: 0.7, delay: 1 }}
>
<ButtonGreen text="Játék" onClick={onNavigateToPlay} width="w-60" />
<ButtonGreen text="Regisztráció" onClick={onNavigateToAuth} width="w-60" />
<ButtonGreen text="Regisztráció" onClick={handleNavigateToAuth} width="w-60" />
</motion.div>
</div>
</motion.section>
@@ -165,7 +171,7 @@ const LandingPage = ({ onNavigateToPlay, onNavigateToAuth }) => {
<ButtonGreen
text="Kapcsolatfelvétel"
onClick={onNavigateToAuth}
onClick={handleNavigateToAuth}
className="px-12 py-4 text-xl font-bold"
/>
</motion.div>
@@ -1,25 +1,27 @@
// 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";
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" }}
animate={{ height: isRegistering ? "750px" : "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 `}
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}/>
<Logo size={100} />
<div className="h-6" />
<Animation sizePercentage={30} />
<p className="text-lg mt-0 text-center font-light whitespace-nowrap overflow-hidden">
@@ -29,18 +31,14 @@ export default function AuthCard({ isRegistering, setIsRegistering }) {
{/* Jobb oldali űrlap */}
<div className="w-full p-10 relative">
<AnimatePresence mode="wait">
{isRegistering ? <RegisterForm /> : <LoginForm />}
</AnimatePresence>
<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!"}
{isRegistering ? "Már van fiókod? Jelentkezz be itt!" : "Nincs még fiókod? Regisztrálj itt!"}
</span>
</div>
</motion.div>
);
)
}
@@ -1,58 +1,94 @@
// 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";
import { useState, useRef, useEffect } from "react"
import Background from "../../assets/backgrounds/Background"
import { motion } from "framer-motion"
import Button from "../../components/Buttons/Button"
import { useLocation } from "react-router-dom"
export default function EmailVerification() {
const [code, setCode] = useState(Array(6).fill(""));
const inputRefs = useRef([]);
const [code, setCode] = useState(Array(6).fill(""))
const inputRefs = useRef([])
const location = useLocation()
const [showSuccess, setShowSuccess] = useState(false)
const [error, setError] = useState("")
useEffect(() => {
if (location.state && location.state.success) {
setShowSuccess(true)
setTimeout(() => setShowSuccess(false), 1500)
}
}, [location.state])
const handleChange = (e, index) => {
const { value } = e.target;
const { value } = e.target
if (/^\d*$/.test(value) && value.length <= 1) {
const newCode = [...code];
newCode[index] = value;
setCode(newCode);
const newCode = [...code]
newCode[index] = value
setCode(newCode)
if (value && index < 5) {
inputRefs.current[index + 1].focus();
inputRefs.current[index + 1].focus()
}
}
};
}
const handleKeyDown = (e, index) => {
if (e.key === "Backspace" && !code[index] && index > 0) {
inputRefs.current[index - 1].focus();
inputRefs.current[index - 1].focus()
} else if (e.key === "ArrowLeft" && index > 0) {
inputRefs.current[index - 1].focus();
inputRefs.current[index - 1].focus()
} else if (e.key === "ArrowRight" && index < 5) {
inputRefs.current[index + 1].focus();
inputRefs.current[index + 1].focus()
} else if (/^\d$/.test(e.key) && code[index]) {
e.preventDefault();
const newCode = [...code];
newCode[index] = e.key;
setCode(newCode);
e.preventDefault()
const newCode = [...code]
newCode[index] = e.key
setCode(newCode)
if (index < 5) {
setTimeout(() => {
inputRefs.current[index + 1].focus();
}, 0);
inputRefs.current[index + 1].focus()
}, 0)
}
}
};
}
const handleSubmit = (e) => {
e.preventDefault();
console.log("Kód:", code.join(""));
// Backend API
};
const handleSubmit = async (e) => {
e.preventDefault()
setError("")
const token = code.join("")
if (token.length !== 6) {
setError("A kód 6 számjegyből áll.")
return
}
try {
const res = await fetch(`/verify-email/${token}`)
const data = await res.json()
if (data.success) {
setShowSuccess(true)
setTimeout(() => setShowSuccess(false), 2000)
} else {
setError(data.message || "Sikertelen ellenőrzés.")
}
} catch (err) {
setError("Hiba történt az ellenőrzés során.")
}
}
return (
<div className="relative flex items-center justify-center min-h-screen bg-gray-100 p-0 font-poppins">
<Background />
{showSuccess && (
<div className="fixed top-6 left-1/2 -translate-x-1/2 bg-green-500 text-white px-6 py-2 rounded shadow-lg z-50 text-center font-semibold transition-opacity duration-300">
Sikeres email ellenőrzés!
</div>
)}
{error && (
<div className="fixed top-20 left-1/2 -translate-x-1/2 bg-red-500 text-white px-6 py-2 rounded shadow-lg z-50 text-center font-semibold transition-opacity duration-300">
{error}
</div>
)}
<motion.div
initial={{ height: "auto" }}
animate={{ height: "300px" }}
@@ -73,7 +109,9 @@ export default function EmailVerification() {
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'}`}
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"
@@ -85,5 +123,5 @@ export default function EmailVerification() {
</div>
</motion.div>
</div>
);
)
}
@@ -1,42 +1,52 @@
// 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";
import { login } from "../../api/userApi";
import InputBox from "../../components/Inputs/InputBox"
import Button from "../../components/Buttons/Button"
import { motion } from "framer-motion"
import { useState, useEffect } from "react"
import { useLocation } from "react-router-dom"
import { login } from "../../api/userApi"
export default function LoginForm() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const [email, setEmail] = useState("")
const [password, setPassword] = useState("")
const [error, setError] = useState("")
const location = useLocation()
const [showSuccess, setShowSuccess] = useState(false)
useEffect(() => {
if (location.state && location.state.success) {
setShowSuccess(true)
setTimeout(() => setShowSuccess(false), 4000)
}
}, [location.state])
function validateEmail(email) {
return /\S+@\S+\.\S+/.test(email);
return /\S+@\S+\.\S+/.test(email)
}
const handleSubmit = (e) => {
e.preventDefault();
setError("");
e.preventDefault()
setError("")
if (!email || !password) {
setError("Minden mező kitöltése kötelező.");
return;
setError("Minden mező kitöltése kötelező.")
return
}
if (!validateEmail(email)) {
setError("Hibás email formátum.");
return;
setError("Hibás email formátum.")
return
}
// Backend API
login(email, password)
.then((data) => {
console.log(data);
console.log("Bejelentkezés:", { email, password });
console.log(data)
console.log("Bejelentkezés:", { email, password })
})
.catch((error) => {
setError("Hibás bejelentkezési adatok.");
});
};
setError("Hibás bejelentkezési adatok.")
})
}
return (
<motion.div
@@ -47,24 +57,27 @@ export default function LoginForm() {
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>
{showSuccess && (
<div className="fixed top-6 left-1/2 -translate-x-1/2 bg-green-500 text-white px-6 py-2 rounded shadow-lg z-50 text-center font-semibold transition-opacity duration-300">
Sikeres regisztráció! Az email ellenőrzése után be tudsz lépni.
</div>
)}
{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)}
onChange={(e) => setEmail(e.target.value)}
/>
<InputBox
type="password"
placeholder="Jelszó"
value={password}
onChange={e => setPassword(e.target.value)}
onChange={(e) => setPassword(e.target.value)}
/>
<Button text="Bejelentkezés" type="submit" />
</form>
</motion.div>
);
)
}
@@ -1,51 +1,79 @@
// 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";
import { register } from "../../api/userApi";
import InputBox from "../../components/Inputs/InputBox"
import Button from "../../components/Buttons/Button"
import { motion } from "framer-motion"
import { useState } from "react"
import { register } from "../../api/userApi"
import { useNavigate } from "react-router-dom"
export default function RegisterForm() {
const [lastname, setLastname] = useState("");
const [firstname, setFirstname] = useState("");
const [username, setUsername] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [phone, setPhone] = useState("");
const [error, setError] = useState("");
const [lastname, setLastname] = useState("")
const [firstname, setFirstname] = useState("")
const [username, setUsername] = useState("")
const [email, setEmail] = useState("")
const [password, setPassword] = useState("")
const [confirmPassword, setConfirmPassword] = useState("")
const [phone, setPhone] = useState("")
const [error, setError] = useState("")
const [showErrorPopup, setShowErrorPopup] = useState(false)
const navigate = useNavigate()
function validateEmail(email) {
return /\S+@\S+\.\S+/.test(email);
return /\S+@\S+\.\S+/.test(email)
}
const handleSubmit = async (e) => {
e.preventDefault();
setError("");
e.preventDefault()
setError("")
setShowErrorPopup(false)
if (!lastname || !firstname || !username || !email || !password || !confirmPassword || !phone) {
setError("Minden mező kitöltése kötelező.");
return;
setError("Minden mező kitöltése kötelező.")
return
}
if (!validateEmail(email)) {
setError("Hibás email formátum.");
return;
setError("Hibás email formátum.")
return
}
if (password.length < 6) {
setError("A jelszónak legalább 6 karakter hosszúnak kell lennie.");
return;
setError("A jelszónak legalább 6 karakter hosszúnak kell lennie.")
return
}
if (password !== confirmPassword) {
setError("A jelszavak nem egyeznek.");
return;
setError("A jelszavak nem egyeznek.")
return
}
// Backend API
const response = await register(username, email, password, firstname, lastname, phone);
console.log(response);
console.log("Regisztráció:", { username, email, password, firstname, lastname, phone });
};
try {
const response = await register(username, email, password, firstname, lastname, phone)
// Check for 201 Created status
if (response && response.status === 201) {
navigate("/login", { state: { success: true } })
console.log(response)
console.log("Regisztráció:", { username, email, password, firstname, lastname, phone })
} else {
let msg = "Sikertelen regisztráció."
if (response && response.error) {
msg = response.error
}
setError(msg)
setShowErrorPopup(true)
setTimeout(() => setShowErrorPopup(false), 2000)
}
} catch (err) {
let msg = "Ismeretlen hiba történt."
if (err.response && err.response.data && err.response.data.error) {
msg = err.response.data.error
} else if (err.message) {
msg = err.message
}
setError(msg)
setShowErrorPopup(true)
setTimeout(() => setShowErrorPopup(false), 2000)
}
}
return (
<motion.div
@@ -56,54 +84,56 @@ export default function RegisterForm() {
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>
{showErrorPopup && error && (
<div className="fixed top-6 left-1/2 -translate-x-1/2 bg-red-500 text-white px-6 py-2 rounded shadow-lg z-50 text-center font-semibold transition-opacity duration-300">
{error}
</div>
)}
<form onSubmit={handleSubmit} className="space-y-6">
<InputBox
type="text"
placeholder="Vezetéknév"
value={lastname}
onChange={e => setLastname(e.target.value)}
onChange={(e) => setLastname(e.target.value)}
/>
<InputBox
type="text"
placeholder="Keresztnév"
value={firstname}
onChange={e => setFirstname(e.target.value)}
onChange={(e) => setFirstname(e.target.value)}
/>
<InputBox
type="text"
placeholder="Felhasználónév"
value={username}
onChange={e => setUsername(e.target.value)}
onChange={(e) => setUsername(e.target.value)}
/>
<InputBox
type="email"
placeholder="Email cím"
value={email}
onChange={e => setEmail(e.target.value)}
onChange={(e) => setEmail(e.target.value)}
/>
<InputBox
type="phone"
placeholder="Telefonszám"
value={phone}
onChange={e => setPhone(e.target.value)}
onChange={(e) => setPhone(e.target.value)}
/>
<InputBox
type="password"
placeholder="Jelszó"
value={password}
onChange={e => setPassword(e.target.value)}
onChange={(e) => setPassword(e.target.value)}
/>
<InputBox
type="password"
placeholder="Jelszó megerősítése"
value={confirmPassword}
onChange={e => setConfirmPassword(e.target.value)}
onChange={(e) => setConfirmPassword(e.target.value)}
/>
<Button text="Regisztráció" type="submit" />
</form>
</motion.div>
);
)
}
+1 -1
View File
@@ -25,7 +25,7 @@ export default function Home() {
<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]">
<main className="flex-1 flex flex-col items-center justify-start py-15 min-h-0 mt-[640px]">
<PlayMenu onJoinGame={handleJoinGame} onCreateGame={handleCreateGame} user={user} />
{/* Ide jöhetnek további szekciók, ha szeretnél még tartalmat */}
</main>