Skip to content

Commit

Permalink
feat: add items to cart + tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ricksonoliveira committed Dec 14, 2023
1 parent 76ee9d7 commit 4c5c4b4
Show file tree
Hide file tree
Showing 15 changed files with 309 additions and 44 deletions.
2 changes: 1 addition & 1 deletion assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import Hooks from "./hooks"

let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")

let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}, hooks: Hooks})
let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken, cart_id: sessionStorage.getItem("cart_id")}, hooks: Hooks})

// Show progress bar on live navigation and form submits
topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"})
Expand Down
4 changes: 3 additions & 1 deletion assets/js/hooks.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import LoadMoreProducts from "./hooks/loadMoreProducts"
import CartSession from "./hooks/cartSession"

let Hooks = {
LoadMoreProducts: LoadMoreProducts
LoadMoreProducts: LoadMoreProducts,
CartSession: CartSession
}

export default Hooks
10 changes: 10 additions & 0 deletions assets/js/hooks/cartSession.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const CartSession = {
mounted(){
this.handleEvent("create_cart_session_id", map => {
var {cart_id: cart_id} = map;
sessionStorage.setItem("cart_id", cart_id);
})
}
}

export default CartSession;
10 changes: 6 additions & 4 deletions lib/food_order_web/components/layouts/header_component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,6 @@ defmodule FoodOrderWeb.HeaderComponent do
</.link>
</li>
</ul>
<a href={~p"/cart"} class="ml-6 p-6 bg-fuchsia-500 rounded-full text-neutral-100 flex group hover:text-fuchsia-500 hover:bg-fuchsia-300 transition">
<span class="text-xs">0</span>
<Heroicons.shopping_cart solid class="h-5 w-5 stroke-current" />
</a>
<% else %>
<li class="ml-6">
<.link href={~p"/users/register"}>
Expand All @@ -63,6 +59,12 @@ defmodule FoodOrderWeb.HeaderComponent do
</.link>
</li>
<% end %>
<%= if !is_nil(@cart_id) do %>
<a href={~p"/cart"} class="ml-6 p-6 bg-fuchsia-500 rounded-full text-neutral-100 flex group hover:text-fuchsia-500 hover:bg-fuchsia-300 transition">
<span class="text-xs"><%= FoodOrder.Carts.get(@cart_id).total_qty %></span>
<Heroicons.shopping_cart solid class="h-5 w-5 stroke-current" />
</a>
<% end %>
</ul>
</nav>
"""
Expand Down
2 changes: 1 addition & 1 deletion lib/food_order_web/components/layouts/root.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</head>
<body>
<div class="mx-auto max-w-7xl">
<FoodOrderWeb.HeaderComponent.menu current_user={@current_user} />
<FoodOrderWeb.HeaderComponent.menu current_user={@current_user} cart_id={Map.get(assigns, :cart_id)} request_path={@conn.request_path} />
<%= @inner_content %>
</div>
</body>
Expand Down
13 changes: 7 additions & 6 deletions lib/food_order_web/live/cart_live/cart_live.ex
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
defmodule FoodOrderWeb.CartLive do
use FoodOrderWeb, :live_view

alias FoodOrder.{Carts, Products}
alias FoodOrder.Carts
alias FoodOrderWeb.CartLive.Details

def mount(_, _, socket) do
uuid = Ecto.UUID.generate()
Carts.create(uuid)
product = Products.list_products() |> hd
Carts.add(uuid, product)
cart = Carts.get(uuid)
cart_id = socket.assigns.cart_id
cart = Carts.get(cart_id)
{:ok, assign(socket, cart: cart)}
end

def handle_info({:update, cart}, socket) do
{:noreply, assign(socket, cart: cart)}
end

defp empty_cart(assigns) do
~H"""
<div class="py-16 container mx-auto text-center" >
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
</div>

<div>
<.live_component :for={item <- @cart.items} module={Item} id={item.item.id} item={item} />
<.live_component :for={item <- @cart.items |> Enum.sort()} module={Item} id={item.item.id} item={item} cart_id={@cart.id} />

<hr />

