defmodule PhoenixKit.Modules.Shop.Web.CartPage do @moduledoc """ Public cart page LiveView for E-Commerce module. """ use PhoenixKitWeb, :live_view alias PhoenixKit.Modules.Billing.Currency alias PhoenixKit.Modules.Shop alias PhoenixKit.Modules.Shop.ShippingMethod alias PhoenixKit.Utils.Routes @impl true def mount(_params, session, socket) do # Get session_id from session (for guest users) session_id = session["shop_session_id"] || generate_session_id() # Get current user if logged in user = get_current_user(socket) user_id = if user, do: user.id, else: nil # Get or create cart {:ok, cart} = Shop.get_or_create_cart(user_id: user_id, session_id: session_id) # Get available shipping methods shipping_methods = Shop.get_available_shipping_methods(cart) # Auto-select cheapest shipping method if none selected {:ok, cart} = Shop.auto_select_shipping_method(cart, shipping_methods) # Get default currency from Billing currency = Shop.get_default_currency() # Check if user is authenticated authenticated = not is_nil(socket.assigns[:phoenix_kit_current_user]) socket = socket |> assign(:page_title, "Shopping Cart") |> assign(:cart, cart) |> assign(:session_id, session_id) |> assign(:shipping_methods, shipping_methods) |> assign(:currency, currency) |> assign(:authenticated, authenticated) {:ok, socket} end @impl true def handle_event("update_quantity", %{"item_id" => item_id, "quantity" => quantity}, socket) do item_id = String.to_integer(item_id) quantity = max(1, String.to_integer(quantity)) update_item_quantity(socket, item_id, quantity) end @impl true def handle_event("remove_item", %{"item_id" => item_id}, socket) do item_id = String.to_integer(item_id) item = Enum.find(socket.assigns.cart.items, &(&1.id == item_id)) if item do case Shop.remove_from_cart(item) do {:ok, updated_cart} -> shipping_methods = Shop.get_available_shipping_methods(updated_cart) {:noreply, socket |> assign(:cart, updated_cart) |> assign(:shipping_methods, shipping_methods) |> put_flash(:info, "Item removed from cart")} {:error, _} -> {:noreply, put_flash(socket, :error, "Failed to remove item")} end else {:noreply, socket} end end @impl true def handle_event("select_shipping", %{"method_id" => method_id}, socket) do method_id = String.to_integer(method_id) method = Enum.find(socket.assigns.shipping_methods, &(&1.id == method_id)) cart = socket.assigns.cart if method do # Country will be set at checkout based on billing info case Shop.set_cart_shipping(cart, method, nil) do {:ok, updated_cart} -> {:noreply, assign(socket, :cart, updated_cart)} {:error, _} -> {:noreply, put_flash(socket, :error, "Failed to set shipping method")} end else {:noreply, socket} end end @impl true def handle_event("proceed_to_checkout", _params, socket) do cart = socket.assigns.cart cond do cart.items == [] -> {:noreply, put_flash(socket, :error, "Your cart is empty")} is_nil(cart.shipping_method_id) -> {:noreply, put_flash(socket, :error, "Please select a shipping method")} true -> {:noreply, push_navigate(socket, to: Routes.path("/checkout"))} end end defp update_item_quantity(socket, item_id, quantity) do item = Enum.find(socket.assigns.cart.items, &(&1.id == item_id)) if item do case Shop.update_cart_item(item, quantity) do {:ok, updated_cart} -> shipping_methods = Shop.get_available_shipping_methods(updated_cart) {:noreply, socket |> assign(:cart, updated_cart) |> assign(:shipping_methods, shipping_methods)} {:error, _} -> {:noreply, put_flash(socket, :error, "Failed to update quantity")} end else {:noreply, socket} end end @impl true def render(assigns) do ~H""" <.shop_layout {assigns}>
Add some products to get started
<.link navigate={Routes.path("/shop")} class="btn btn-primary"> Browse Products| Product | Quantity | Price | |
|---|---|---|---|
|
<%= if item.product_image do %>
<.icon name="hero-cube" class="w-8 h-8 opacity-30" />
<% end %>
{item.product_title}
<%= if item.product_sku do %>
SKU: {item.product_sku}
<% end %>
<%= if item.compare_at_price && Decimal.compare(item.compare_at_price, item.unit_price) == :gt do %>
{format_price(item.compare_at_price, @currency)}
On sale!
<% end %>
|
{format_price(item.line_total, @currency)}
{format_price(item.unit_price, @currency)} each
|
Secure checkout powered by PhoenixKit
<% end %>