defmodule BerrypodWeb.CartController do @moduledoc """ Cart controller handling both JSON API persistence and HTML form fallbacks. The JSON `update/2` action is called by a JS hook after each LiveView cart change. The HTML actions (`add/2`, `remove/2`, `update_item/2`) provide no-JS fallbacks via plain form POST + redirect. """ use BerrypodWeb, :controller alias Berrypod.Cart plug BerrypodWeb.Plugs.RateLimit, [type: :api] when action == :update @doc """ Updates the cart in session (JSON API for JS hook). """ def update(conn, %{"items" => items}) when is_list(items) do cart_items = Cart.deserialize(items) conn |> Cart.put_in_session(cart_items) |> json(%{ok: true}) end def update(conn, _params) do conn |> put_status(:bad_request) |> json(%{error: "Invalid cart data"}) end @doc """ Adds an item to cart via form POST (no-JS fallback). """ def add(conn, %{"variant_id" => variant_id, "quantity" => qty_str}) do quantity = parse_quantity(qty_str) cart = Cart.get_from_session(get_session(conn)) cart = Cart.add_item(cart, variant_id, quantity) conn |> Cart.put_in_session(cart) |> put_flash(:info, "Added to basket") |> redirect(to: ~p"/cart") end def add(conn, _params) do conn |> put_flash(:error, "Could not add item to basket") |> redirect(to: ~p"/cart") end @doc """ Removes an item from cart via form POST (no-JS fallback). """ def remove(conn, %{"variant_id" => variant_id}) do cart = Cart.get_from_session(get_session(conn)) cart = Cart.remove_item(cart, variant_id) conn |> Cart.put_in_session(cart) |> put_flash(:info, "Removed from basket") |> redirect(to: ~p"/cart") end @doc """ Updates item quantity via form POST (no-JS fallback). """ def update_item(conn, %{"variant_id" => variant_id, "quantity" => qty_str}) do quantity = parse_update_quantity(qty_str) cart = Cart.get_from_session(get_session(conn)) cart = Cart.update_quantity(cart, variant_id, quantity) conn |> Cart.put_in_session(cart) |> redirect(to: ~p"/cart") end @doc """ Updates delivery country via form POST (no-JS fallback). """ def update_country(conn, %{"country" => code}) when is_binary(code) and code != "" do conn |> put_session("country_code", code) |> redirect(to: ~p"/cart") end def update_country(conn, _params) do redirect(conn, to: ~p"/cart") end defp parse_quantity(str) when is_binary(str) do case Integer.parse(str) do {qty, _} when qty > 0 -> qty _ -> 1 end end defp parse_quantity(_), do: 1 # Allows 0 and negative values so Cart.update_quantity can remove items defp parse_update_quantity(str) when is_binary(str) do case Integer.parse(str) do {qty, _} -> qty :error -> 1 end end defp parse_update_quantity(_), do: 1 end