add no-JS progressive enhancement for all shop flows
All checks were successful
deploy / deploy (push) Successful in 1m23s
All checks were successful
deploy / deploy (push) Successful in 1m23s
Every key shop flow now works via plain HTML forms when JS is unavailable. LiveView progressively enhances when JS connects. - PDP: form wraps variant/qty/add-to-cart with action="/cart/add" - Cart page: qty +/- and remove use form POST fallbacks - Cart/search header icons are now links with phx-click enhancement - Collection sort form has GET action + noscript submit button - New /search page with form-based search for no-JS users - CartController gains add/remove/update_item POST actions - CartHook gains update_quantity_form and remove_item_form handlers - Fix flaky analytics tests caused by event table pollution Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -176,13 +176,16 @@ defmodule BerrypodWeb.Shop.Collection do
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<form phx-change="sort_changed">
|
||||
<form action={~p"/collections/#{@current_slug || "all"}"} method="get" phx-change="sort_changed">
|
||||
<.shop_select
|
||||
name="sort"
|
||||
options={@sort_options}
|
||||
selected={@current_sort}
|
||||
aria-label="Sort products"
|
||||
/>
|
||||
<noscript>
|
||||
<button type="submit" class="themed-button collection-sort-submit">Sort</button>
|
||||
</noscript>
|
||||
</form>
|
||||
</div>
|
||||
"""
|
||||
|
||||
75
lib/berrypod_web/live/shop/search.ex
Normal file
75
lib/berrypod_web/live/shop/search.ex
Normal file
@@ -0,0 +1,75 @@
|
||||
defmodule BerrypodWeb.Shop.Search do
|
||||
use BerrypodWeb, :live_view
|
||||
|
||||
alias Berrypod.Search
|
||||
|
||||
@impl true
|
||||
def mount(_params, _session, socket) do
|
||||
{:ok, assign(socket, :page_title, "Search")}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_params(params, _uri, socket) do
|
||||
query = params["q"] || ""
|
||||
results = if query != "", do: Search.search(query), else: []
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> assign(:search_page_query, query)
|
||||
|> assign(:search_page_results, results)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("search_submit", %{"q" => query}, socket) do
|
||||
{:noreply, push_patch(socket, to: ~p"/search?q=#{query}")}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<.shop_layout {layout_assigns(assigns)} active_page="search">
|
||||
<main id="main-content" class="page-container">
|
||||
<.page_title text="Search" />
|
||||
|
||||
<form action="/search" method="get" phx-submit="search_submit" class="search-page-form">
|
||||
<input
|
||||
type="search"
|
||||
name="q"
|
||||
value={@search_page_query}
|
||||
placeholder="Search products..."
|
||||
class="themed-input"
|
||||
/>
|
||||
<button type="submit" class="themed-button">Search</button>
|
||||
</form>
|
||||
|
||||
<%= if @search_page_results != [] do %>
|
||||
<p class="search-page-count">
|
||||
{length(@search_page_results)} {if length(@search_page_results) == 1,
|
||||
do: "result",
|
||||
else: "results"} for "{@search_page_query}"
|
||||
</p>
|
||||
<.product_grid theme_settings={@theme_settings}>
|
||||
<%= for product <- @search_page_results do %>
|
||||
<.product_card
|
||||
product={product}
|
||||
theme_settings={@theme_settings}
|
||||
mode={@mode}
|
||||
variant={:default}
|
||||
/>
|
||||
<% end %>
|
||||
</.product_grid>
|
||||
<% else %>
|
||||
<%= if @search_page_query != "" do %>
|
||||
<div class="collection-empty">
|
||||
<p>No products found for "{@search_page_query}"</p>
|
||||
<.link navigate="/collections/all" class="collection-empty-link">
|
||||
Browse all products
|
||||
</.link>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</main>
|
||||
</.shop_layout>
|
||||
"""
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user