Expand Down
34 changes: 29 additions & 5 deletions lib/food_order_web/live/cart_live/details/item/item.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
defmodule FoodOrderWeb.CartLive.Details.Item do
use FoodOrderWeb, :live_component

alias FoodOrder.Carts

def render(assigns) do
~H"""
<div id={@id} data-role="item" class="flex items-center my-8 shadow-lg p-2 hover:bg-neutral-100">
Expand All @@ -13,19 +15,41 @@ defmodule FoodOrderWeb.CartLive.Details.Item do
<div class="flex-1" data-role="quantity">
<div class="flex items-center">
<button data-role="dec" class="p-1 m-2 rounded-full text-white font-bold bg-fuchsia-500">-</button>
<span><%= @item.qty %> Item(s)</span>
<button data-role="add" class="p-1 m-2 rounded-full text-white font-bold bg-fuchsia-500">+</button>
<button data-role="dec" phx-click="dec" phx-target={@myself} data-id={@id} class="p-1 m-2 rounded-full text-white font-bold bg-fuchsia-500">-</button>
<span data-role="items" data-id={@id}><%= @item.qty %> Item(s)</span>
<button data-role="inc" data-role="add" phx-click="inc" phx-target={@myself} data-id={@id} class="p-1 m-2 rounded-full text-white font-bold bg-fuchsia-500">+</button>
</div>
</div>
<div class="flex flex-1 items-center" data-role="total-item">
<span class="font-bold text-lg"><%= @item.item.price %></span>
<button class="ml-2 w-6 h-6 rounded-full text-white bg-fuchsia-500 font-bold">
<span data-role="price" data-id={@id} class="font-bold text-lg"><%= @item.item.price %></span>
<button data-role="remove" phx-click="remove" phx-target={@myself} data-id={@id} class="ml-2 w-6 h-6 rounded-full text-white bg-fuchsia-500 font-bold">
&times
</button>
</div>
</div>
"""
end

def handle_event("remove", _, socket) do
update_cart(socket, &Carts.remove/2)
{:noreply, socket}
end

def handle_event("dec", _, socket) do
update_cart(socket, &Carts.decrement/2)
{:noreply, socket}
end

def handle_event("inc", _, socket) do
update_cart(socket, &Carts.increment/2)
{:noreply, socket}
end

defp update_cart(socket, fun) do
product_id = socket.assigns.id
cart_id = socket.assigns.cart_id
cart = fun.(cart_id, product_id)
send(self(), {:update, cart})
end
end
48 changes: 48 additions & 0 deletions lib/food_order_web/live/middlewares/cart_session.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
defmodule FoodOrderWeb.Middlewares.CartSession do
import Phoenix.LiveView, only: [get_connect_params: 1, push_event: 3]
import Phoenix.Component
alias FoodOrder.Accounts
alias FoodOrder.Carts

def on_mount(:default, _, params, socket) do
cart_id = get_connect_params(socket)["cart_id"]

socket =
socket
|> assign_user(params["user_token"])
|> create_cart(cart_id)

{:cont, socket}
end

defp assign_user(socket, nil), do: assign(socket, :current_user, nil)

defp assign_user(socket, user_token) do
assign_new(socket, :current_user, fn -> Accounts.get_user_by_session_token(user_token) end)
end

defp create_cart(socket, cart_id) do
current_user = socket.assigns.current_user
cart_id = build_cart(current_user, cart_id)

socket
|> assign(cart_id: cart_id)
|> push_event("create_cart_session_id", %{cart_id: cart_id})
end

defp build_cart(nil, nil) do
cart_id = Ecto.UUID.generate()
Carts.create(cart_id)
cart_id
end

defp build_cart(nil, cart_id) do
Carts.create(cart_id)
cart_id
end

defp build_cart(%{id: cart_id}, _) do
Carts.create(cart_id)
cart_id
end
end
19 changes: 18 additions & 1 deletion lib/food_order_web/live/page_live/item/item.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
defmodule FoodOrderWeb.PageLive.Item do
use FoodOrderWeb, :live_component

alias FoodOrder.Carts

def handle_event("add", _, socket) do
product = socket.assigns.product
cart_id = socket.assigns.cart_id
add_product_to_cart(cart_id, product)

