From 5aee4f61bbcc98a6612f9669665d39a614aead77 Mon Sep 17 00:00:00 2001 From: Lheorvine Date: Fri, 30 May 2025 15:40:23 +0200 Subject: [PATCH] delete in cart --- src/.main.rs.swp | Bin 16384 -> 0 bytes src/main.rs | 74 ++++++++++++++- static/css/styles.css | 75 +++++++++++++++ static/js/cart.js | 206 +++++++++++++++++++++++++++++++----------- static/js/main.js | 1 - 5 files changed, 299 insertions(+), 57 deletions(-) delete mode 100644 src/.main.rs.swp diff --git a/src/.main.rs.swp b/src/.main.rs.swp deleted file mode 100644 index 077325381a93336c82b5836a475b83c5630b466c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 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 b5N(iKWhdx;=*udrdnRE2`1s`XT!s7#mww+; diff --git a/src/main.rs b/src/main.rs index 0543bca..a9d28a7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use actix_web::{Error, post, get, web, App, HttpResponse, HttpServer, Responder, HttpRequest}; +use actix_web::{Error, post, get, web, delete, App, HttpResponse, HttpServer, Responder, HttpRequest}; use actix_cors::Cors; use actix_files::Files; use dotenv::dotenv; @@ -366,6 +366,74 @@ async fn add_to_cart( Ok(HttpResponse::Ok().json(json!({"status": "success"}))) } +#[delete("/api/remove-from-cart/{book_id}")] +async fn remove_from_cart( + req: HttpRequest, + pool: web::Data, + book_id: web::Path, +) -> Result { + let user_id = validate_token(get_token(&req)).await?; + let book_id = book_id.into_inner(); + + sqlx::query!( + "DELETE FROM koszyk WHERE user_id = $1 AND book_id = $2", + user_id, + book_id + ) + .execute(pool.get_ref()) + .await + .map_err(|e| actix_web::error::ErrorInternalServerError(e))?; // Dodaj mapowanie błędu + + Ok(HttpResponse::Ok().json(json!({"status": "success"}))) +} + +#[post("/api/decrease-cart-item/{book_id}")] +async fn decrease_cart_item( + req: HttpRequest, + pool: web::Data, + book_id: web::Path, +) -> Result { + let user_id = validate_token(get_token(&req)).await?; + let book_id = book_id.into_inner(); + + // Sprawdź aktualną ilość + let current_quantity = sqlx::query!( + "SELECT quantity FROM koszyk WHERE user_id = $1 AND book_id = $2", + user_id, + book_id + ) + .fetch_optional(pool.get_ref()) + .await + .map_err(|e| actix_web::error::ErrorInternalServerError(e))? // Dodaj mapowanie błędu + .map(|r| r.quantity); + + if let Some(qty) = current_quantity { + if qty > 1 { + // Zmniejsz ilość o 1 + sqlx::query!( + "UPDATE koszyk SET quantity = quantity - 1 WHERE user_id = $1 AND book_id = $2", + user_id, + book_id + ) + .execute(pool.get_ref()) + .await + .map_err(|e| actix_web::error::ErrorInternalServerError(e))?; // Dodaj mapowanie błędu + } else { + // Usuń pozycję jeśli ilość = 1 + sqlx::query!( + "DELETE FROM koszyk WHERE user_id = $1 AND book_id = $2", + user_id, + book_id + ) + .execute(pool.get_ref()) + .await + .map_err(|e| actix_web::error::ErrorInternalServerError(e))?; // Dodaj mapowanie błędu + } + } + + Ok(HttpResponse::Ok().json(json!({"status": "success"}))) +} + #[get("/api/order-history")] async fn get_order_history( req: HttpRequest, @@ -500,7 +568,7 @@ async fn main() -> std::io::Result<()> { HttpServer::new(move || { let cors = Cors::default() .allow_any_origin() - .allowed_methods(vec!["GET", "POST"]) + .allowed_methods(vec!["GET", "POST", "DELETE"]) .allowed_headers(vec![ header::CONTENT_TYPE, header::AUTHORIZATION, @@ -521,6 +589,8 @@ async fn main() -> std::io::Result<()> { .service(add_to_cart) // Dodaj .service(checkout) // Dodaj .service(check_auth) + .service(remove_from_cart) + .service(decrease_cart_item) .service(get_order_history) .service( Files::new("/images", "./static/images") diff --git a/static/css/styles.css b/static/css/styles.css index b4bde74..5a77e78 100644 --- a/static/css/styles.css +++ b/static/css/styles.css @@ -270,6 +270,15 @@ footer a { opacity: 0.7; } +.remove-from-cart { + transition: all 0.3s ease; +} + +.remove-from-cart:hover { + transform: scale(1.05); + box-shadow: 0 0 8px rgba(220, 53, 69, 0.6); +} + .user-links, .anonymous-links { gap: 1rem; align-items: center; @@ -370,3 +379,69 @@ footer a { a { text-decoration: none; } + +/* Koszyk - nowe style */ +.cart-item-details { + display: flex; + flex-direction: column; +} + +.cart-item .card-body { + padding: 1.5rem; +} + +.cart-item-title { + font-size: 1.4rem; + font-weight: bold; + margin-bottom: 0.5rem; +} + +.cart-item-author { + color: #aaa; + font-style: italic; + margin-bottom: 1.5rem; +} + +.cart-item-info { + display: flex; + justify-content: space-between; + gap: 1.5rem; + margin-bottom: 1.5rem; + background-color: #222; + padding: 1.2rem; + border-radius: 8px; + border: 1px solid #333; +} + +.cart-item-info > div { + flex: 1; + min-width: 120px; +} + +.cart-item-price, +.cart-item-quantity, +.cart-item-total { + display: block; + margin-bottom: 0.5rem; + font-size: 0.95rem; + color: #ddd; +} + +.cart-remove-btn, .decrease-quantity { + padding: 0.7rem 1rem; + font-size: 0.95rem; + transition: all 0.3s ease; +} + +.cart-remove-btn:hover, .decrease-quantity:hover { + transform: translateY(-3px); + box-shadow: 0 4px 12px rgba(0,0,0,0.3); +} + +/* Suma całkowita - większy nacisk */ +.total-cart-value { + font-size: 2rem; + font-weight: bold; + color: #ffcc00; + text-shadow: 0 0 8px rgba(255, 204, 0, 0.4); +} diff --git a/static/js/cart.js b/static/js/cart.js index 432eecc..71b5192 100644 --- a/static/js/cart.js +++ b/static/js/cart.js @@ -1,47 +1,3 @@ -async function loadCart() { - try { - const response = await fetch('/api/cart', { - headers: getAuthHeaders() - }); - - if (response.status === 401) { - window.location.href = '/login.html'; - return; - } - - const items = await response.json(); - - if (items.length === 0) { - container.innerHTML = ` -
-

Twój koszyk jest pusty

- Przeglądaj książki -
- `; - return; - } - - const container = document.getElementById('cart-items'); - container.innerHTML = items.map(item => ` -
-
- -
-
${item.tytul}
-

Ilość: ${item.quantity}

-

${item.cena.toString()} PLN

-
-
-
- `).join(''); - } catch (error) { - console.error('Błąd:', error); - showError('Wystąpił błąd podczas ładowania koszyka'); - } -} - document.addEventListener('DOMContentLoaded', async () => { try { const token = localStorage.getItem('token'); @@ -57,24 +13,74 @@ document.addEventListener('DOMContentLoaded', async () => { }); if (!response.ok) throw new Error('Błąd ładowania koszyka'); - + const cartItems = await response.json(); const container = document.getElementById('cart-items'); container.innerHTML = ''; + if (cartItems.length === 0) { + container.innerHTML = '

Twój koszyk jest pusty

'; + return; + } + + let totalCartValue = 0; + cartItems.forEach(item => { + // Formatowanie cen + const price = parseFloat(item.cena); + const formattedPrice = price.toFixed(2); + const itemTotal = price * item.quantity; + const formattedTotal = itemTotal.toFixed(2); + totalCartValue += itemTotal; + const itemHTML = ` -
-
+
+
-
- ${item.tytul} +
+ ${item.tytul}
-
-
-
${item.tytul}
-

Ilość: ${item.quantity}

-

Cena: ${item.cena} PLN

+
+
+
+
${item.tytul}
+

${item.autor}

+
+ +
+
+ Cena jednostkowa + ${formattedPrice} PLN +
+ +
+ Ilość +
+ ${item.quantity} +
+
+ +
+ Suma + ${formattedTotal} PLN +
+
+ +
+
+ + +
+
@@ -84,12 +90,104 @@ document.addEventListener('DOMContentLoaded', async () => { container.insertAdjacentHTML('beforeend', itemHTML); }); + // Dodaj całkowitą sumę koszyka + const totalHTML = ` +
+
+
+

Suma całkowita

+

${totalCartValue.toFixed(2)} PLN

+
+
+
+ `; + container.insertAdjacentHTML('beforeend', totalHTML); + + // Obsługa przycisku zmniejszania ilości + document.querySelectorAll('.decrease-quantity').forEach(button => { + button.addEventListener('click', function() { + const bookId = this.getAttribute('data-book-id'); + updateCartItemQuantity(bookId, 'decrease'); + }); + }); + + // Obsługa przycisku usuwania + document.querySelectorAll('.remove-from-cart').forEach(button => { + button.addEventListener('click', function() { + const bookId = this.getAttribute('data-book-id'); + updateCartItemQuantity(bookId, 'remove'); + }); + }); + } catch (error) { console.error('Error:', error); alert('Nie udało się załadować koszyka'); } }); +async function updateCartItemQuantity(bookId, action) { + try { + const token = localStorage.getItem('token'); + if (!token) { + window.location.href = '/login.html'; + return; + } + + let endpoint, method; + + if (action === 'decrease') { + endpoint = `/api/decrease-cart-item/${bookId}`; + method = 'POST'; + } else if (action === 'remove') { + endpoint = `/api/remove-from-cart/${bookId}`; + method = 'DELETE'; + } else { + throw new Error('Nieznana akcja'); + } + + const response = await fetch(endpoint, { + method: method, + headers: { + 'Authorization': `Bearer ${token}` + } + }); + + if (!response.ok) throw new Error('Błąd aktualizacji koszyka'); + + // Odśwież koszyk + location.reload(); + } catch (error) { + console.error('Error:', error); + alert('Nie udało się zaktualizować koszyka'); + } +} + +async function removeFromCart(bookId) { + try { + const token = localStorage.getItem('token'); + if (!token) { + window.location.href = '/login.html'; + return; + } + + const response = await fetch(`/api/remove-from-cart/${bookId}`, { + method: 'DELETE', + headers: { + 'Authorization': `Bearer ${token}` + } + }); + + if (!response.ok) throw new Error('Błąd usuwania z koszyka'); + + // Odśwież koszyk + location.reload(); + } catch (error) { + console.error('Error:', error); + alert('Nie udało się usunąć z koszyka'); + } +} + + document.getElementById('checkoutBtn').addEventListener('click', async () => { try { const token = localStorage.getItem('token'); diff --git a/static/js/main.js b/static/js/main.js index f35403d..a6a5bc3 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -5,7 +5,6 @@ const sortSelect = document.getElementById('sortSelect'); if (!booksContainer || !searchInput || !sortSelect) return; - // Funkcje specyficzne dla strony głównej const createBookCard = (book) => `