diff --git a/migrations/20250531163129_dostawa.sql b/migrations/20250531163129_dostawa.sql new file mode 100644 index 0000000..cf716bd --- /dev/null +++ b/migrations/20250531163129_dostawa.sql @@ -0,0 +1 @@ +ALTER TABLE zamowienia ADD COLUMN typ_dostawy VARCHAR(20) NOT NULL DEFAULT 'shipping'; diff --git a/src/cart.rs b/src/cart.rs index 7692f4c..7e0b795 100644 --- a/src/cart.rs +++ b/src/cart.rs @@ -7,6 +7,7 @@ use bigdecimal::BigDecimal; use serde_json::json; use log; use std::str::FromStr; // Dodane +use crate::models::CartQuantityUpdate; #[get("/api/cart")] pub async fn get_cart( @@ -110,20 +111,22 @@ pub async fn checkout( let total_bigdecimal = BigDecimal::from_str(&total_str) .map_err(|_| AppError::BadRequest("Invalid total value".to_string()))?; - // 1. Utwórz zamówienie - let order_id = sqlx::query!( - "INSERT INTO zamowienia (user_id, suma_totalna) - VALUES ($1, $2) RETURNING id", + // 1. Utwórz zamówienie z typem dostawy + let order_record = sqlx::query!( + "INSERT INTO zamowienia (user_id, suma_totalna, typ_dostawy) + VALUES ($1, $2, $3) RETURNING id", user_id, - total_bigdecimal + total_bigdecimal, + data.delivery_type ) .fetch_one(&mut *transaction) .await .map_err(|e| { log::error!("Błąd tworzenia zamówienia: {}", e); AppError::InternalServerError("Błąd serwera".to_string()) - })? - .id; + })?; + + let order_id = order_record.id; // 2. Dodaj pozycje zamówienia for item in &data.items { @@ -173,3 +176,28 @@ pub async fn checkout( Ok(HttpResponse::Ok().json(json!({"status": "success"}))) } + +#[post("/api/update-cart-quantity")] +pub async fn update_cart_quantity( + req: HttpRequest, + pool: web::Data, + data: web::Json, +) -> Result { + let user_id = validate_token(&req).await?; + + sqlx::query!( + "UPDATE koszyk SET quantity = quantity + $1 + WHERE user_id = $2 AND book_id = $3", + data.change, + user_id, + data.book_id + ) + .execute(pool.get_ref()) + .await + .map_err(|e| { + log::error!("Błąd bazy danych: {}", e); + AppError::InternalServerError("Błąd serwera".to_string()) + })?; + + Ok(HttpResponse::Ok().json(json!({"status": "success"}))) +} diff --git a/src/main.rs b/src/main.rs index a05808c..b1d2b1d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -51,6 +51,7 @@ async fn main() -> std::io::Result<()> { .service(cart::add_to_cart) .service(cart::remove_from_cart) .service(cart::checkout) + .service(cart::update_cart_quantity) .service(profile::get_order_history) .service( Files::new("/images", "./static/images") diff --git a/src/models.rs b/src/models.rs index 310464b..a8b50e3 100644 --- a/src/models.rs +++ b/src/models.rs @@ -67,9 +67,15 @@ pub struct OrderWithItems { pub items: Vec, } +#[derive(Deserialize)] +pub struct CartQuantityUpdate { + pub book_id: i32, + pub change: i32, +} + #[derive(Deserialize)] pub struct CheckoutRequest { pub items: Vec, pub total: f64, + pub delivery_type: String, // "shipping" lub "local" } - diff --git a/static/book.html b/static/book.html index 17c6cf8..d009046 100644 --- a/static/book.html +++ b/static/book.html @@ -12,20 +12,16 @@ - +
-
-
+
+
- Okładka książki + Okładka książki
-
+