{:noreply,
socket
|> put_flash(:info, "Item added to cart")
|> push_redirect(to: "/")}
end

defp product_detail(assigns) do
~H"""
<h2 class="mb-4 text-lg"><%= @name %></h2>
Expand All @@ -14,11 +27,15 @@ defmodule FoodOrderWeb.PageLive.Item do
~H"""
<div class="mt-6 flex items-center justify-around">
<span class="font-bold text-lg"><%= @price %></span>
<button class="border-2 py-1 px-6 rounded-full border-fuchsia-500 text-fuchsia-500 transition hover:bg-fuchsia-500 hover:text-neutral-100">
<button phx-click="add" phx-target={@myself} class="border-2 py-1 px-6 rounded-full border-fuchsia-500 text-fuchsia-500 transition hover:bg-fuchsia-500 hover:text-neutral-100">
<span>Add</span>
<span>+</span>
</button>
</div>
"""
end

defp add_product_to_cart(cart_id, product) do
Carts.add(cart_id, product)
end
end
2 changes: 1 addition & 1 deletion lib/food_order_web/live/page_live/item/item.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
<img src={~p"/images/products/#{@product.image_url}"} alt="" class="h-40 mb-4 mx-auto">
<div class="text-center" data-role="item-details" data-id={@product.id}>
<.product_detail name={@product.name} size={@product.size} />
<.product_info price={@product.price} />
<.product_info price={@product.price} myself={@myself} />
</div>
</div>
44 changes: 23 additions & 21 deletions lib/food_order_web/live/page_live/page_live.html.heex
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
<section class="py-16" data-role="hero">
<div class="container mx-auto flex flex-col md:flex-row items-center justify-between">
<div data-role="hero-cta">
<h6 class="text-lg">Make your order</h6>
<h1 class="text-3xl font-bold">Right now!</h1>
<button class="px-6 bg-fuchsia-500 rounded-full py-2 text-white mt-5 fond-bold">Order now</button>
<div id="cart-session" phx-hook="CartSession">
<section class="py-16" data-role="hero">
<div class="container mx-auto flex flex-col md:flex-row items-center justify-between">
<div data-role="hero-cta">
<h6 class="text-lg">Make your order</h6>
<h1 class="text-3xl font-bold">Right now!</h1>
<button class="px-6 bg-fuchsia-500 rounded-full py-2 text-white mt-5 fond-bold">Order now</button>
</div>
<img src={~p"/images/hamburger.svg"} alt="" data-role="hero-img">
</div>
<img src={~p"/images/hamburger.svg"} alt="" data-role="hero-img">
</div>
</section>
</section>

<section class="container mx-auto py-8" data-role="products-section">
<h1 class="text-lg font-bold mb-8">All foods</h1>
<section class="container mx-auto py-8" data-role="products-section">
<h1 class="text-lg font-bold mb-8">All foods</h1>

<div
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-8"
data-role="products-list"
id="products-list"
phx-update="append"
>
<.live_component :for={product <- @products} module={Item}, id={product.id} product={product} />
</div>
<div
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-8"
data-role="products-list"
id="products-list"
phx-update="append"
>
<.live_component :for={product <- @products} module={Item} id={product.id} product={product} cart_id={@cart_id} />
</div>

</section>
</section>

<div id="load_more_products" phx-hook="LoadMoreProducts"></div>
<div id="load_more_products" phx-hook="LoadMoreProducts"></div>
</div>
8 changes: 6 additions & 2 deletions lib/food_order_web/router.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
defmodule FoodOrderWeb.Router do
use FoodOrderWeb, :router

alias FoodOrderWeb.Middlewares.CartSession

import FoodOrderWeb.UserAuth

pipeline :browser do
Expand All @@ -20,8 +22,10 @@ defmodule FoodOrderWeb.Router do
scope "/", FoodOrderWeb do
pipe_through :browser

live "/", PageLive, :index
live "/cart", CartLive, :index
live_session :create_cart_session, on_mount: CartSession do
live "/", PageLive, :index
live "/cart", CartLive, :index
end
end

# Other scopes may use custom stacks.
Expand Down
Loading

0 comments on commit 4c5c4b4

Please sign in to comment.