From 0ed804f7ef87f75cf8bc0fafa68383859ae76828 Mon Sep 17 00:00:00 2001 From: miki549 <szucs549@gmail.com> Date: Fri, 2 May 2025 17:21:45 +0200 Subject: [PATCH] =?UTF-8?q?m=C5=B1k=C3=B6dik:=20login=20register=20randel?= =?UTF-8?q?=C3=A9seim=20men=C3=BCpontban=20megjelennek=20a=20leadottak=20m?= =?UTF-8?q?egjelennek=20a=20term=C3=A9kek=20k=C3=A9pei?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/build.gradle | 2 + .../java/kisbe32/backend/AuthController.java | 30 ++++++- .../java/kisbe32/backend/LoginRequest.java | 33 ++++++++ .../src/main/java/kisbe32/backend/Order.java | 12 ++- .../main/java/kisbe32/backend/OrderItem.java | 18 +++++ .../kisbe32/backend/OrderItemRepository.java | 2 +- .../java/kisbe32/backend/OrderRepository.java | 2 +- .../src/main/java/kisbe32/backend/User.java | 5 ++ .../main/java/kisbe32/backend/WebConfig.java | 6 +- frontend/Dockerfile | 2 + frontend/src/App.vue | 18 ++++- frontend/src/components/Cart.vue | 56 +++++++------ frontend/src/components/Login.vue | 32 ++++---- frontend/src/components/MainContent.vue | 46 ++++++++--- frontend/src/components/Orders.vue | 78 +++++++++++++++---- frontend/src/components/Register.vue | 51 ++++++++---- frontend/src/config/api.js | 2 +- frontend/src/stores/cartStore.js | 33 +++++++- frontend/vite.config.js | 2 +- 19 files changed, 340 insertions(+), 90 deletions(-) diff --git a/backend/build.gradle b/backend/build.gradle index deebb25..d2f1cc4 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -22,6 +22,8 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-data-rest' implementation 'org.springframework.boot:spring-boot-starter-actuator' + implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' + implementation 'com.fasterxml.jackson.core:jackson-databind' runtimeOnly 'org.postgresql:postgresql' testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' diff --git a/backend/src/main/java/kisbe32/backend/AuthController.java b/backend/src/main/java/kisbe32/backend/AuthController.java index e396a83..54bc076 100644 --- a/backend/src/main/java/kisbe32/backend/AuthController.java +++ b/backend/src/main/java/kisbe32/backend/AuthController.java @@ -15,16 +15,40 @@ public class AuthController { @Autowired private UserRepository userRepository; - @PostMapping("/login") + @PostMapping("/api/login") public ResponseEntity<LoginResponse> login(@RequestBody LoginRequest request) { User user = userRepository.findByUsername(request.getUsername()); // Simple string comparison for password if (user != null && user.getPassword().equals(request.getPassword())) { - return ResponseEntity.ok(new LoginResponse(true, user.getId(), user.getUsername())); + return ResponseEntity.ok(new LoginResponse(true, user.getId(), user.getUsername(), user.getEmail(), user.getRole())); } else { return ResponseEntity.status(HttpStatus.UNAUTHORIZED) - .body(new LoginResponse(false, null, null)); + .body(new LoginResponse(false, null, null, null, null)); } } + + @PostMapping("/api/register") + public ResponseEntity<LoginResponse> register(@RequestBody LoginRequest request) { + // Check if username already exists + User existingUser = userRepository.findByUsername(request.getUsername()); + if (existingUser != null) { + return ResponseEntity.status(HttpStatus.CONFLICT) + .body(new LoginResponse(false, null, null, null, null)); + } + + // Create new user + User newUser = new User(); + newUser.setUsername(request.getUsername()); + newUser.setPassword(request.getPassword()); + newUser.setEmail(request.getEmail()); + newUser.setRole("USER"); + + // Save user to database + User savedUser = userRepository.save(newUser); + + // Return success response with user details + return ResponseEntity.status(HttpStatus.CREATED) + .body(new LoginResponse(true, savedUser.getId(), savedUser.getUsername(), savedUser.getEmail(), savedUser.getRole())); + } } \ No newline at end of file diff --git a/backend/src/main/java/kisbe32/backend/LoginRequest.java b/backend/src/main/java/kisbe32/backend/LoginRequest.java index 26aa511..dc236b4 100644 --- a/backend/src/main/java/kisbe32/backend/LoginRequest.java +++ b/backend/src/main/java/kisbe32/backend/LoginRequest.java @@ -3,6 +3,7 @@ package kisbe32.backend; class LoginRequest { private String username; private String password; + private String email; // Default constructor needed for JSON deserialization public LoginRequest() {} @@ -11,24 +12,50 @@ class LoginRequest { this.username = username; this.password = password; } + + public LoginRequest(String username, String email, String password) { + this.username = username; + this.email = email; + this.password = password; + } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } + + public String getEmail() { return email; } + public void setEmail(String email) { this.email = email; } } class LoginResponse { private boolean success; private Integer userId; private String username; + private String email; + private String role; public LoginResponse(boolean success, Integer userId, String username) { this.success = success; this.userId = userId; this.username = username; } + + public LoginResponse(boolean success, Integer userId, String username, String role) { + this.success = success; + this.userId = userId; + this.username = username; + this.role = role; + } + + public LoginResponse(boolean success, Integer userId, String username, String email, String role) { + this.success = success; + this.userId = userId; + this.username = username; + this.email = email; + this.role = role; + } public boolean isSuccess() { return success; } public void setSuccess(boolean success) { this.success = success; } @@ -38,4 +65,10 @@ class LoginResponse { public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } + + public String getEmail() { return email; } + public void setEmail(String email) { this.email = email; } + + public String getRole() { return role; } + public void setRole(String role) { this.role = role; } } \ No newline at end of file diff --git a/backend/src/main/java/kisbe32/backend/Order.java b/backend/src/main/java/kisbe32/backend/Order.java index f3ca63a..1e5b2f6 100644 --- a/backend/src/main/java/kisbe32/backend/Order.java +++ b/backend/src/main/java/kisbe32/backend/Order.java @@ -4,6 +4,7 @@ import jakarta.persistence.*; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.List; +import com.fasterxml.jackson.annotation.*; @Entity @Table(name = "orders") @@ -15,6 +16,8 @@ public class Order { @ManyToOne @JoinColumn(name = "user_id", nullable = false) + @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") + @JsonIdentityReference(alwaysAsId = true) private User user; @Column(name = "order_date", nullable = false) @@ -26,7 +29,7 @@ public class Order { @Column(name = "total_price", nullable = false) private BigDecimal totalPrice; - @OneToMany(mappedBy = "order", cascade = CascadeType.ALL) + @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, fetch = FetchType.EAGER) private List<OrderItem> items; protected Order() {} @@ -43,6 +46,13 @@ public class Order { public User getUser() { return user; } public LocalDateTime getOrderDate() { return orderDate; } public String getStatus() { return status; } + + @JsonProperty("totalAmount") public BigDecimal getTotalPrice() { return totalPrice; } + public List<OrderItem> getItems() { return items; } + + // Setters + public void setOrderDate(LocalDateTime orderDate) { this.orderDate = orderDate; } + public void setItems(List<OrderItem> items) { this.items = items; } } \ No newline at end of file diff --git a/backend/src/main/java/kisbe32/backend/OrderItem.java b/backend/src/main/java/kisbe32/backend/OrderItem.java index bf6260a..a84b82d 100644 --- a/backend/src/main/java/kisbe32/backend/OrderItem.java +++ b/backend/src/main/java/kisbe32/backend/OrderItem.java @@ -3,6 +3,7 @@ package kisbe32.backend; import jakarta.persistence.*; import java.math.BigDecimal; import java.time.LocalDateTime; +import com.fasterxml.jackson.annotation.*; @Entity @Table(name = "orderitems") @@ -14,10 +15,13 @@ public class OrderItem { @ManyToOne @JoinColumn(name = "order_id", nullable = false) + @JsonBackReference private Order order; @ManyToOne @JoinColumn(name = "product_id", nullable = false) + @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") + @JsonIdentityReference(alwaysAsId = true) private Product product; @Column(name = "quantity", nullable = false) @@ -39,12 +43,26 @@ public class OrderItem { this.product = product; this.quantity = quantity; this.price = price; + this.createdAt = LocalDateTime.now(); + this.updatedAt = LocalDateTime.now(); } // Getters public Integer getId() { return id; } public Order getOrder() { return order; } + public Product getProduct() { return product; } + + @JsonProperty("productId") + public Integer getProductId() { + return product != null ? product.getId() : null; + } + public Integer getQuantity() { return quantity; } public BigDecimal getPrice() { return price; } + public LocalDateTime getCreatedAt() { return createdAt; } + public LocalDateTime getUpdatedAt() { return updatedAt; } + + // Setters + public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; } } \ No newline at end of file diff --git a/backend/src/main/java/kisbe32/backend/OrderItemRepository.java b/backend/src/main/java/kisbe32/backend/OrderItemRepository.java index 1447349..087a503 100644 --- a/backend/src/main/java/kisbe32/backend/OrderItemRepository.java +++ b/backend/src/main/java/kisbe32/backend/OrderItemRepository.java @@ -12,5 +12,5 @@ interface OrderItemRepository extends JpaRepository<OrderItem, Integer> { List<OrderItem> findByOrderId(@Param("orderId") Integer orderId); // Find order items by product ID - List<OrderItem> findByProductId(@Param("productId") Integer productId); + List<OrderItem> findByProduct_Id(@Param("productId") Integer productId); } diff --git a/backend/src/main/java/kisbe32/backend/OrderRepository.java b/backend/src/main/java/kisbe32/backend/OrderRepository.java index 3ed2633..2420475 100644 --- a/backend/src/main/java/kisbe32/backend/OrderRepository.java +++ b/backend/src/main/java/kisbe32/backend/OrderRepository.java @@ -9,7 +9,7 @@ import java.util.List; @RepositoryRestResource(collectionResourceRel = "orders", path = "orders") interface OrderRepository extends JpaRepository<Order, Integer> { // Find orders by user ID - List<Order> findByUserId(@Param("userId") Integer userId); + List<Order> findByUser_Id(@Param("userId") Integer userId); // Find orders by status List<Order> findByStatus(@Param("status") String status); diff --git a/backend/src/main/java/kisbe32/backend/User.java b/backend/src/main/java/kisbe32/backend/User.java index 5d0139f..e4f6ffe 100644 --- a/backend/src/main/java/kisbe32/backend/User.java +++ b/backend/src/main/java/kisbe32/backend/User.java @@ -15,6 +15,7 @@ public class User { private String username; private String email; private String password; + private String role; // Default constructor required by JPA public User() {} @@ -23,6 +24,7 @@ public class User { this.username = username; this.email = email; this.password = password; + this.role = "USER"; // Default role } // Getters and setters @@ -37,4 +39,7 @@ public class User { public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } + + public String getRole() { return role; } + public void setRole(String role) { this.role = role; } } \ No newline at end of file diff --git a/backend/src/main/java/kisbe32/backend/WebConfig.java b/backend/src/main/java/kisbe32/backend/WebConfig.java index 826898c..77d5b97 100644 --- a/backend/src/main/java/kisbe32/backend/WebConfig.java +++ b/backend/src/main/java/kisbe32/backend/WebConfig.java @@ -9,7 +9,9 @@ public class WebConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") // Allow all endpoints - .allowedOrigins("*") - .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS"); + .allowedOrigins("http://localhost", "http://localhost:80") + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") + .allowedHeaders("*") + .allowCredentials(true); } } \ No newline at end of file diff --git a/frontend/Dockerfile b/frontend/Dockerfile index c448e2f..19ae6e9 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -21,6 +21,8 @@ RUN echo 'server { \ proxy_pass http://backend:8080/api/; \ proxy_set_header Host $host; \ proxy_set_header X-Real-IP $remote_addr; \ + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; \ + proxy_set_header X-Forwarded-Proto $scheme; \ } \ }' > /etc/nginx/conf.d/default.conf EXPOSE 80 diff --git a/frontend/src/App.vue b/frontend/src/App.vue index acbf44c..5f41346 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -4,17 +4,31 @@ import Register from './components/Register.vue' import Cart from './components/Cart.vue' import Orders from './components/Orders.vue' - import { ref } from 'vue' + import { ref, onMounted } from 'vue' const currentPage = ref('main') + // Frissítés trigger + const authStateUpdate = ref(0) + function navigate(view){ currentPage.value = view + // Frissítjük az auth állapotot navigáció után + authStateUpdate.value++ } + + // Figyeljük a localStorage változásait + function handleStorageChange() { + authStateUpdate.value++ + } + + onMounted(() => { + window.addEventListener('storage', handleStorageChange) + }) </script> <template> <main> - <MainContent v-if="currentPage === 'main'" @navigate="navigate" /> + <MainContent v-if="currentPage === 'main'" @navigate="navigate" :key="authStateUpdate" /> <Login v-else-if="currentPage === 'login'" @navigate="navigate" /> <Register v-else-if="currentPage === 'register'" @navigate="navigate" /> <Cart v-else-if="currentPage === 'cart'" @navigate="navigate" /> diff --git a/frontend/src/components/Cart.vue b/frontend/src/components/Cart.vue index 27b4443..25885af 100644 --- a/frontend/src/components/Cart.vue +++ b/frontend/src/components/Cart.vue @@ -45,39 +45,51 @@ async function checkout() { error.value = null; successMessage.value = null; - const token = localStorage.getItem('authToken'); + // Felhasználó ellenőrzése localStorage-ból + const userStr = localStorage.getItem('user'); - if (!token) { + if (!userStr) { error.value = "Bejelentkezés szükséges a vásárláshoz!"; emit('navigate', 'login'); return; } + // Felhasználó adatok kinyerése + const user = JSON.parse(userStr); + + // Adatok előkészítése - csak primitív értékek használata, nem reaktív objektumok + const simpleItems = cartItems.value.map(item => ({ + productId: item.id, + quantity: item.quantity, + price: Number(item.price) + })); + + // Összérték számítása + const totalAmountValue = simpleItems.reduce((sum, item) => sum + (item.price * item.quantity), 0); + + // Rendelés adatok előkészítése const orderData = { - items: cartItems.value.map(item => ({ - productId: item.id, - quantity: item.quantity - })) + userId: Number(user.id), + status: "confirmed", + orderDate: new Date().toISOString(), + totalAmount: totalAmountValue, + items: simpleItems }; - const response = await axios.post(`${API_BASE_URL}/orders`, orderData, { - headers: { - Authorization: `Bearer ${token}` - } - }); - - if (response.data.success) { - successMessage.value = "Sikeres vásárlás! Köszönjük rendelését."; - // Clear the cart after successful purchase - cartItems.value.forEach(item => { - cartStore.removeFromCart(item.id); - }); - } else { - error.value = response.data.message || "Hiba történt a vásárlás során!"; - } + console.log("Rendelési adatok:", JSON.stringify(orderData)); + + // Rendelés küldése a backend API-nak + const response = await axios.post(`${API_BASE_URL}/orders`, orderData); + + // Sikeres rendelés + successMessage.value = "Sikeres vásárlás! Köszönjük rendelését."; + + // Kosár ürítése sikeres rendelés után + cartStore.clearCart(); + } catch (err) { console.error('Checkout error:', err); - error.value = err.response?.data?.message || "Hiba történt a vásárlás során! Kérjük próbálja újra később."; + error.value = "Hiba történt a vásárlás során! Kérjük próbálja újra később."; } finally { isLoading.value = false; } diff --git a/frontend/src/components/Login.vue b/frontend/src/components/Login.vue index b99afe7..2895050 100644 --- a/frontend/src/components/Login.vue +++ b/frontend/src/components/Login.vue @@ -20,26 +20,28 @@ async function onLogin() { password: password.value }); - // Handle successful login - if (response.data.success) { - // Store the token if your API returns one - if (response.data.token) { - localStorage.setItem('authToken', response.data.token); - } - - // Store user info if needed - if (response.data.user) { - localStorage.setItem('user', JSON.stringify(response.data.user)); - } - + // Store user info in local storage + if (response.data) { + // Create user object from response + const user = { + id: response.data.userId, + username: response.data.username, + role: response.data.role || 'USER' + }; + + // Store in localStorage + localStorage.setItem('user', JSON.stringify(user)); + + // Triggereljük a localStorage változás eseményt, + // hogy más komponensek is értesüljenek róla + window.dispatchEvent(new Event('storage')); + // Navigate back to main page emit('navigate', 'main'); - } else { - error.value = response.data.message || 'Hibás felhasználónév vagy jelszó'; } } catch (err) { console.error('Login error:', err); - error.value = err.response?.data?.message || 'Hiba történt a bejelentkezés során'; + error.value = 'Hibás felhasználónév vagy jelszó'; } finally { isLoading.value = false; } diff --git a/frontend/src/components/MainContent.vue b/frontend/src/components/MainContent.vue index ac91181..860f287 100644 --- a/frontend/src/components/MainContent.vue +++ b/frontend/src/components/MainContent.vue @@ -35,13 +35,14 @@ const checkAuthStatus = () => { const token = localStorage.getItem('authToken'); const userStr = localStorage.getItem('user'); - if (token && userStr) { + if (userStr) { isLoggedIn.value = true; try { currentUser.value = JSON.parse(userStr); } catch (e) { console.error('User data parsing error:', e); currentUser.value = null; + isLoggedIn.value = false; } } else { isLoggedIn.value = false; @@ -54,6 +55,9 @@ const logout = () => { localStorage.removeItem('authToken'); localStorage.removeItem('user'); checkAuthStatus(); + + // Triggereljük a localStorage változás eseményt is + window.dispatchEvent(new Event('storage')); }; onMounted(() => { @@ -99,14 +103,38 @@ const fetchProducts = async () => { const response = await axios.get(`${API_BASE_URL}/products`); // Az adatbázisból érkező adatok formázása - products.value = response.data.map(product => ({ - id: product.id, - name: product.name, - category: (product.category || '').toLowerCase(), - price: product.price, - stock: product.stock, - image: product.image_url || 'https://via.placeholder.com/600x600' - })); + // Check if the data has _embedded.products structure (HAL format) + const productsData = response.data._embedded?.products || []; + + // Egyszerű placeholder kép data URL formátumban + const placeholderImage = 'data:image/png;base64,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'; + + products.value = productsData.map(product => { + let imageUrl = product.imageUrl || product.image_url; + + // Ellenőrizzük, hogy létezik-e a kép URL + if (!imageUrl) { + imageUrl = placeholderImage; + } + // Ellenőrizzük, hogy a kép URL tartalmaz-e http vagy https protokollt + else if (!imageUrl.startsWith('http://') && !imageUrl.startsWith('https://') && !imageUrl.startsWith('data:')) { + // Relatív URL esetén egészítsük ki + if (imageUrl.startsWith('/')) { + imageUrl = `${API_BASE_URL}${imageUrl}`; + } else { + imageUrl = `${API_BASE_URL}/${imageUrl}`; + } + } + + return { + id: product.id, + name: product.name, + category: (product.category || '').toLowerCase(), + price: product.price, + stock: product.stock, + image: imageUrl + }; + }); } catch (err) { console.error('Hiba a termékek betöltése során:', err); diff --git a/frontend/src/components/Orders.vue b/frontend/src/components/Orders.vue index 8413367..b73ff6f 100644 --- a/frontend/src/components/Orders.vue +++ b/frontend/src/components/Orders.vue @@ -17,24 +17,72 @@ async function fetchOrders() { isLoading.value = true error.value = null - const token = localStorage.getItem('authToken') - - if (!token) { + // Felhasználó adatok ellenőrzése localStorage-ban + const userStr = localStorage.getItem('user') + + if (!userStr) { error.value = 'Bejelentkezés szükséges a rendelések megtekintéséhez' return } - const response = await axios.get(`${API_BASE_URL}/orders`, { - headers: { - Authorization: `Bearer ${token}` + // Felhasználó adatok JSON-ból + const user = JSON.parse(userStr) + console.log("Felhasználói adatok:", user) + + // Az adott felhasználó rendeléseinek lekérdezése a megfelelő végpontról + const apiUrl = `${API_BASE_URL}/orders/user/${user.id}` + console.log("API kérés URL:", apiUrl) + + try { + const response = await axios.get(apiUrl) + console.log('Rendelés válasz:', response.data) + + // Ha üres a válasz, üres tömböt állítunk be + if (!response.data || response.data.length === 0) { + console.log("Nincsenek rendelések") + orders.value = [] + return } - }) - - if (response.data.success) { - orders.value = response.data.orders - } else { - error.value = 'Nem sikerült betölteni a rendeléseket' + + if (Array.isArray(response.data)) { + console.log("Tömb válasz feldolgozása") + orders.value = response.data.map(order => ({ + id: order.id, + order_date: order.orderDate, + status: order.status || 'confirmed', + total_amount: order.totalAmount || order.totalPrice || 0, + OrderItems: Array.isArray(order.items) ? order.items : [] + })) + } else if (response.data._embedded && response.data._embedded.orders) { + console.log("Spring Data REST válasz feldolgozása") + orders.value = response.data._embedded.orders.map(order => ({ + id: order.id, + order_date: order.orderDate, + status: order.status || 'confirmed', + total_amount: order.totalAmount || order.totalPrice || 0, + OrderItems: Array.isArray(order.items) ? order.items : [] + })) + } else { + // Próbáljuk egyetlen objektumként kezelni + console.log("Egyedi objektum válasz feldolgozása") + if (response.data.id) { + orders.value = [{ + id: response.data.id, + order_date: response.data.orderDate, + status: response.data.status || 'confirmed', + total_amount: response.data.totalAmount || response.data.totalPrice || 0, + OrderItems: Array.isArray(response.data.items) ? response.data.items : [] + }] + } else { + orders.value = [] + } + } + } catch (error) { + console.error("API hívási hiba:", error) + throw error } + + console.log('Feldolgozott rendelések:', orders.value) } catch (err) { console.error('Rendelések betöltési hiba:', err) error.value = 'Hiba történt a rendelések betöltése során' @@ -131,12 +179,12 @@ onMounted(fetchOrders) </div> <div class="order-items" v-if="order.OrderItems && order.OrderItems.length"> - <div v-for="item in order.OrderItems" :key="item.id" class="order-item"> + <div v-for="(item, index) in order.OrderItems" :key="index" class="order-item"> <div class="item-quantity">{{ item.quantity }}×</div> <div class="item-name"> - {{ item.product ? item.product.name : `Termék #${item.productId}` }} + {{ item.product && item.product.name ? item.product.name : `Termék #${item.productId || (item.product ? item.product.id : 'ismeretlen')}` }} </div> - <div class="item-price">{{ item.price * item.quantity }} Ft</div> + <div class="item-price">{{ (item.price || 0) * (item.quantity || 1) }} Ft</div> </div> </div> diff --git a/frontend/src/components/Register.vue b/frontend/src/components/Register.vue index 76f08d8..9032d15 100644 --- a/frontend/src/components/Register.vue +++ b/frontend/src/components/Register.vue @@ -6,6 +6,7 @@ import {API_BASE_URL} from "@/config/api.js"; const emit = defineEmits(['navigate']) const username = ref('') +const email = ref('') const password = ref('') const confirmPassword = ref('') const error = ref('') @@ -19,34 +20,49 @@ async function onRegister() { return } + // Email validáció + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailRegex.test(email.value)) { + error.value = 'Kérjük, adjon meg egy érvényes email címet!' + return + } + error.value = '' isLoading.value = true const response = await axios.post(`${API_BASE_URL}/register`, { username: username.value, + email: email.value, password: password.value }); - // Handle successful registration - if (response.data.success) { - // Store the token if your API returns one - if (response.data.token) { - localStorage.setItem('authToken', response.data.token); - } - - // Store user info if needed - if (response.data.user) { - localStorage.setItem('user', JSON.stringify(response.data.user)); - } - + // Store user info in local storage + if (response.data) { + // Create user object from response + const user = { + id: response.data.userId, + username: response.data.username, + email: response.data.email, + role: response.data.role || 'USER' + }; + + // Store in localStorage + localStorage.setItem('user', JSON.stringify(user)); + + // Triggereljük a localStorage változás eseményt, + // hogy más komponensek is értesüljenek róla + window.dispatchEvent(new Event('storage')); + // Navigate back to main page emit('navigate', 'main'); - } else { - error.value = response.data.message || 'Hiba történt a regisztráció során'; } } catch (err) { console.error('Registration error:', err); - error.value = err.response?.data?.message || 'Hiba történt a regisztráció során'; + if (err.response && err.response.status === 409) { + error.value = 'Ez a felhasználónév vagy email cím már foglalt!'; + } else { + error.value = 'Hiba történt a regisztráció során. Kérjük, próbálja újra!'; + } } finally { isLoading.value = false; } @@ -81,6 +97,11 @@ function onBack() { <input type="text" id="username" v-model="username" required /> </div> + <div class="form-group"> + <label for="email">Email cím</label> + <input type="email" id="email" v-model="email" required /> + </div> + <div class="form-group"> <label for="password">Jelszó</label> <input type="password" id="password" v-model="password" required /> diff --git a/frontend/src/config/api.js b/frontend/src/config/api.js index b7e6565..969c830 100644 --- a/frontend/src/config/api.js +++ b/frontend/src/config/api.js @@ -1 +1 @@ -export const API_BASE_URL = 'http://localhost:8080/api'; \ No newline at end of file +export const API_BASE_URL = '/api'; \ No newline at end of file diff --git a/frontend/src/stores/cartStore.js b/frontend/src/stores/cartStore.js index 3d5d339..38ce0e5 100644 --- a/frontend/src/stores/cartStore.js +++ b/frontend/src/stores/cartStore.js @@ -1,10 +1,31 @@ // src/stores/cartStore.js import { ref, computed, watch } from 'vue' +import { API_BASE_URL } from '@/config/api.js' // Create a reactive cart const cartItems = ref([]) const isCartLoaded = ref(false) +// Helper function to format image URL +function formatImageUrl(imageUrl) { + if (!imageUrl) { + return null; + } + + // Ha már tartalmaz protokollt (http, https, data), akkor nem változtatunk rajta + if (imageUrl.startsWith('http://') || imageUrl.startsWith('https://') || imageUrl.startsWith('data:')) { + return imageUrl; + } + + // Relatív URL esetén kiegészítjük + if (imageUrl.startsWith('/')) { + return `${API_BASE_URL}${imageUrl}`; + } + + // Egyéb esetben + return `${API_BASE_URL}/${imageUrl}`; +} + // Load cart from localStorage function loadCart() { if (isCartLoaded.value) return @@ -60,7 +81,7 @@ function addToCart(product, quantity = 1) { id: product.id, name: product.name, price: product.price, - image: product.image, + image: formatImageUrl(product.image), quantity: availableStock, stock: product.stock // Store stock info for later checks }); @@ -81,7 +102,7 @@ function addToCart(product, quantity = 1) { id: product.id, name: product.name, price: product.price, - image: product.image, + image: formatImageUrl(product.image), quantity: quantity, stock: product.stock // Store stock info for later checks }); @@ -103,6 +124,13 @@ function updateQuantity(productId, quantity) { } } +// Kosár teljes törlése +function clearCart() { + loadCart() + cartItems.value = [] + localStorage.removeItem('cart') +} + // Computed properties const totalItems = computed(() => { loadCart() @@ -119,6 +147,7 @@ export default { addToCart, removeFromCart, updateQuantity, + clearCart, totalItems, totalPrice, loadCart diff --git a/frontend/vite.config.js b/frontend/vite.config.js index 4eafdca..118ab15 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -18,7 +18,7 @@ export default defineConfig({ server: { proxy: { '/api': { - target: 'http://localhost:8080', + target: 'http://backend:8080', changeOrigin: true } } -- GitLab