delete in cart
This commit is contained in:
parent
a13a13b296
commit
5aee4f61bb
5 changed files with 299 additions and 57 deletions
BIN
src/.main.rs.swp
BIN
src/.main.rs.swp
Binary file not shown.
74
src/main.rs
74
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_cors::Cors;
|
||||||
use actix_files::Files;
|
use actix_files::Files;
|
||||||
use dotenv::dotenv;
|
use dotenv::dotenv;
|
||||||
|
@ -366,6 +366,74 @@ async fn add_to_cart(
|
||||||
Ok(HttpResponse::Ok().json(json!({"status": "success"})))
|
Ok(HttpResponse::Ok().json(json!({"status": "success"})))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[delete("/api/remove-from-cart/{book_id}")]
|
||||||
|
async fn remove_from_cart(
|
||||||
|
req: HttpRequest,
|
||||||
|
pool: web::Data<sqlx::PgPool>,
|
||||||
|
book_id: web::Path<i32>,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
|
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<sqlx::PgPool>,
|
||||||
|
book_id: web::Path<i32>,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
|
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")]
|
#[get("/api/order-history")]
|
||||||
async fn get_order_history(
|
async fn get_order_history(
|
||||||
req: HttpRequest,
|
req: HttpRequest,
|
||||||
|
@ -500,7 +568,7 @@ async fn main() -> std::io::Result<()> {
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
let cors = Cors::default()
|
let cors = Cors::default()
|
||||||
.allow_any_origin()
|
.allow_any_origin()
|
||||||
.allowed_methods(vec!["GET", "POST"])
|
.allowed_methods(vec!["GET", "POST", "DELETE"])
|
||||||
.allowed_headers(vec![
|
.allowed_headers(vec![
|
||||||
header::CONTENT_TYPE,
|
header::CONTENT_TYPE,
|
||||||
header::AUTHORIZATION,
|
header::AUTHORIZATION,
|
||||||
|
@ -521,6 +589,8 @@ async fn main() -> std::io::Result<()> {
|
||||||
.service(add_to_cart) // Dodaj
|
.service(add_to_cart) // Dodaj
|
||||||
.service(checkout) // Dodaj
|
.service(checkout) // Dodaj
|
||||||
.service(check_auth)
|
.service(check_auth)
|
||||||
|
.service(remove_from_cart)
|
||||||
|
.service(decrease_cart_item)
|
||||||
.service(get_order_history)
|
.service(get_order_history)
|
||||||
.service(
|
.service(
|
||||||
Files::new("/images", "./static/images")
|
Files::new("/images", "./static/images")
|
||||||
|
|
|
@ -270,6 +270,15 @@ footer a {
|
||||||
opacity: 0.7;
|
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 {
|
.user-links, .anonymous-links {
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -370,3 +379,69 @@ footer a {
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -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 = `
|
|
||||||
<div class="col-12 text-center">
|
|
||||||
<p class="text-muted">Twój koszyk jest pusty</p>
|
|
||||||
<a href="/" class="btn btn-gothic">Przeglądaj książki</a>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const container = document.getElementById('cart-items');
|
|
||||||
container.innerHTML = items.map(item => `
|
|
||||||
<div class="col-md-6 col-lg-4">
|
|
||||||
<div class="card dark-card h-100">
|
|
||||||
<img src="${item.obraz_url}"
|
|
||||||
class="card-img-top"
|
|
||||||
style="height: 200px; object-fit: cover;">
|
|
||||||
<div class="card-body">
|
|
||||||
<h5 class="card-title">${item.tytul}</h5>
|
|
||||||
<p class="card-text">Ilość: ${item.quantity}</p>
|
|
||||||
<p class="text-danger">${item.cena.toString()} PLN</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`).join('');
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Błąd:', error);
|
|
||||||
showError('Wystąpił błąd podczas ładowania koszyka');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
try {
|
try {
|
||||||
const token = localStorage.getItem('token');
|
const token = localStorage.getItem('token');
|
||||||
|
@ -57,24 +13,74 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) throw new Error('Błąd ładowania koszyka');
|
if (!response.ok) throw new Error('Błąd ładowania koszyka');
|
||||||
|
|
||||||
const cartItems = await response.json();
|
const cartItems = await response.json();
|
||||||
const container = document.getElementById('cart-items');
|
const container = document.getElementById('cart-items');
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
|
|
||||||
|
if (cartItems.length === 0) {
|
||||||
|
container.innerHTML = '<p class="text-center">Twój koszyk jest pusty</p>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let totalCartValue = 0;
|
||||||
|
|
||||||
cartItems.forEach(item => {
|
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 = `
|
const itemHTML = `
|
||||||
<div class="col-md-6">
|
<div class="col-12 mb-4 cart-item">
|
||||||
<div class="card dark-card mb-3">
|
<div class="card dark-card">
|
||||||
<div class="row g-0">
|
<div class="row g-0">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4 text-center p-3 cart-item-image">
|
||||||
<img src="${item.obraz_url}" class="img-fluid rounded-start" alt="${item.tytul}">
|
<img src="${item.obraz_url}"
|
||||||
|
class="img-fluid rounded"
|
||||||
|
alt="${item.tytul}"
|
||||||
|
style="max-height: 200px;">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-8">
|
<div class="col-md-8 cart-item-details">
|
||||||
<div class="card-body">
|
<div class="card-body h-100 d-flex flex-column">
|
||||||
<h5 class="card-title">${item.tytul}</h5>
|
<div>
|
||||||
<p class="card-text">Ilość: ${item.quantity}</p>
|
<h5 class="card-title cart-item-title">${item.tytul}</h5>
|
||||||
<p class="card-text">Cena: ${item.cena} PLN</p>
|
<p class="card-text cart-item-author">${item.autor}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex flex-wrap gap-3 mb-2 cart-item-info mt-auto">
|
||||||
|
<div class="d-flex flex-column flex-grow-1">
|
||||||
|
<span class="fw-bold cart-item-price">Cena jednostkowa</span>
|
||||||
|
<span class="fs-5">${formattedPrice} PLN</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex flex-column">
|
||||||
|
<span class="fw-bold">Ilość</span>
|
||||||
|
<div class="d-flex align-items-center gap-2">
|
||||||
|
<span class="fs-5 badge bg-dark rounded-pill">${item.quantity}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex flex-column">
|
||||||
|
<span class="fw-bold cart-item-total">Suma</span>
|
||||||
|
<span class="fs-5 text-warning">${formattedTotal} PLN</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-3">
|
||||||
|
<div class="btn-group w-100" role="group">
|
||||||
|
<button class="btn btn-sm btn-outline-secondary decrease-quantity flex-grow-1"
|
||||||
|
data-book-id="${item.book_id}">
|
||||||
|
<i class="bi bi-dash"></i> Zmniejsz ilość
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm btn-outline-danger remove-from-cart cart-remove-btn flex-grow-1"
|
||||||
|
data-book-id="${item.book_id}">
|
||||||
|
<i class="bi bi-trash"></i> Usuń
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -84,12 +90,104 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||||
container.insertAdjacentHTML('beforeend', itemHTML);
|
container.insertAdjacentHTML('beforeend', itemHTML);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Dodaj całkowitą sumę koszyka
|
||||||
|
const totalHTML = `
|
||||||
|
<div class="col-12 mt-4">
|
||||||
|
<div class="card dark-card p-4">
|
||||||
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
|
<h2 class="mb-0">Suma całkowita</h2>
|
||||||
|
<h2 class="mb-0 total-cart-value">${totalCartValue.toFixed(2)} PLN</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
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) {
|
} catch (error) {
|
||||||
console.error('Error:', error);
|
console.error('Error:', error);
|
||||||
alert('Nie udało się załadować koszyka');
|
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 () => {
|
document.getElementById('checkoutBtn').addEventListener('click', async () => {
|
||||||
try {
|
try {
|
||||||
const token = localStorage.getItem('token');
|
const token = localStorage.getItem('token');
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
const sortSelect = document.getElementById('sortSelect');
|
const sortSelect = document.getElementById('sortSelect');
|
||||||
|
|
||||||
if (!booksContainer || !searchInput || !sortSelect) return;
|
if (!booksContainer || !searchInput || !sortSelect) return;
|
||||||
|
|
||||||
// Funkcje specyficzne dla strony głównej
|
// Funkcje specyficzne dla strony głównej
|
||||||
const createBookCard = (book) => `
|
const createBookCard = (book) => `
|
||||||
<div class="col">
|
<div class="col">
|
||||||
|
|
Loading…
Add table
Reference in a new issue