From 8014def1a8da3397d78d1162f9e1b8c3f22d0322 Mon Sep 17 00:00:00 2001 From: Akshay Date: Sat, 26 Dec 2020 10:51:46 +0530 Subject: add transactions and quantities - backend exposes endpoints to perform transactions - frontend introduces a new page to "checkout" cart --- .../2020-12-25-041256_cart_quantity/down.sql | 4 + .../2020-12-25-041256_cart_quantity/up.sql | 3 + .../2020-12-25-150728_transaction_date/down.sql | 4 + .../2020-12-25-150728_transaction_date/up.sql | 4 + backend/src/bin/server.rs | 18 ++- backend/src/handlers/cart_items.rs | 131 ++++++++++++++++++--- backend/src/handlers/mod.rs | 1 + backend/src/handlers/transaction.rs | 74 ++++++++++++ backend/src/handlers/users.rs | 4 +- backend/src/models.rs | 28 +++++ backend/src/schema.rs | 2 + frontend/src/Cart.elm | 76 ++++++++++-- frontend/src/Catalog.elm | 16 +-- frontend/src/Checkout.elm | 126 ++++++++++++++++++++ frontend/src/Main.elm | 39 +++++- 15 files changed, 485 insertions(+), 45 deletions(-) create mode 100644 backend/migrations/2020-12-25-041256_cart_quantity/down.sql create mode 100644 backend/migrations/2020-12-25-041256_cart_quantity/up.sql create mode 100644 backend/migrations/2020-12-25-150728_transaction_date/down.sql create mode 100644 backend/migrations/2020-12-25-150728_transaction_date/up.sql create mode 100644 backend/src/handlers/transaction.rs create mode 100644 frontend/src/Checkout.elm diff --git a/backend/migrations/2020-12-25-041256_cart_quantity/down.sql b/backend/migrations/2020-12-25-041256_cart_quantity/down.sql new file mode 100644 index 0000000..94aec7a --- /dev/null +++ b/backend/migrations/2020-12-25-041256_cart_quantity/down.sql @@ -0,0 +1,4 @@ +-- This file should undo anything in `up.sql` + +alter table cart_items +drop column quantity; diff --git a/backend/migrations/2020-12-25-041256_cart_quantity/up.sql b/backend/migrations/2020-12-25-041256_cart_quantity/up.sql new file mode 100644 index 0000000..314c11c --- /dev/null +++ b/backend/migrations/2020-12-25-041256_cart_quantity/up.sql @@ -0,0 +1,3 @@ +-- Your SQL goes here +alter table cart_items +add quantity integer default 1; diff --git a/backend/migrations/2020-12-25-150728_transaction_date/down.sql b/backend/migrations/2020-12-25-150728_transaction_date/down.sql new file mode 100644 index 0000000..18fe306 --- /dev/null +++ b/backend/migrations/2020-12-25-150728_transaction_date/down.sql @@ -0,0 +1,4 @@ +-- This file should undo anything in `up.sql` + +alter table transaction +drop column order_date; diff --git a/backend/migrations/2020-12-25-150728_transaction_date/up.sql b/backend/migrations/2020-12-25-150728_transaction_date/up.sql new file mode 100644 index 0000000..76f9820 --- /dev/null +++ b/backend/migrations/2020-12-25-150728_transaction_date/up.sql @@ -0,0 +1,4 @@ +-- Your SQL goes here + +alter table transaction +add order_date date not null default curdate(); diff --git a/backend/src/bin/server.rs b/backend/src/bin/server.rs index 7c67e4f..310914e 100644 --- a/backend/src/bin/server.rs +++ b/backend/src/bin/server.rs @@ -5,7 +5,7 @@ use actix_web::{web, App, HttpServer}; use diesel::r2d2::{ConnectionManager, Pool}; use diesel::MysqlConnection; use furby::handlers::smoke::manual_hello; -use furby::handlers::{cart_items, product, rating, users}; +use furby::handlers::{cart_items, product, rating, transaction, users}; use rand::Rng; #[actix_web::main] @@ -48,6 +48,7 @@ async fn main() -> std::io::Result<()> { web::scope("/user") .route("/existing", web::post().to(users::name_exists)) .route("/login", web::post().to(users::login)) + .route("/logout", web::post().to(users::logout)) .route("/{uname}", web::get().to(users::user_details)) .route("/new", web::post().to(users::new_user)) .route( @@ -75,6 +76,10 @@ async fn main() -> std::io::Result<()> { "/items", web::get().to(cart_items::get_user_cart_items), ) + .route( + "/total", + web::get().to(cart_items::get_user_cart_total), + ) .route("/add", web::post().to(cart_items::add_to_cart)) .route( "/remove", @@ -86,6 +91,17 @@ async fn main() -> std::io::Result<()> { .route("/add", web::post().to(rating::add_rating)) .route("/remove", web::post().to(rating::remove_rating)), ) + .service( + web::scope("/transaction") + .route( + "/checkout", + web::post().to(transaction::checkout_cart), + ) + .route( + "/list", + web::get().to(transaction::list_transactions), + ), + ) .route("/hey", web::get().to(manual_hello)) }) .bind("127.0.0.1:7878")? diff --git a/backend/src/handlers/cart_items.rs b/backend/src/handlers/cart_items.rs index 25baaeb..e17f4c4 100644 --- a/backend/src/handlers/cart_items.rs +++ b/backend/src/handlers/cart_items.rs @@ -7,7 +7,7 @@ use actix_identity::Identity; use actix_web::{web, HttpResponse, Responder}; use diesel::prelude::*; use log::{error, info}; -use serde::Deserialize; +use serde::Serialize; pub async fn add_to_cart( cookie: Identity, @@ -27,16 +27,41 @@ pub async fn add_to_cart( let new_cart_item = AddCartItem { cart_id: selected_user.id, product_id: item_details, + quantity: Some(1), }; info!( "cart id: {:?}, product id {:?}", selected_user.id, item_details ); - diesel::insert_into(cart_items) - .values((cart_id.eq(selected_user.id), product_id.eq(item_details))) - .execute(&conn) - .expect("Coundn't connect to DB"); - HttpResponse::Ok().body("Inserted successfully!") + let current_entry = cart_items + .filter(cart_id.eq(selected_user.id)) + .filter(product_id.eq(item_details)) + .limit(1) + .first::(&conn); + match current_entry { + Ok(v) => { + info!("Item already present in cart, increasing quantity."); + let old_quantity = v.quantity.unwrap_or(1); + diesel::update( + cart_items + .filter(cart_id.eq(selected_user.id)) + .filter(product_id.eq(item_details)), + ) + .set(quantity.eq(old_quantity + 1)) + .execute(&conn) + .unwrap(); + return HttpResponse::Ok() + .body("Updated quantity successfully!"); + } + Err(_) => { + info!("Item not present, adding to cart."); + diesel::insert_into(cart_items) + .values(new_cart_item) + .execute(&conn) + .expect("Couldn't connect to DB"); + HttpResponse::Ok().body("Inserted successfully!") + } + } } else { error!("Unauthorized add to cart action!"); return HttpResponse::Unauthorized() @@ -58,15 +83,44 @@ pub async fn remove_from_cart( .limit(1) .first::(&conn) .expect("Couldn't connect to DB"); - - diesel::delete( - cart_items - .filter(cart_id.eq(selected_user.id)) - .filter(product_id.eq(item_details)), - ) - .execute(&conn) - .expect("Coundn't connect to DB"); - HttpResponse::Ok().body("Removed successfully!") + let current_entry = cart_items + .filter(cart_id.eq(selected_user.id)) + .filter(product_id.eq(item_details)) + .limit(1) + .first::(&conn); + match current_entry { + Ok(v) => { + info!("Item already present in cart, increasing quantity."); + let old_quantity = v.quantity.unwrap_or(1); + if old_quantity == 1 { + diesel::delete( + cart_items + .filter(cart_id.eq(selected_user.id)) + .filter(product_id.eq(item_details)), + ) + .execute(&conn) + .expect("Coundn't connect to DB"); + } else { + diesel::update( + cart_items + .filter(cart_id.eq(selected_user.id)) + .filter(product_id.eq(item_details)), + ) + .set(quantity.eq(old_quantity - 1)) + .execute(&conn) + .unwrap(); + return HttpResponse::Ok() + .body("Updated quantity successfully!"); + } + return HttpResponse::Ok() + .body("Updated quantity successfully!"); + } + Err(_) => { + info!("Item not present."); + return HttpResponse::InternalServerError() + .body("Item not found!"); + } + } } else { error!("Unauthorized add to cart action!"); return HttpResponse::Unauthorized() @@ -74,6 +128,12 @@ pub async fn remove_from_cart( } } +#[derive(Serialize)] +struct UserCartItem { + product_item: Product, + quantity: i32, +} + pub async fn get_user_cart_items( cookie: Identity, pool: web::Data, @@ -92,11 +152,15 @@ pub async fn get_user_cart_items( let cart_products = user_cart_items .into_iter() .map(|item| { - prod::product + let p = prod::product .filter(prod::id.eq(item.product_id)) .limit(1) .first::(&conn) - .expect("Couldn't connect to db") + .expect("Couldn't connect to db"); + UserCartItem { + product_item: p, + quantity: item.quantity.unwrap_or(1), + } }) .collect::>(); return HttpResponse::Ok().json(&cart_products); @@ -105,3 +169,36 @@ pub async fn get_user_cart_items( .body("Need to be logged in to add to cart!"); } } + +pub async fn get_user_cart_total( + cookie: Identity, + pool: web::Data, +) -> impl Responder { + let conn = pool.get().unwrap(); + if let Some(uname) = cookie.identity() { + let selected_user = customer + .filter(username.eq(&uname)) + .limit(1) + .first::(&conn) + .expect("Couldn't connect to DB"); + let user_cart_items = cart_items + .filter(cart_id.eq(selected_user.id)) + .load::(&conn) + .expect("Couldn't connect to DB"); + let cart_total: f32 = user_cart_items + .into_iter() + .map(|item| { + let p = prod::product + .filter(prod::id.eq(item.product_id)) + .limit(1) + .first::(&conn) + .expect("Couldn't connect to db"); + return p.price * item.quantity.unwrap_or(1) as f32; + }) + .sum(); + return HttpResponse::Ok().json(&cart_total); + } else { + return HttpResponse::Unauthorized() + .body("Need to be logged in to add to cart!"); + } +} diff --git a/backend/src/handlers/mod.rs b/backend/src/handlers/mod.rs index 9416857..e4ecb3a 100644 --- a/backend/src/handlers/mod.rs +++ b/backend/src/handlers/mod.rs @@ -2,4 +2,5 @@ pub mod cart_items; pub mod product; pub mod rating; pub mod smoke; +pub mod transaction; pub mod users; diff --git a/backend/src/handlers/transaction.rs b/backend/src/handlers/transaction.rs new file mode 100644 index 0000000..1e87312 --- /dev/null +++ b/backend/src/handlers/transaction.rs @@ -0,0 +1,74 @@ +use crate::models::{AddTransaction, CartItem, Customer, Product, Transaction}; +use crate::schema::cart_items::dsl::*; +use crate::schema::customer::dsl::*; +use crate::schema::product::dsl as prod; +use crate::schema::transaction::dsl::*; +use crate::TPool; + +use actix_identity::Identity; +use actix_web::{web, HttpResponse, Responder}; +use diesel::prelude::*; +use log::{error, info}; + +pub async fn checkout_cart( + pool: web::Data, + pmt_kind: String, + cookie: Identity, +) -> impl Responder { + let conn = pool.get().unwrap(); + info!("Checkout cart for user: {:?}", cookie.identity()); + if let Some(uname) = cookie.identity() { + let selected_user = customer + .filter(username.eq(&uname)) + .limit(1) + .first::(&conn) + .expect("Couldn't connect to DB"); + let user_cart_items = cart_items + .filter(cart_id.eq(selected_user.id)) + .load::(&conn) + .expect("Couldn't connect to DB"); + let cart_total = user_cart_items.into_iter().fold(0., |acc, item| { + let item_price = prod::product + .filter(prod::id.eq(item.product_id)) + .limit(1) + .first::(&conn) + .unwrap() + .price; + acc + item.quantity.unwrap_or(1) as f32 * item_price + }); + let transaction_entry = AddTransaction { + customer_id: Some(selected_user.id), + amount: cart_total, + payment_type: pmt_kind, + }; + diesel::insert_into(transaction) + .values(transaction_entry) + .execute(&conn) + .expect("Coundn't connect to DB"); + return HttpResponse::Ok().body("Transaction performed successfully"); + } else { + return HttpResponse::Unauthorized().body("Login first"); + } +} + +pub async fn list_transactions( + pool: web::Data, + cookie: Identity, +) -> impl Responder { + let conn = pool.get().unwrap(); + if let Some(uname) = cookie.identity() { + let selected_user = customer + .filter(username.eq(&uname)) + .limit(1) + .first::(&conn) + .expect("Couldn't connect to DB"); + let user_transactions = transaction + .filter(customer_id.eq(selected_user.id)) + .load::(&conn) + .expect("Couldn't connect to DB"); + return HttpResponse::Ok().json(&user_transactions); + } else { + return HttpResponse::Unauthorized() + .body("Need to be logged in to add to cart!"); + } +} diff --git a/backend/src/handlers/users.rs b/backend/src/handlers/users.rs index 24fb591..a043c1f 100644 --- a/backend/src/handlers/users.rs +++ b/backend/src/handlers/users.rs @@ -58,7 +58,7 @@ pub async fn login( login_details: web::Json, ) -> impl Responder { info!("Login hit"); - if let Some(uname) = cookie.identity() { + if cookie.identity().is_some() { info!("Found existing cookie: {:?}", cookie.identity()); return HttpResponse::Ok().finish(); } @@ -84,7 +84,7 @@ pub async fn login( pub async fn logout(cookie: Identity) -> impl Responder { cookie.forget(); - HttpResponse::Found().header("location", "/").finish() + HttpResponse::Ok().body("Successful logout.") } pub async fn user_details( diff --git a/backend/src/models.rs b/backend/src/models.rs index a104209..bf531ad 100644 --- a/backend/src/models.rs +++ b/backend/src/models.rs @@ -35,6 +35,8 @@ pub struct Product { pub kind: Option, pub price: f32, pub description: Option, + pub src: Option, + pub ios_src: Option, } #[derive(Insertable, Deserialize)] @@ -48,6 +50,12 @@ pub struct NewProduct { #[serde(skip_serializing_if = "Option::is_none")] pub description: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub src: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub ios_src: Option, } #[derive(Deserialize)] @@ -63,6 +71,7 @@ pub struct UpdateProduct { pub struct CartItem { pub cart_id: i32, pub product_id: i32, + pub quantity: Option, } #[derive(Insertable, Deserialize)] @@ -70,6 +79,7 @@ pub struct CartItem { pub struct AddCartItem { pub cart_id: i32, pub product_id: i32, + pub quantity: Option, } /* Rating */ @@ -95,3 +105,21 @@ pub struct AddRating { pub product_id: i32, pub customer_id: i32, } + +/* Transaction */ +#[derive(Queryable, Serialize)] +pub struct Transaction { + pub id: i32, + pub payment_type: String, + pub amount: f32, + pub customer_id: Option, + pub order_date: NaiveDate, +} + +#[derive(Insertable, Deserialize)] +#[table_name = "transaction"] +pub struct AddTransaction { + pub payment_type: String, + pub amount: f32, + pub customer_id: Option, +} diff --git a/backend/src/schema.rs b/backend/src/schema.rs index f08221a..1419bc0 100644 --- a/backend/src/schema.rs +++ b/backend/src/schema.rs @@ -2,6 +2,7 @@ table! { cart_items (cart_id, product_id) { cart_id -> Integer, product_id -> Integer, + quantity -> Nullable, } } @@ -43,6 +44,7 @@ table! { payment_type -> Varchar, amount -> Float, customer_id -> Nullable, + order_date -> Date, } } diff --git a/frontend/src/Cart.elm b/frontend/src/Cart.elm index a1750f6..58fb72e 100644 --- a/frontend/src/Cart.elm +++ b/frontend/src/Cart.elm @@ -21,9 +21,15 @@ type alias Product = } +type alias CartListing = + { productItem : Product + , quantity : Int + } + + type alias Model = { pageStatus : Status - , products : List Product + , products : List CartListing } @@ -34,10 +40,12 @@ type Status type Msg - = CartLoaded (Result Http.Error (List Product)) + = CartLoaded (Result Http.Error (List CartListing)) | FetchCartItems | RemoveFromCart Int | CartItemRemoved (Result Http.Error ()) + | AddToCartSuccess (Result Http.Error ()) + | AddToCartPressed Int init : Model @@ -69,6 +77,12 @@ update msg model = FetchCartItems -> ( { model | pageStatus = Loading }, fetchCartItems ) + AddToCartPressed id -> + ( model, addToCart id ) + + AddToCartSuccess _ -> + ( { model | pageStatus = Loading }, fetchCartItems ) + decodeProduct : D.Decoder Product decodeProduct = @@ -80,9 +94,13 @@ decodeProduct = (D.field "description" (D.nullable D.string)) -decodeResponse : D.Decoder (List Product) +decodeResponse : D.Decoder (List CartListing) decodeResponse = - D.list decodeProduct + D.list + (D.map2 CartListing + (D.field "product_item" decodeProduct) + (D.field "quantity" D.int) + ) removeProduct : Int -> Cmd Msg @@ -132,15 +150,45 @@ viewStatus s = "Not loaded ..." -viewProduct : Product -> Html Msg -viewProduct p = +addToCart : Int -> Cmd Msg +addToCart id = + let + _ = + Debug.log "err" <| "adding to cart: " ++ String.fromInt id + in + Http.riskyRequest + { method = "POST" + , headers = [] + , url = "http://127.0.0.1:7878/cart/add" + , body = Http.stringBody "applcation/json" <| String.fromInt <| id + , expect = Http.expectWhatever AddToCartSuccess + , timeout = Nothing + , tracker = Nothing + } + + +calculateTotal : Model -> Float +calculateTotal model = + let + items = + model.products + in + items + |> List.map (\i -> toFloat i.quantity * i.productItem.price) + |> List.foldl (+) 0 + + +viewCartItemListing : CartListing -> Html Msg +viewCartItemListing listing = div [] - [ text p.name - , div [] [ text <| Maybe.withDefault "" p.kind ] - , div [] [ text <| Maybe.withDefault "" p.description ] - , div [] [ text <| String.fromFloat p.price ] - , div [] [ button [ onClick (RemoveFromCart p.id) ] [ text "Remove" ] ] - , div [] [ a [ href ("/product/" ++ String.fromInt p.id) ] [ text "View Product" ] ] + [ text listing.productItem.name + , div [] [ text <| Maybe.withDefault "" listing.productItem.kind ] + , div [] [ text <| Maybe.withDefault "" listing.productItem.description ] + , div [] [ text <| String.fromFloat listing.productItem.price ] + , div [] [ text <| String.fromInt listing.quantity ] + , div [] [ button [ onClick (AddToCartPressed listing.productItem.id) ] [ text "Add" ] ] + , div [] [ button [ onClick (RemoveFromCart listing.productItem.id) ] [ text "Remove" ] ] + , div [] [ a [ href ("/product/" ++ String.fromInt listing.productItem.id) ] [ text "View Product" ] ] ] @@ -154,11 +202,13 @@ view model = div [] [ let cart = - List.map viewProduct model.products + List.map viewCartItemListing model.products in if List.isEmpty cart then text "No items in cart" else ul [] cart + , calculateTotal model |> String.fromFloat |> text + , a [ href "/checkout" ] [ text "Checkout" ] ] diff --git a/frontend/src/Catalog.elm b/frontend/src/Catalog.elm index 6882c73..d00cb92 100644 --- a/frontend/src/Catalog.elm +++ b/frontend/src/Catalog.elm @@ -188,13 +188,13 @@ viewFilters : Model -> Html Msg viewFilters model = let priceRange = - range 0 50000 5000 + range 0 55000 5000 ratingRange = - List.range 1 5 + range 1 6 1 - viewRange = - List.map (\i -> option [] [ text <| String.fromInt i ]) + viewRange default scale = + List.map (\i -> option [ selected (i == default) ] [ text <| String.fromInt i ]) scale inp = Maybe.withDefault 0 << String.toFloat @@ -202,15 +202,15 @@ viewFilters model = div [] [ div [] [ text "Price" - , select [ onInput (ChangePriceLower << inp) ] (viewRange priceRange) + , select [ onInput (ChangePriceLower << inp) ] (viewRange 0 priceRange) , text "to" - , select [ onInput (ChangePriceUpper << inp) ] (viewRange priceRange) + , select [ onInput (ChangePriceUpper << inp) ] (viewRange 50000 priceRange) ] , div [] [ text "Rating" - , select [ onInput (ChangeRatingLower << inp) ] (viewRange ratingRange) + , select [ onInput (ChangeRatingLower << inp) ] (viewRange 1 ratingRange) , text "to" - , select [ onInput (ChangeRatingUpper << inp) ] (viewRange ratingRange) + , select [ onInput (ChangeRatingUpper << inp) ] (viewRange 5 ratingRange) ] ] diff --git a/frontend/src/Checkout.elm b/frontend/src/Checkout.elm new file mode 100644 index 0000000..c60da0d --- /dev/null +++ b/frontend/src/Checkout.elm @@ -0,0 +1,126 @@ +module Checkout exposing (..) + +import Browser +import Browser.Navigation as Nav +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) +import Http +import Json.Decode as D +import Json.Encode as Encode +import Tuple exposing (..) +import Url +import Url.Parser as P exposing ((), Parser, int, oneOf, s, string) +import Utils exposing (..) + + +type alias Model = + { pageStatus : Status + , paymentMode : String + , cartTotal : Float + } + + +type Status + = Loading + | Loaded + | NotLoaded + + +type Msg + = CheckoutPressed + | CheckoutSuccessful (Result Http.Error ()) + | AmountLoaded (Result Http.Error Float) + | FetchAmount + | PaymentModeSelected String + + +init : Model +init = + Model NotLoaded "Cash" 0 + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + CheckoutPressed -> + ( model, tryCheckout model.paymentMode ) + + CheckoutSuccessful _ -> + ( model, Cmd.none ) + + AmountLoaded res -> + case res of + Ok v -> + ( { model | cartTotal = v }, Cmd.none ) + + Err _ -> + ( { model | pageStatus = NotLoaded }, Cmd.none ) + + FetchAmount -> + let + _ = + Debug.log "err" "fetching checkout amount" + in + ( { model | pageStatus = Loading }, fetchAmount ) + + PaymentModeSelected s -> + ( { model | paymentMode = s }, Cmd.none ) + + +fetchAmount : Cmd Msg +fetchAmount = + Http.riskyRequest + { method = "GET" + , headers = [] + , url = "http://127.0.0.1:7878/cart/total" + , body = Http.emptyBody + , expect = Http.expectJson AmountLoaded D.float + , timeout = Nothing + , tracker = Nothing + } + + +tryCheckout : String -> Cmd Msg +tryCheckout pm = + Http.riskyRequest + { method = "POST" + , headers = [] + , url = "http://127.0.0.1:7878/transaction/checkout" + , body = Http.stringBody "application/json" pm + , expect = Http.expectWhatever CheckoutSuccessful + , timeout = Nothing + , tracker = Nothing + } + + +viewStatus : Status -> String +viewStatus s = + case s of + Loading -> + "Loading" + + Loaded -> + "Ready!" + + NotLoaded -> + "Not loaded ..." + + +view : Model -> Html Msg +view model = + case model.pageStatus of + Loading -> + div [] [ text <| viewStatus Loading ] + + _ -> + div [] + [ div [] [ text <| String.fromFloat <| model.cartTotal ] + , select [] + [ option [ onInput PaymentModeSelected ] [ text "Cash" ] + , option [ onInput PaymentModeSelected ] [ text "Debit Card" ] + , option [ onInput PaymentModeSelected ] [ text "Credit Card" ] + ] + , div [] [ a [ href "/cart" ] [ text "Cancel" ] ] + , div [] [ button [ onClick CheckoutPressed ] [ text "Confirm and Pay" ] ] + ] diff --git a/frontend/src/Main.elm b/frontend/src/Main.elm index bf1583c..f1883a1 100644 --- a/frontend/src/Main.elm +++ b/frontend/src/Main.elm @@ -4,6 +4,7 @@ import Browser import Browser.Navigation as Nav import Cart import Catalog +import Checkout import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (..) @@ -43,6 +44,7 @@ type Route | CatalogPage | CartPage | ProductPage Int + | CheckoutPage | NotFoundPage @@ -54,9 +56,8 @@ parseRoute = , P.map CatalogPage (P.s "catalog") , P.map CartPage (P.s "cart") , P.map SignupPage (P.s "signup") + , P.map CheckoutPage (P.s "checkout") , P.map ProductPage (P.s "product" P.int) - - --, P.map ProductPage (P.s "product" int) ] @@ -69,6 +70,7 @@ type alias Model = , productModel : Product.Model , signupModel : Signup.Model , cartModel : Cart.Model + , checkoutModel : Checkout.Model } @@ -92,8 +94,11 @@ init flags url key = cart = Cart.init + + checkout = + Checkout.init in - ( Model key url start login catalog product signup cart, Cmd.none ) + ( Model key url start login catalog product signup cart checkout, Cmd.none ) @@ -108,6 +113,7 @@ type Msg | ProductMessage Product.Msg | SignupMessage Signup.Msg | CartMessage Cart.Msg + | CheckoutMessage Checkout.Msg | LogoutPressed | LogoutSuccess (Result Http.Error ()) @@ -127,7 +133,7 @@ update msg model = ( model, tryLogout ) LogoutSuccess _ -> - ( model, Nav.replaceUrl model.key "/login" ) + ( { model | loginModel = Login.init }, Nav.replaceUrl model.key "/login" ) UrlChanged url -> let @@ -155,6 +161,16 @@ update msg model = in ( { model | location = CartPage }, cmd ) + Just CheckoutPage -> + let + _ = + Debug.log "err" "loading checkout page ..." + + cmd = + Cmd.map CheckoutMessage Checkout.fetchAmount + in + ( { model | location = CheckoutPage }, cmd ) + Just p -> ( { model | location = p }, Cmd.none ) @@ -205,6 +221,16 @@ update msg model = in ( { model | cartModel = cmn }, Cmd.map CartMessage cmd ) + CheckoutMessage cm -> + let + ( cmn, cmd ) = + Checkout.update cm model.checkoutModel + + _ = + Debug.log "err" "received checkout message ..." + in + ( { model | checkoutModel = cmn }, Cmd.map CheckoutMessage cmd ) + ProductMessage pm -> let ( pmn, cmd ) = @@ -292,6 +318,11 @@ view model = , body = pageWrap model (Html.map CartMessage (Cart.view model.cartModel)) } + CheckoutPage -> + { title = "Checkout" + , body = pageWrap model (Html.map CheckoutMessage (Checkout.view model.checkoutModel)) + } + ProductPage item -> { title = "Product " ++ String.fromInt item , body = pageWrap model (Html.map ProductMessage (Product.view model.productModel)) -- cgit v1.2.3