react_minta

This commit is contained in:
magdo
2026-03-18 00:01:04 +01:00
parent 28bd7661f5
commit 8b8c08be1b
15 changed files with 655 additions and 0 deletions
@@ -0,0 +1,77 @@
import { useMemo, useState } from "react";
function ContactPage() {
const [formData, setFormData] = useState({
name: "",
email: "",
message: ""
});
const [submitted, setSubmitted] = useState(false);
const errors = useMemo(() => {
const validationErrors = {};
if (!formData.name.trim()) {
validationErrors.name = "A nev megadasa kotelezo.";
}
if (!formData.email.trim()) {
validationErrors.email = "Az email cim megadasa kotelezo.";
} else if (!/^\S+@\S+\.\S+$/.test(formData.email)) {
validationErrors.email = "Adj meg ervenyes email cimet.";
}
if (formData.message.trim().length < 10) {
validationErrors.message = "Az uzenet legalabb 10 karakter legyen.";
}
return validationErrors;
}, [formData]);
const isValid = Object.keys(errors).length === 0;
function handleChange(event) {
const { name, value } = event.target;
setFormData((current) => ({ ...current, [name]: value }));
setSubmitted(false);
}
function handleSubmit(event) {
event.preventDefault();
if (!isValid) {
return;
}
setSubmitted(true);
}
return (
<section className="panel">
<h2>Kapcsolat</h2>
<p>Kontrollalt urlap peldaval, valos ideju validacioval.</p>
<form className="contact-form" onSubmit={handleSubmit} noValidate>
<label htmlFor="name">Nev</label>
<input id="name" name="name" value={formData.name} onChange={handleChange} />
{errors.name && <p className="error-message">{errors.name}</p>}
<label htmlFor="email">Email</label>
<input id="email" name="email" value={formData.email} onChange={handleChange} />
{errors.email && <p className="error-message">{errors.email}</p>}
<label htmlFor="message">Uzenet</label>
<textarea id="message" name="message" rows="5" value={formData.message} onChange={handleChange} />
{errors.message && <p className="error-message">{errors.message}</p>}
<button type="submit" disabled={!isValid}>
Kuldes
</button>
{submitted && <p className="success-message">Sikeres kuldes (demo).</p>}
</form>
</section>
);
}
export default ContactPage;
@@ -0,0 +1,22 @@
import { useState } from "react";
function HomePage() {
const [count, setCount] = useState(0);
return (
<section className="panel">
<h2>React alap mintaalkalmazas</h2>
<p>
Ez az oldal egy oktatasi frontend, ami bemutatja a komponenseket, allapotkezelest,
routingot, contextet, kontrollalt urlapot es API hivasokat.
</p>
<div className="counter">
<p>useState pelda: szamlalo erteke {count}</p>
<button onClick={() => setCount((current) => current + 1)}>Novel</button>
<button onClick={() => setCount(0)}>Nullaz</button>
</div>
</section>
);
}
export default HomePage;
@@ -0,0 +1,65 @@
import { useEffect, useState } from "react";
import { Link, useParams } from "react-router-dom";
import { fetchProductById } from "../api/productsApi";
function ProductDetailsPage() {
const { id } = useParams();
const [product, setProduct] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
useEffect(() => {
let isMounted = true;
async function loadProduct() {
try {
setLoading(true);
setError("");
const data = await fetchProductById(id);
if (isMounted) {
setProduct(data);
}
} catch (requestError) {
if (isMounted) {
setError("A termek adatainak betoltese nem sikerult.");
}
} finally {
if (isMounted) {
setLoading(false);
}
}
}
loadProduct();
return () => {
isMounted = false;
};
}, [id]);
if (loading) {
return <p>Betoltes folyamatban...</p>;
}
if (error) {
return <p className="error-message">{error}</p>;
}
if (!product) {
return <p>Nincs ilyen termek.</p>;
}
return (
<section className="panel details-page">
<Link to="/termekek">Vissza a listahoz</Link>
<h2>{product.title}</h2>
<img src={product.image} alt={product.title} />
<p>Kategoria: {product.category}</p>
<p>{product.description}</p>
<strong>{product.price.toFixed(2)} USD</strong>
</section>
);
}
export default ProductDetailsPage;
@@ -0,0 +1,79 @@
import { useEffect, useMemo, useState } from "react";
import ProductCard from "../components/ProductCard";
import { fetchProducts } from "../api/productsApi";
function ProductsPage() {
const [products, setProducts] = useState([]);
const [searchTerm, setSearchTerm] = useState("");
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
const [cartCount, setCartCount] = useState(0);
useEffect(() => {
let isMounted = true;
async function loadProducts() {
try {
setLoading(true);
setError("");
const data = await fetchProducts();
if (isMounted) {
setProducts(data);
}
} catch (requestError) {
if (isMounted) {
setError("Az adatok betoltese nem sikerult. Probald ujra kesobb.");
}
} finally {
if (isMounted) {
setLoading(false);
}
}
}
loadProducts();
return () => {
isMounted = false;
};
}, []);
const filteredProducts = useMemo(() => {
return products.filter((product) =>
product.title.toLowerCase().includes(searchTerm.trim().toLowerCase())
);
}, [products, searchTerm]);
return (
<section className="panel">
<h2>Termekek</h2>
<p>A kosarban levo termekek szama: {cartCount}</p>
<label htmlFor="search">Szures nev alapjan</label>
<input
id="search"
type="text"
value={searchTerm}
onChange={(event) => setSearchTerm(event.target.value)}
placeholder="Pl. backpack"
/>
{loading && <p>Betoltes folyamatban...</p>}
{!loading && error && <p className="error-message">{error}</p>}
{!loading && !error && filteredProducts.length === 0 && <p>Nincs talalat.</p>}
<div className="product-grid">
{filteredProducts.map((product) => (
<ProductCard
key={product.id}
product={product}
onAddToCart={() => setCartCount((current) => current + 1)}
/>
))}
</div>
</section>
);
}
export default ProductsPage;