6 Commits

Author SHA1 Message Date
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
12 changed files with 246 additions and 160 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. * * Copyright (c) Meta Platforms, Inc. and affiliates.
+2 -1
View File
@@ -1,5 +1,6 @@
#!/usr/bin/env node #!/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. * Copyright (c) Meta Platforms, Inc. and affiliates.
@@ -6,6 +6,5 @@ export interface CreateUserCommand {
lname: string; lname: string;
code?: string; code?: string;
orgid?: string; orgid?: string;
type: string;
phone?: string; phone?: string;
} }
@@ -7,7 +7,6 @@ export interface UpdateUserCommand {
fname?: string; fname?: string;
lname?: string; lname?: string;
code?: string; code?: string;
type?: string;
phone?: string; phone?: string;
state?: number; state?: number;
} }
+1 -1
View File
@@ -83,7 +83,7 @@ services:
POSTGRES_INITDB_ARGS: "--encoding=UTF-8" POSTGRES_INITDB_ARGS: "--encoding=UTF-8"
volumes: volumes:
- postgres_dev_data:/var/lib/postgresql/data - 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: networks:
- serpentrace-network - serpentrace-network
healthcheck: healthcheck:
+1 -1
View File
@@ -116,7 +116,7 @@ services:
POSTGRES_INITDB_ARGS: "--encoding=UTF-8" POSTGRES_INITDB_ARGS: "--encoding=UTF-8"
volumes: volumes:
- postgres_dev_data:/var/lib/postgresql/data - 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: networks:
- serpentrace-network - serpentrace-network
healthcheck: healthcheck:
@@ -48,7 +48,6 @@ CREATE TABLE "Users" (
"lname" character varying(100) NOT NULL, "lname" character varying(100) NOT NULL,
"token" character varying(255), "token" character varying(255),
"TokenExpires" TIMESTAMP, "TokenExpires" TIMESTAMP,
"type" character varying(50) NOT NULL,
"phone" character varying(20), "phone" character varying(20),
"state" integer NOT NULL DEFAULT 0, "state" integer NOT NULL DEFAULT 0,
"regdate" TIMESTAMP NOT NULL DEFAULT now(), "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); ('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 -- 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 -- 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), ('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, 'premium', '+1-555-1002', 2, '2024-01-25 12:30:00', '2024-01-25 12:30:00', '2024-01-25 12:30:00'), ('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, 'premium', '+1-555-1003', 2, '2024-02-05 09:15:00', '2024-02-05 09:15:00', '2024-02-05 09:15: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 -- 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), ('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 -- Unverified user
+40 -32
View File
@@ -1,67 +1,75 @@
import axios from 'axios'; import axios from "axios"
export const API_CONFIG = { export const API_CONFIG = {
baseURL: import.meta.env.VITE_API_URL+'/api', baseURL: import.meta.env.VITE_API_URL + "/api",
wsURL: 'http://localhost:3000', wsURL: "http://localhost:3000",
timeout: 10000, timeout: 10000,
retryAttempts: 3 retryAttempts: 3,
}; }
const apiClient = axios.create({ const apiClient = axios.create({
baseURL: API_CONFIG.baseURL, baseURL: API_CONFIG.baseURL,
timeout: API_CONFIG.timeout, timeout: API_CONFIG.timeout,
withCredentials: true, // Important for cookie-based auth withCredentials: true, // Important for cookie-based auth
headers: { headers: {
'Content-Type': 'application/json' "Content-Type": "application/json",
} },
}); })
// Add request interceptor for debugging // Add request interceptor for debugging
apiClient.interceptors.request.use( apiClient.interceptors.request.use(
(config) => { (config) => {
console.log('Request URL:', config.url); console.log("Request URL:", config.url)
console.log('Request headers:', config.headers); console.log("Request headers:", config.headers)
console.log('Current cookies:', document.cookie); console.log("Current cookies:", document.cookie)
return config; return config
}, },
(error) => { (error) => {
return Promise.reject(error); return Promise.reject(error)
} }
); )
// Add response interceptor for debugging cookies // Add response interceptor for debugging cookies
apiClient.interceptors.response.use( apiClient.interceptors.response.use(
(response) => { (response) => {
console.log('Response status:', response.status); console.log("Response status:", response.status)
console.log('Response headers:', response.headers); console.log("Response headers:", response.headers)
console.log('Set-Cookie headers:', response.headers['set-cookie']); console.log("Set-Cookie headers:", response.headers["set-cookie"])
console.log('Cookies after response:', document.cookie); console.log("Cookies after response:", document.cookie)
return response; return response
}, },
(error) => { (error) => {
console.error('API Error:', error.response?.data || error.message); console.error("API Error:", error.response?.data || error.message)
return Promise.reject(error); return Promise.reject(error)
} }
); )
//login //login
export const login = async (username, password) => { export const login = async (username, password) => {
try { try {
const response = await apiClient.post('/users/login', { username, password }); const response = await apiClient.post("/users/login", { username, password })
return response.data; return response.data
} catch (error) { } catch (error) {
throw error; throw error
}
} }
};
//register //register
export const register = async (username, email, password, fname, lname, phone) => { export const register = async (username, email, password, fname, lname, phone) => {
try { try {
const response = await apiClient.post('/users/create', { username, email, password, fname, lname, phone }); const response = await apiClient.post("/users/create", { username, email, password, fname, lname, phone })
return response.data; return { ...response.data, status: response.status }
} catch (error) { } catch (error) {
throw 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,23 +1,25 @@
// src/pages/Auth/AuthLogin.jsx // src/pages/Auth/AuthLogin.jsx
// Kártya amelyiken a bejelentkezés és regisztráció van // Kártya amelyiken a bejelentkezés és regisztráció van
import { motion, AnimatePresence } from "framer-motion"; import { motion, AnimatePresence } from "framer-motion"
import Animation from "../../assets/SerpentRace_Animation/SerpentRace_Animation"; import Animation from "../../assets/SerpentRace_Animation/SerpentRace_Animation"
import LoginForm from "./LoginForm"; import LoginForm from "./LoginForm"
import RegisterForm from "./RegisterForm"; import RegisterForm from "./RegisterForm"
import Logo from "../../assets/pictures/Logo"; import Logo from "../../assets/pictures/Logo"
export default function AuthCard({ isRegistering, setIsRegistering }) { export default function AuthCard({ isRegistering, setIsRegistering }) {
return ( return (
<motion.div <motion.div
initial={{ height: "auto" }} initial={{ height: "auto" }}
animate={{ height: isRegistering ? "600px" : "385px" }} animate={{ height: isRegistering ? "750px" : "385px" }}
transition={{ duration: 0.5, ease: "easeInOut" }} transition={{ duration: 0.5, ease: "easeInOut" }}
className="absolute flex max-w-4xl w-full bg-white rounded-2xl shadow-lg overflow-hidden" className="absolute flex max-w-4xl w-full bg-white rounded-2xl shadow-lg overflow-hidden"
> >
{/* Bal oldali kép és szöveg */} {/* Bal oldali kép és szöveg */}
<div <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" /> <div className="h-6" />
@@ -29,18 +31,14 @@ export default function AuthCard({ isRegistering, setIsRegistering }) {
{/* Jobb oldali űrlap */} {/* Jobb oldali űrlap */}
<div className="w-full p-10 relative"> <div className="w-full p-10 relative">
<AnimatePresence mode="wait"> <AnimatePresence mode="wait">{isRegistering ? <RegisterForm /> : <LoginForm />}</AnimatePresence>
{isRegistering ? <RegisterForm /> : <LoginForm />}
</AnimatePresence>
<span <span
className="text-secondary cursor-pointer hover:underline mt-4 block text-center" className="text-secondary cursor-pointer hover:underline mt-4 block text-center"
onClick={() => setIsRegistering(!isRegistering)} onClick={() => setIsRegistering(!isRegistering)}
> >
{isRegistering {isRegistering ? "Már van fiókod? Jelentkezz be itt!" : "Nincs még fiókod? Regisztrálj itt!"}
? "Már van fiókod? Jelentkezz be itt!"
: "Nincs még fiókod? Regisztrálj itt!"}
</span> </span>
</div> </div>
</motion.div> </motion.div>
); )
} }
@@ -1,58 +1,94 @@
// src/pages/Auth/EmailVerification.jsx // src/pages/Auth/EmailVerification.jsx
// Rublikák a kód beírásához, email ellenőrzéshez // Rublikák a kód beírásához, email ellenőrzéshez
import { useState, useRef } from "react"; import { useState, useRef, useEffect } from "react"
import Background from "../../assets/backgrounds/Background"; import Background from "../../assets/backgrounds/Background"
import { motion } from "framer-motion"; import { motion } from "framer-motion"
import Button from "../../components/Buttons/Button"; import Button from "../../components/Buttons/Button"
import { useLocation } from "react-router-dom"
export default function EmailVerification() { export default function EmailVerification() {
const [code, setCode] = useState(Array(6).fill("")); const [code, setCode] = useState(Array(6).fill(""))
const inputRefs = useRef([]); 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 handleChange = (e, index) => {
const { value } = e.target; const { value } = e.target
if (/^\d*$/.test(value) && value.length <= 1) { if (/^\d*$/.test(value) && value.length <= 1) {
const newCode = [...code]; const newCode = [...code]
newCode[index] = value; newCode[index] = value
setCode(newCode); setCode(newCode)
if (value && index < 5) { if (value && index < 5) {
inputRefs.current[index + 1].focus(); inputRefs.current[index + 1].focus()
}
} }
} }
};
const handleKeyDown = (e, index) => { const handleKeyDown = (e, index) => {
if (e.key === "Backspace" && !code[index] && index > 0) { 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) { } else if (e.key === "ArrowLeft" && index > 0) {
inputRefs.current[index - 1].focus(); inputRefs.current[index - 1].focus()
} else if (e.key === "ArrowRight" && index < 5) { } 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]) { } else if (/^\d$/.test(e.key) && code[index]) {
e.preventDefault(); e.preventDefault()
const newCode = [...code]; const newCode = [...code]
newCode[index] = e.key; newCode[index] = e.key
setCode(newCode); setCode(newCode)
if (index < 5) { if (index < 5) {
setTimeout(() => { setTimeout(() => {
inputRefs.current[index + 1].focus(); inputRefs.current[index + 1].focus()
}, 0); }, 0)
}
} }
} }
};
const handleSubmit = (e) => { const handleSubmit = async (e) => {
e.preventDefault(); e.preventDefault()
console.log("Kód:", code.join("")); setError("")
// Backend API 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 ( return (
<div className="relative flex items-center justify-center min-h-screen bg-gray-100 p-0 font-poppins"> <div className="relative flex items-center justify-center min-h-screen bg-gray-100 p-0 font-poppins">
<Background /> <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 <motion.div
initial={{ height: "auto" }} initial={{ height: "auto" }}
animate={{ height: "300px" }} animate={{ height: "300px" }}
@@ -73,7 +109,9 @@ export default function EmailVerification() {
onChange={(e) => handleChange(e, index)} onChange={(e) => handleChange(e, index)}
onKeyDown={(e) => handleKeyDown(e, index)} onKeyDown={(e) => handleKeyDown(e, index)}
ref={(el) => (inputRefs.current[index] = el)} 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 // nem tudom, hogy hogyan jobb
// placeholder="_" // placeholder="_"
maxLength="1" maxLength="1"
@@ -85,5 +123,5 @@ export default function EmailVerification() {
</div> </div>
</motion.div> </motion.div>
</div> </div>
); )
} }
@@ -1,42 +1,52 @@
// src/pages/Auth/LoginForm.jsx // src/pages/Auth/LoginForm.jsx
// Bejelentkezési űrlap // Bejelentkezési űrlap
import InputBox from "../../components/Inputs/InputBox"; import InputBox from "../../components/Inputs/InputBox"
import Button from "../../components/Buttons/Button"; import Button from "../../components/Buttons/Button"
import { motion } from "framer-motion"; import { motion } from "framer-motion"
import { useState } from "react"; import { useState, useEffect } from "react"
import { login } from "../../api/userApi"; import { useLocation } from "react-router-dom"
import { login } from "../../api/userApi"
export default function LoginForm() { export default function LoginForm() {
const [email, setEmail] = useState(""); const [email, setEmail] = useState("")
const [password, setPassword] = useState(""); const [password, setPassword] = useState("")
const [error, setError] = 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) { function validateEmail(email) {
return /\S+@\S+\.\S+/.test(email); return /\S+@\S+\.\S+/.test(email)
} }
const handleSubmit = (e) => { const handleSubmit = (e) => {
e.preventDefault(); e.preventDefault()
setError(""); setError("")
if (!email || !password) { if (!email || !password) {
setError("Minden mező kitöltése kötelező."); setError("Minden mező kitöltése kötelező.")
return; return
} }
if (!validateEmail(email)) { if (!validateEmail(email)) {
setError("Hibás email formátum."); setError("Hibás email formátum.")
return; return
} }
// Backend API // Backend API
login(email, password) login(email, password)
.then((data) => { .then((data) => {
console.log(data); console.log(data)
console.log("Bejelentkezés:", { email, password }); console.log("Bejelentkezés:", { email, password })
}) })
.catch((error) => { .catch((error) => {
setError("Hibás bejelentkezési adatok."); setError("Hibás bejelentkezési adatok.")
}); })
}; }
return ( return (
<motion.div <motion.div
@@ -47,24 +57,27 @@ export default function LoginForm() {
transition={{ duration: 0.25 }} transition={{ duration: 0.25 }}
> >
<h2 className="text-4xl font-extrabold text-center mb-6 text-gray-800 tracking-wide">Bejelentkezés</h2> <h2 className="text-4xl font-extrabold text-center mb-6 text-gray-800 tracking-wide">Bejelentkezés</h2>
{error && ( {showSuccess && (
<div className="mb-4 text-red-600 text-center font-semibold">{error}</div> <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"> <form onSubmit={handleSubmit} className="space-y-6">
<InputBox <InputBox
type="email" type="email"
placeholder="Email cím" placeholder="Email cím"
value={email} value={email}
onChange={e => setEmail(e.target.value)} onChange={(e) => setEmail(e.target.value)}
/> />
<InputBox <InputBox
type="password" type="password"
placeholder="Jelszó" placeholder="Jelszó"
value={password} value={password}
onChange={e => setPassword(e.target.value)} onChange={(e) => setPassword(e.target.value)}
/> />
<Button text="Bejelentkezés" type="submit" /> <Button text="Bejelentkezés" type="submit" />
</form> </form>
</motion.div> </motion.div>
); )
} }
@@ -1,51 +1,79 @@
// src/pages/Auth/RegisterForm.jsx // src/pages/Auth/RegisterForm.jsx
// Regisztrációs űrlap // Regisztrációs űrlap
import InputBox from "../../components/Inputs/InputBox"; import InputBox from "../../components/Inputs/InputBox"
import Button from "../../components/Buttons/Button"; import Button from "../../components/Buttons/Button"
import { motion } from "framer-motion"; import { motion } from "framer-motion"
import { useState } from "react"; import { useState } from "react"
import { register } from "../../api/userApi"; import { register } from "../../api/userApi"
import { useNavigate } from "react-router-dom"
export default function RegisterForm() { export default function RegisterForm() {
const [lastname, setLastname] = useState(""); const [lastname, setLastname] = useState("")
const [firstname, setFirstname] = useState(""); const [firstname, setFirstname] = useState("")
const [username, setUsername] = useState(""); const [username, setUsername] = useState("")
const [email, setEmail] = useState(""); const [email, setEmail] = useState("")
const [password, setPassword] = useState(""); const [password, setPassword] = useState("")
const [confirmPassword, setConfirmPassword] = useState(""); const [confirmPassword, setConfirmPassword] = useState("")
const [phone, setPhone] = useState(""); const [phone, setPhone] = useState("")
const [error, setError] = useState(""); const [error, setError] = useState("")
const [showErrorPopup, setShowErrorPopup] = useState(false)
const navigate = useNavigate()
function validateEmail(email) { function validateEmail(email) {
return /\S+@\S+\.\S+/.test(email); return /\S+@\S+\.\S+/.test(email)
} }
const handleSubmit = async (e) => { const handleSubmit = async (e) => {
e.preventDefault(); e.preventDefault()
setError(""); setError("")
setShowErrorPopup(false)
if (!lastname || !firstname || !username || !email || !password || !confirmPassword || !phone) { if (!lastname || !firstname || !username || !email || !password || !confirmPassword || !phone) {
setError("Minden mező kitöltése kötelező."); setError("Minden mező kitöltése kötelező.")
return; return
} }
if (!validateEmail(email)) { if (!validateEmail(email)) {
setError("Hibás email formátum."); setError("Hibás email formátum.")
return; return
} }
if (password.length < 6) { if (password.length < 6) {
setError("A jelszónak legalább 6 karakter hosszúnak kell lennie."); setError("A jelszónak legalább 6 karakter hosszúnak kell lennie.")
return; return
} }
if (password !== confirmPassword) { if (password !== confirmPassword) {
setError("A jelszavak nem egyeznek."); setError("A jelszavak nem egyeznek.")
return; return
} }
// Backend API // Backend API
const response = await register(username, email, password, firstname, lastname, phone); try {
console.log(response); const response = await register(username, email, password, firstname, lastname, phone)
console.log("Regisztráció:", { 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 ( return (
<motion.div <motion.div
@@ -56,54 +84,56 @@ export default function RegisterForm() {
transition={{ duration: 0.25 }} transition={{ duration: 0.25 }}
> >
<h2 className="text-4xl font-extrabold text-center mb-6 text-gray-800 tracking-wide">Regisztráció</h2> <h2 className="text-4xl font-extrabold text-center mb-6 text-gray-800 tracking-wide">Regisztráció</h2>
{error && ( {showErrorPopup && error && (
<div className="mb-4 text-red-600 text-center font-semibold">{error}</div> <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"> <form onSubmit={handleSubmit} className="space-y-6">
<InputBox <InputBox
type="text" type="text"
placeholder="Vezetéknév" placeholder="Vezetéknév"
value={lastname} value={lastname}
onChange={e => setLastname(e.target.value)} onChange={(e) => setLastname(e.target.value)}
/> />
<InputBox <InputBox
type="text" type="text"
placeholder="Keresztnév" placeholder="Keresztnév"
value={firstname} value={firstname}
onChange={e => setFirstname(e.target.value)} onChange={(e) => setFirstname(e.target.value)}
/> />
<InputBox <InputBox
type="text" type="text"
placeholder="Felhasználónév" placeholder="Felhasználónév"
value={username} value={username}
onChange={e => setUsername(e.target.value)} onChange={(e) => setUsername(e.target.value)}
/> />
<InputBox <InputBox
type="email" type="email"
placeholder="Email cím" placeholder="Email cím"
value={email} value={email}
onChange={e => setEmail(e.target.value)} onChange={(e) => setEmail(e.target.value)}
/> />
<InputBox <InputBox
type="phone" type="phone"
placeholder="Telefonszám" placeholder="Telefonszám"
value={phone} value={phone}
onChange={e => setPhone(e.target.value)} onChange={(e) => setPhone(e.target.value)}
/> />
<InputBox <InputBox
type="password" type="password"
placeholder="Jelszó" placeholder="Jelszó"
value={password} value={password}
onChange={e => setPassword(e.target.value)} onChange={(e) => setPassword(e.target.value)}
/> />
<InputBox <InputBox
type="password" type="password"
placeholder="Jelszó megerősítése" placeholder="Jelszó megerősítése"
value={confirmPassword} value={confirmPassword}
onChange={e => setConfirmPassword(e.target.value)} onChange={(e) => setConfirmPassword(e.target.value)}
/> />
<Button text="Regisztráció" type="submit" /> <Button text="Regisztráció" type="submit" />
</form> </form>
</motion.div> </motion.div>
); )
} }