@@ -91,6 +87,7 @@ + diff --git a/static/cart.html b/static/cart.html index 82e144a..d0f749b 100644 --- a/static/cart.html +++ b/static/cart.html @@ -12,20 +12,16 @@ - +
@@ -101,6 +107,7 @@ + diff --git a/static/css/styles.css b/static/css/styles.css index e1619c9..3e40158 100644 --- a/static/css/styles.css +++ b/static/css/styles.css @@ -1,80 +1,272 @@ /* static/css/styles.css */ - +/* Główne zmienne kolorystyczne */ :root { - --dark-bg: #121212; - --darker-bg: #0a0a0a; - --light-text: #f8f9fa; - --muted-text: #adb5bd; - --primary: #d4af37; /* Złoty - główny kolor */ - --primary-dark: #b38f2d; /* Ciemniejszy złoty */ - --secondary: #0a1f0a; /* Ciemnozielony - drugi kolor */ - --border: #343a40; - --accent: #228B22; /* Zielony akcent */ + --dark-primary: #1a2e1a; /* ciemnozielony */ + --light-primary: #f8f9fa; /* jasny */ + --gold: #d4af37; /* złoty */ + --dark-text: #f5f5f5; + --light-text: #212529; + --dark-secondary: #0d1c0d; + --light-secondary: #e9ecef; + --dark-card: #142814; + --light-card: #ffffff; } -.dark-theme { - background-color: var(--dark-bg); - color: var(--light-text); +/* Reset i podstawowe style */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; +} + +body { + font-family: 'Inter', sans-serif; + transition: background-color 0.3s ease, color 0.3s ease; min-height: 100vh; display: flex; flex-direction: column; - font-family: 'Inter', sans-serif; } -/* Nagłówek - oryginalny styl */ +/* Motyw ciemny - domyślny */ +body.dark-theme { + background-color: var(--dark-primary); + color: var(--dark-text); +} + +body.dark-theme .navbar { + background-color: var(--dark-secondary); + border-bottom: 1px solid rgba(212, 175, 55, 0.2); +} + +body.dark-theme .card { + background-color: var(--dark-card); + border: 1px solid rgba(212, 175, 55, 0.2); + color: var(--dark-text); +} + +body.dark-theme .form-control, +body.dark-theme .form-select { + background-color: var(--dark-card); + border: 1px solid rgba(212, 175, 55, 0.3); + color: var(--dark-text); +} + +body.dark-theme .form-control:focus, +body.dark-theme .form-select:focus { + border-color: var(--gold); + box-shadow: 0 0 0 0.25rem rgba(212, 175, 55, 0.25); +} + +body.dark-theme .auth-container { + background-color: var(--dark-card); + border: 1px solid rgba(212, 175, 55, 0.2); +} + +body.dark-theme footer { + background-color: var(--dark-secondary); + border-top: 1px solid rgba(212, 175, 55, 0.2); +} + +body.dark-theme .order-card { + background-color: #142814; + border: 1px solid rgba(212, 175, 55, 0.2); + color: #f5f5f5; +} + +body.dark-theme .order-card .card-header { + background-color: #0d1c0d; + border-bottom: 1px solid rgba(212, 175, 55, 0.2); + color: #f5f5f5; +} + +body.dark-theme .order-card .list-group-item { + background-color: #1a2e1a; + border: 1px solid rgba(212, 175, 55, 0.1); + color: #f5f5f5; +} + +body.dark-theme .order-card .list-group-item strong { + color: #d4af37; +} + +/* Motyw jasny */ +body.light-theme { + background-color: var(--light-primary); + color: var(--light-text); +} + +body.light-theme .navbar { + background-color: var(--light-secondary); + border-bottom: 1px solid rgba(0, 0, 0, 0.1); +} + +body.light-theme .card { + background-color: var(--light-card); + border: 1px solid rgba(0, 0, 0, 0.1); + color: var(--light-text); +} + +body.light-theme .form-control, +body.light-theme .form-select { + background-color: var(--light-card); + border: 1px solid rgba(0, 0, 0, 0.1); + color: var(--light-text); +} + +body.light-theme .form-control:focus, +body.light-theme .form-select:focus { + border-color: var(--gold); + box-shadow: 0 0 0 0.25rem rgba(212, 175, 55, 0.25); +} + +body.light-theme .auth-container { + background-color: var(--light-card); + border: 1px solid rgba(0, 0, 0, 0.1); +} + +body.light-theme footer { + background-color: var(--light-secondary); + border-top: 1px solid rgba(0, 0, 0, 0.1); +} + +/* Wspólne style dla obu motywów */ .navbar { - background-color: #000; padding: 1rem 0; - border-bottom: 1px solid var(--border); + transition: background-color 0.3s ease; } .navbar-brand { font-family: 'Old English Text MT', serif; - font-size: 2.5rem; - color: var(--primary) !important; - text-shadow: 0 0 5px rgba(212, 175, 55, 0.5); + font-size: 2rem; + color: var(--gold) !important; + letter-spacing: 1px; } .navbar-link { - color: var(--muted-text) !important; + color: var(--gold) !important; text-decoration: none; - margin: 0 0.8rem; - transition: color 0.3s; - display: flex; - align-items: center; + margin: 0 0.5rem; + transition: opacity 0.3s ease; + font-weight: 500; } -.navbar-link:hover, .navbar-link:focus { - color: var(--primary) !important; +.navbar-link:hover { + opacity: 0.8; } -.navbar-link i { - margin-right: 0.3rem; +.btn { + background-color: transparent; + border: 2px solid var(--gold); + color: var(--gold) !important; + padding: 0.5rem 1.5rem; + border-radius: 0; + font-weight: 500; + transition: all 0.3s ease; } -/* Karty książek - proporcje 5:8 */ -.book-card { - height: 100%; - display: flex; - flex-direction: column; - background: var(--darker-bg); - border: 1px solid var(--border); +.btn:hover { + background-color: var(--gold); + color: #fff !important; +} + +.btn-add-to-cart { + padding: 0.75rem 2rem; + font-size: 1.1rem; +} + +.btn-gothic { + font-family: 'Old English Text MT', serif; + letter-spacing: 1px; +} + +.btn-outline-gothic { + font-family: 'Old English Text MT', serif; + letter-spacing: 1px; + border: 2px solid var(--gold); + color: var(--gold) !important; +} + +.btn-outline-gothic:hover { + background-color: var(--gold); + color: #fff !important; +} + +.card { border-radius: 8px; overflow: hidden; - transition: transform 0.3s ease, box-shadow 0.3s ease; + transition: transform 0.3s ease; } -.book-card:hover { +.card:hover { transform: translateY(-5px); - box-shadow: 0 10px 20px rgba(212, 175, 55, 0.2); - border-color: var(--primary); } -.cover-container { +.card-img-top { + height: 300px; + object-fit: cover; + width: 100%; +} + +.auth-container { + border-radius: 10px; + padding: 2rem; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); +} + +.text-gothic { + font-family: 'Old English Text MT', serif; + color: var(--gold); +} + +footer { + padding: 2rem 0; + margin-top: auto; +} + +footer a { + color: var(--gold); + text-decoration: none; + margin: 0 1rem; +} + +footer a:hover { + text-decoration: underline; +} + +.text-primary { + color: var(--gold) !important; +} + +.border-primary { + border-color: var(--gold) !important; +} + +/* Styl dla książek */ +.books-grid { + display: grid; + grid-template-columns: repeat(5, 1fr); /* 5 kolumn */ + gap: 1.5rem; +} + +.book-card { position: relative; - padding-bottom: 160%; /* 5:8 proportion (5/8 = 0.625) */ overflow: hidden; - background: #000; +} + +.book-cover-link { + display: block; + text-decoration: none; +} + +.book-cover-container { + position: relative; + width: 100%; + padding-top: 160%; /* 5:8 ratio (5/8=0.625 -> 1/0.625=1.6) */ + overflow: hidden; } .book-cover { @@ -87,134 +279,184 @@ transition: transform 0.3s ease; } -.cover-container:hover .book-cover { +.book-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.7); + color: white; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + opacity: 0; + transition: opacity 0.3s ease; + padding: 1rem; + text-align: center; +} + +.book-overlay h5 { + font-size: 1.25rem; + margin-bottom: 0.5rem; +} + +.book-overlay p { + margin-bottom: 0.25rem; +} + +.book-overlay .price { + font-weight: bold; + color: #d4af37; +} + +.book-cover-link:hover .book-overlay { + opacity: 1; +} + +.book-cover-link:hover .book-cover { transform: scale(1.05); } -.book-body { - padding: 1.2rem; - flex-grow: 1; - display: flex; - flex-direction: column; +/* Responsywność */ +@media (max-width: 1200px) { + .books-grid { + grid-template-columns: repeat(4, 1fr); + } } -.book-title { - font-size: 1.1rem; - margin-bottom: 0.5rem; - color: var(--light-text); - font-weight: 600; - min-height: 2.8em; +@media (max-width: 992px) { + .books-grid { + grid-template-columns: repeat(3, 1fr); + } } -.book-author { - font-size: 0.9rem; - color: var(--muted-text); - margin-bottom: 0.5rem; +@media (max-width: 768px) { + .books-grid { + grid-template-columns: repeat(2, 1fr); + } } -.book-price { - font-weight: 700; - color: var(--primary); - margin-top: auto; - font-size: 1.2rem; +@media (max-width: 576px) { + .books-grid { + grid-template-columns: 1fr; + } } -/* Przyciski i elementy interfejsu */ -.btn { - background-color: var(--primary); - border: none; - color: #000; - padding: 0.8rem 1.5rem; - border-radius: 5px; - font-weight: 500; - transition: all 0.3s; - text-transform: uppercase; - letter-spacing: 1px; +@media (max-width: 768px) { + .book-cover-container { + padding-top: 140%; /* Slightly different ratio on mobile */ + } +} + +/* Koszyk */ +#cart-items .card { + margin-bottom: 1.5rem; +} + +.card { + transition: opacity 0.3s ease, transform 0.3s ease; +} + +.decrease-btn, .increase-btn, .remove-btn { + transition: background-color 0.2s ease, transform 0.2s ease; +} + +.decrease-btn:hover, .increase-btn:hover, .remove-btn:hover { + transform: scale(1.1); +} + +.quantity-display { + display: inline-block; + min-width: 30px; + text-align: center; font-weight: bold; } -.btn:hover { - background-color: var(--primary-dark); - transform: translateY(-2px); -} - -.text-primary { - color: var(--primary) !important; -} - -.bg-primary { - background-color: var(--primary) !important; +/* Animacje dla ilości */ +.quantity-change { + transition: opacity 0.15s ease, transform 0.15s ease; } /* Formularze */ -.auth-container { - max-width: 500px; - margin: 3rem auto; - padding: 2.5rem; - background: var(--darker-bg); - border-radius: 10px; - border: 1px solid var(--border); - box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); -} - -.auth-container h2 { - text-align: center; - margin-bottom: 1.5rem; - font-weight: 700; - color: var(--primary); -} - -.dark-input { - background-color: #1e1e1e; - border: 1px solid var(--border); - color: var(--light-text); - padding: 0.8rem; - border-radius: 5px; - margin-bottom: 1.2rem; - width: 100%; -} - -.dark-input:focus { - background-color: #1e1e1e; - color: var(--light-text); - border-color: var(--primary); - box-shadow: 0 0 0 0.25rem rgba(212, 175, 55, 0.25); -} - -/* Stopka */ -footer { - background-color: #000; - padding: 2.5rem 0; - margin-top: auto; - border-top: 1px solid var(--border); -} - -footer a { - color: var(--muted-text); - text-decoration: none; - margin: 0 1rem; - transition: color 0.3s; -} - -footer a:hover { - color: var(--primary); -} - -footer .text-center { - color: var(--muted-text); +.form-control, .form-select { + border-radius: 0; + padding: 0.75rem; + font-size: 1rem; } /* Responsywność */ @media (max-width: 768px) { .navbar-brand { - font-size: 1.8rem; + font-size: 1.5rem; } - .cover-container { - padding-bottom: 130%; + .books-grid { + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); } - .book-title { - min-height: auto; + .row.mb-4 { + flex-direction: column; + gap: 1rem; + } + + .col-md-4, .col-md-5, .col-md-3 { + width: 100%; } } + +/* Animacje spinnera */ +@keyframes spin { + to { transform: rotate(360deg); } +} + +.spinner-border { + animation: spin 0.75s linear infinite; + color: var(--gold); +} + +/* Alerty */ +.alert { + border-radius: 0; + padding: 1rem; + margin-bottom: 1rem; +} + +.alert-danger { + background-color: rgba(220, 53, 69, 0.1); + border: 1px solid rgba(220, 53, 69, 0.2); + color: #dc3545; +} + +.alert-info { + background-color: rgba(13, 202, 240, 0.1); + border: 1px solid rgba(13, 202, 240, 0.2); + color: #0dcaf0; +} + +/* Ikony w koszyku */ +.bi { + vertical-align: middle; +} + +/* Strona podziękowania */ +.py-5 { + padding-top: 6rem !important; + padding-bottom: 6rem !important; +} + +.display-3 { + font-family: 'Old English Text MT', serif; + margin-bottom: 2rem; +} + +.lead { + font-size: 1.5rem; + margin-bottom: 3rem; +} + +/* Układ dla strony profilu */ +#order-history .card { + margin-bottom: 1.5rem; +} diff --git a/static/index.html b/static/index.html index a7f169f..aad36fe 100644 --- a/static/index.html +++ b/static/index.html @@ -15,11 +15,13 @@