diff --git a/Frontend/react_minta/README.md b/Frontend/react_minta/README.md
new file mode 100644
index 0000000..092f163
--- /dev/null
+++ b/Frontend/react_minta/README.md
@@ -0,0 +1,30 @@
+# React frontend minta
+
+Ez a mini projekt a Frontend PPT React temai alapjan keszult, es bemutatja:
+
+- komponens alapu felepitest
+- allapotkezelest useState hookkal
+- adatbetoltest useEffect + Axios segitsegevel
+- kliensoldali routingot React Routerrel
+- Context API hasznalatat tema valtassal
+- kontrollalt urlapot valos ideju validacioval
+
+## Inditas
+
+```bash
+npm install
+npm run dev
+```
+
+## Build ellenorzes
+
+```bash
+npm run build
+```
+
+## Fo oldalak
+
+- `/` fooldal, useState szamlalo
+- `/termekek` lista API-bol, szures, loading/error/allapotok
+- `/termekek/:id` dinamikus route reszletoldal
+- `/kapcsolat` kontrollalt form validacioval
diff --git a/Frontend/react_minta/index.html b/Frontend/react_minta/index.html
new file mode 100644
index 0000000..86ec0c7
--- /dev/null
+++ b/Frontend/react_minta/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ React Frontend Minta
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Frontend/react_minta/package.json b/Frontend/react_minta/package.json
new file mode 100644
index 0000000..a2b0296
--- /dev/null
+++ b/Frontend/react_minta/package.json
@@ -0,0 +1,21 @@
+{
+ "name": "react-minta-frontend",
+ "private": true,
+ "version": "1.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "axios": "^1.9.0",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "react-router-dom": "^6.30.1"
+ },
+ "devDependencies": {
+ "@vitejs/plugin-react": "^4.5.0",
+ "vite": "^6.3.5"
+ }
+}
\ No newline at end of file
diff --git a/Frontend/react_minta/src/App.jsx b/Frontend/react_minta/src/App.jsx
new file mode 100644
index 0000000..bd31568
--- /dev/null
+++ b/Frontend/react_minta/src/App.jsx
@@ -0,0 +1,22 @@
+import { Navigate, Route, Routes } from "react-router-dom";
+import Layout from "./components/Layout";
+import HomePage from "./pages/HomePage";
+import ProductsPage from "./pages/ProductsPage";
+import ProductDetailsPage from "./pages/ProductDetailsPage";
+import ContactPage from "./pages/ContactPage";
+
+function App() {
+ return (
+
+
+ } />
+ } />
+ } />
+ } />
+ } />
+
+
+ );
+}
+
+export default App;
diff --git a/Frontend/react_minta/src/api/productsApi.js b/Frontend/react_minta/src/api/productsApi.js
new file mode 100644
index 0000000..2a33e37
--- /dev/null
+++ b/Frontend/react_minta/src/api/productsApi.js
@@ -0,0 +1,16 @@
+import axios from "axios";
+
+const api = axios.create({
+ baseURL: "https://fakestoreapi.com",
+ timeout: 10000
+});
+
+export async function fetchProducts() {
+ const response = await api.get("/products");
+ return response.data;
+}
+
+export async function fetchProductById(id) {
+ const response = await api.get(`/products/${id}`);
+ return response.data;
+}
diff --git a/Frontend/react_minta/src/components/Layout.jsx b/Frontend/react_minta/src/components/Layout.jsx
new file mode 100644
index 0000000..cd29280
--- /dev/null
+++ b/Frontend/react_minta/src/components/Layout.jsx
@@ -0,0 +1,37 @@
+import { NavLink } from "react-router-dom";
+import { useTheme } from "../context/ThemeContext";
+
+const navItems = [
+ { to: "/", label: "Fooldal" },
+ { to: "/termekek", label: "Termekek" },
+ { to: "/kapcsolat", label: "Kapcsolat" }
+];
+
+function Layout({ children }) {
+ const { theme, toggleTheme } = useTheme();
+
+ return (
+
+
+ React Frontend Minta
+
+
+
+ {children}
+
+ );
+}
+
+export default Layout;
diff --git a/Frontend/react_minta/src/components/ProductCard.jsx b/Frontend/react_minta/src/components/ProductCard.jsx
new file mode 100644
index 0000000..9dd8e0f
--- /dev/null
+++ b/Frontend/react_minta/src/components/ProductCard.jsx
@@ -0,0 +1,18 @@
+import { Link } from "react-router-dom";
+
+function ProductCard({ product, onAddToCart }) {
+ return (
+
+
+ {product.title}
+ {product.category}
+ {product.price.toFixed(2)} USD
+
+
+ Reszletek
+
+
+ );
+}
+
+export default ProductCard;
diff --git a/Frontend/react_minta/src/context/ThemeContext.jsx b/Frontend/react_minta/src/context/ThemeContext.jsx
new file mode 100644
index 0000000..e25cd11
--- /dev/null
+++ b/Frontend/react_minta/src/context/ThemeContext.jsx
@@ -0,0 +1,24 @@
+import { createContext, useContext, useMemo, useState } from "react";
+
+const ThemeContext = createContext({
+ theme: "light",
+ toggleTheme: () => {}
+});
+
+export function ThemeProvider({ children }) {
+ const [theme, setTheme] = useState("light");
+
+ const value = useMemo(
+ () => ({
+ theme,
+ toggleTheme: () => setTheme((current) => (current === "light" ? "dark" : "light"))
+ }),
+ [theme]
+ );
+
+ return {children};
+}
+
+export function useTheme() {
+ return useContext(ThemeContext);
+}
diff --git a/Frontend/react_minta/src/main.jsx b/Frontend/react_minta/src/main.jsx
new file mode 100644
index 0000000..db03ec5
--- /dev/null
+++ b/Frontend/react_minta/src/main.jsx
@@ -0,0 +1,16 @@
+import React from "react";
+import ReactDOM from "react-dom/client";
+import { BrowserRouter } from "react-router-dom";
+import App from "./App";
+import { ThemeProvider } from "./context/ThemeContext";
+import "./styles.css";
+
+ReactDOM.createRoot(document.getElementById("root")).render(
+
+
+
+
+
+
+
+);
diff --git a/Frontend/react_minta/src/pages/ContactPage.jsx b/Frontend/react_minta/src/pages/ContactPage.jsx
new file mode 100644
index 0000000..51d92e5
--- /dev/null
+++ b/Frontend/react_minta/src/pages/ContactPage.jsx
@@ -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 (
+
+ Kapcsolat
+ Kontrollalt urlap peldaval, valos ideju validacioval.
+
+
+
+ );
+}
+
+export default ContactPage;
diff --git a/Frontend/react_minta/src/pages/HomePage.jsx b/Frontend/react_minta/src/pages/HomePage.jsx
new file mode 100644
index 0000000..2f7baca
--- /dev/null
+++ b/Frontend/react_minta/src/pages/HomePage.jsx
@@ -0,0 +1,22 @@
+import { useState } from "react";
+
+function HomePage() {
+ const [count, setCount] = useState(0);
+
+ return (
+
+ React alap mintaalkalmazas
+
+ Ez az oldal egy oktatasi frontend, ami bemutatja a komponenseket, allapotkezelest,
+ routingot, contextet, kontrollalt urlapot es API hivasokat.
+
+
+
useState pelda: szamlalo erteke {count}
+
+
+
+
+ );
+}
+
+export default HomePage;
diff --git a/Frontend/react_minta/src/pages/ProductDetailsPage.jsx b/Frontend/react_minta/src/pages/ProductDetailsPage.jsx
new file mode 100644
index 0000000..3452da2
--- /dev/null
+++ b/Frontend/react_minta/src/pages/ProductDetailsPage.jsx
@@ -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 Betoltes folyamatban...
;
+ }
+
+ if (error) {
+ return {error}
;
+ }
+
+ if (!product) {
+ return Nincs ilyen termek.
;
+ }
+
+ return (
+
+ Vissza a listahoz
+ {product.title}
+
+ Kategoria: {product.category}
+ {product.description}
+ {product.price.toFixed(2)} USD
+
+ );
+}
+
+export default ProductDetailsPage;
diff --git a/Frontend/react_minta/src/pages/ProductsPage.jsx b/Frontend/react_minta/src/pages/ProductsPage.jsx
new file mode 100644
index 0000000..f23f78d
--- /dev/null
+++ b/Frontend/react_minta/src/pages/ProductsPage.jsx
@@ -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 (
+
+ Termekek
+ A kosarban levo termekek szama: {cartCount}
+
+
+ setSearchTerm(event.target.value)}
+ placeholder="Pl. backpack"
+ />
+
+ {loading && Betoltes folyamatban...
}
+ {!loading && error && {error}
}
+ {!loading && !error && filteredProducts.length === 0 && Nincs talalat.
}
+
+
+ {filteredProducts.map((product) => (
+
setCartCount((current) => current + 1)}
+ />
+ ))}
+
+
+ );
+}
+
+export default ProductsPage;
diff --git a/Frontend/react_minta/src/styles.css b/Frontend/react_minta/src/styles.css
new file mode 100644
index 0000000..97acb92
--- /dev/null
+++ b/Frontend/react_minta/src/styles.css
@@ -0,0 +1,210 @@
+:root {
+ font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
+ color-scheme: light;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ min-height: 100vh;
+ background: linear-gradient(120deg, #f4f7ff, #eef3f6);
+ color: #1f2937;
+}
+
+.app-shell {
+ min-height: 100vh;
+}
+
+.topbar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 1rem;
+ padding: 1rem 1.5rem;
+ border-bottom: 1px solid #d6dce5;
+ background: rgba(255, 255, 255, 0.9);
+ backdrop-filter: blur(6px);
+ position: sticky;
+ top: 0;
+}
+
+.topbar h1 {
+ margin: 0;
+ font-size: 1.25rem;
+}
+
+.topbar nav {
+ display: flex;
+ gap: 0.75rem;
+}
+
+.nav-link {
+ text-decoration: none;
+ color: #1e3a8a;
+ font-weight: 600;
+}
+
+.nav-link.active {
+ color: #ef4444;
+}
+
+.theme-button {
+ border: 1px solid #93c5fd;
+ background: #dbeafe;
+ color: #1e3a8a;
+ border-radius: 8px;
+ padding: 0.5rem 0.75rem;
+ cursor: pointer;
+}
+
+.page-content {
+ max-width: 1080px;
+ margin: 0 auto;
+ padding: 1.25rem;
+}
+
+.panel {
+ border: 1px solid #d6dce5;
+ border-radius: 12px;
+ background: #ffffff;
+ padding: 1rem;
+}
+
+.counter {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 0.75rem;
+}
+
+.counter button,
+.card-actions button,
+.contact-form button {
+ border: none;
+ border-radius: 8px;
+ padding: 0.55rem 0.8rem;
+ cursor: pointer;
+ background: #0ea5e9;
+ color: #ffffff;
+ font-weight: 600;
+}
+
+input,
+textarea {
+ width: 100%;
+ padding: 0.65rem;
+ border-radius: 8px;
+ border: 1px solid #cbd5e1;
+}
+
+.product-grid {
+ margin-top: 1rem;
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
+ gap: 1rem;
+}
+
+.product-card {
+ border: 1px solid #dbe5ef;
+ border-radius: 10px;
+ background: #f8fbff;
+ padding: 0.8rem;
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+}
+
+.product-card img {
+ width: 100%;
+ height: 180px;
+ object-fit: contain;
+ background: #ffffff;
+ border-radius: 8px;
+}
+
+.product-card h3 {
+ margin: 0;
+ font-size: 1rem;
+}
+
+.price {
+ margin: 0;
+ font-weight: 700;
+}
+
+.category {
+ margin: 0;
+ color: #64748b;
+}
+
+.card-actions {
+ margin-top: auto;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.details-page img {
+ width: min(280px, 100%);
+ display: block;
+ margin: 0.75rem 0;
+}
+
+.contact-form {
+ display: grid;
+ gap: 0.55rem;
+}
+
+.error-message {
+ margin: 0;
+ color: #b91c1c;
+}
+
+.success-message {
+ margin: 0;
+ color: #15803d;
+ font-weight: 700;
+}
+
+.app-shell.dark {
+ background: #0f172a;
+ color: #e2e8f0;
+}
+
+.app-shell.dark .topbar {
+ background: rgba(15, 23, 42, 0.95);
+ border-bottom-color: #334155;
+}
+
+.app-shell.dark .panel {
+ background: #1e293b;
+ border-color: #334155;
+}
+
+.app-shell.dark .product-card {
+ background: #1f2937;
+ border-color: #334155;
+}
+
+.app-shell.dark .nav-link {
+ color: #93c5fd;
+}
+
+.app-shell.dark .nav-link.active {
+ color: #fb7185;
+}
+
+@media (max-width: 760px) {
+ .topbar {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .topbar nav {
+ width: 100%;
+ justify-content: space-between;
+ }
+}
diff --git a/Frontend/react_minta/vite.config.js b/Frontend/react_minta/vite.config.js
new file mode 100644
index 0000000..8a7fe35
--- /dev/null
+++ b/Frontend/react_minta/vite.config.js
@@ -0,0 +1,6 @@
+import { defineConfig } from "vite";
+import react from "@vitejs/plugin-react";
+
+export default defineConfig({
+ plugins: [react()]
+});
\ No newline at end of file