react_minta
This commit is contained in:
@@ -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;
|
||||
Reference in New Issue
Block a user