add contextual prompts for skipped setup steps
All checks were successful
deploy / deploy (push) Successful in 1m26s
All checks were successful
deploy / deploy (push) Successful in 1m26s
Disable checkout when Stripe isn't connected (cart drawer, cart page, and early guard in checkout controller to prevent orphaned orders). Show amber warning on order detail when email isn't configured. Fix pre-existing missing vertical spacing between page blocks. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -19,8 +19,7 @@ defmodule BerrypodWeb.CartHook do
|
||||
import Phoenix.Component, only: [assign: 3]
|
||||
import Phoenix.LiveView, only: [attach_hook: 4, connected?: 1, push_event: 3]
|
||||
|
||||
alias Berrypod.Cart
|
||||
alias Berrypod.Shipping
|
||||
alias Berrypod.{Cart, Settings, Shipping}
|
||||
|
||||
def on_mount(:mount_cart, _params, session, socket) do
|
||||
cart_items = Cart.get_from_session(session)
|
||||
@@ -34,6 +33,7 @@ defmodule BerrypodWeb.CartHook do
|
||||
|> update_cart_assigns(cart_items)
|
||||
|> assign(:cart_drawer_open, false)
|
||||
|> assign(:cart_status, nil)
|
||||
|> assign(:stripe_connected, Settings.has_secret?("stripe_api_key"))
|
||||
|> attach_hook(:cart_events, :handle_event, &handle_cart_event/3)
|
||||
|> attach_hook(:cart_info, :handle_info, &handle_cart_info/2)
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ defmodule BerrypodWeb.ShopComponents.Cart do
|
||||
attr :shipping_estimate, :integer, default: nil
|
||||
attr :country_code, :string, default: "GB"
|
||||
attr :available_countries, :list, default: []
|
||||
attr :stripe_connected, :boolean, default: true
|
||||
|
||||
def cart_drawer(assigns) do
|
||||
assigns =
|
||||
@@ -131,23 +132,23 @@ defmodule BerrypodWeb.ShopComponents.Cart do
|
||||
<span>{if @shipping_estimate, do: "Estimated total", else: "Subtotal"}</span>
|
||||
<span>{@display_total}</span>
|
||||
</div>
|
||||
<%= if @mode == :preview do %>
|
||||
<button
|
||||
type="button"
|
||||
class="cart-drawer-checkout"
|
||||
>
|
||||
Checkout
|
||||
</button>
|
||||
<% else %>
|
||||
<form action="/checkout" method="post">
|
||||
<input type="hidden" name="_csrf_token" value={Phoenix.Controller.get_csrf_token()} />
|
||||
<button
|
||||
type="submit"
|
||||
class="cart-drawer-checkout"
|
||||
>
|
||||
<%= cond do %>
|
||||
<% @mode == :preview -> %>
|
||||
<button type="button" class="cart-drawer-checkout">
|
||||
Checkout
|
||||
</button>
|
||||
</form>
|
||||
<% !@stripe_connected -> %>
|
||||
<button type="button" disabled class="cart-drawer-checkout">
|
||||
Checkout
|
||||
</button>
|
||||
<p class="cart-drawer-notice">Checkout isn't available yet.</p>
|
||||
<% true -> %>
|
||||
<form action="/checkout" method="post">
|
||||
<input type="hidden" name="_csrf_token" value={Phoenix.Controller.get_csrf_token()} />
|
||||
<button type="submit" class="cart-drawer-checkout">
|
||||
Checkout
|
||||
</button>
|
||||
</form>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
@@ -451,6 +452,7 @@ defmodule BerrypodWeb.ShopComponents.Cart do
|
||||
attr :country_code, :string, default: "GB"
|
||||
attr :available_countries, :list, default: []
|
||||
attr :mode, :atom, default: :live
|
||||
attr :stripe_connected, :boolean, default: true
|
||||
|
||||
def order_summary(assigns) do
|
||||
assigns =
|
||||
@@ -487,30 +489,42 @@ defmodule BerrypodWeb.ShopComponents.Cart do
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= if @mode == :preview do %>
|
||||
<.shop_button class="order-summary-checkout">
|
||||
Checkout
|
||||
</.shop_button>
|
||||
<.shop_button_outline
|
||||
phx-click="change_preview_page"
|
||||
phx-value-page="collection"
|
||||
class="order-summary-continue"
|
||||
>
|
||||
Continue shopping
|
||||
</.shop_button_outline>
|
||||
<% else %>
|
||||
<form action="/checkout" method="post" class="order-summary-checkout-form">
|
||||
<input type="hidden" name="_csrf_token" value={Phoenix.Controller.get_csrf_token()} />
|
||||
<.shop_button type="submit" class="order-summary-checkout">
|
||||
<%= cond do %>
|
||||
<% @mode == :preview -> %>
|
||||
<.shop_button class="order-summary-checkout">
|
||||
Checkout
|
||||
</.shop_button>
|
||||
</form>
|
||||
<.shop_link_outline
|
||||
href="/collections/all"
|
||||
class="order-summary-continue"
|
||||
>
|
||||
Continue shopping
|
||||
</.shop_link_outline>
|
||||
<.shop_button_outline
|
||||
phx-click="change_preview_page"
|
||||
phx-value-page="collection"
|
||||
class="order-summary-continue"
|
||||
>
|
||||
Continue shopping
|
||||
</.shop_button_outline>
|
||||
<% !@stripe_connected -> %>
|
||||
<.shop_button disabled class="order-summary-checkout">
|
||||
Checkout
|
||||
</.shop_button>
|
||||
<p class="order-summary-notice">Checkout isn't available yet.</p>
|
||||
<.shop_link_outline
|
||||
href="/collections/all"
|
||||
class="order-summary-continue"
|
||||
>
|
||||
Continue shopping
|
||||
</.shop_link_outline>
|
||||
<% true -> %>
|
||||
<form action="/checkout" method="post" class="order-summary-checkout-form">
|
||||
<input type="hidden" name="_csrf_token" value={Phoenix.Controller.get_csrf_token()} />
|
||||
<.shop_button type="submit" class="order-summary-checkout">
|
||||
Checkout
|
||||
</.shop_button>
|
||||
</form>
|
||||
<.shop_link_outline
|
||||
href="/collections/all"
|
||||
class="order-summary-continue"
|
||||
>
|
||||
Continue shopping
|
||||
</.shop_link_outline>
|
||||
<% end %>
|
||||
</.shop_card>
|
||||
"""
|
||||
|
||||
@@ -52,7 +52,7 @@ defmodule BerrypodWeb.ShopComponents.Layout do
|
||||
cart_subtotal cart_total cart_drawer_open cart_status active_page error_page is_admin
|
||||
search_query search_results search_open categories shipping_estimate
|
||||
country_code available_countries editing editor_current_path editor_sidebar_open
|
||||
header_nav_items footer_nav_items newsletter_enabled newsletter_state)a
|
||||
header_nav_items footer_nav_items newsletter_enabled newsletter_state stripe_connected)a
|
||||
|
||||
@doc """
|
||||
Extracts the assigns relevant to `shop_layout` from a full assigns map.
|
||||
@@ -101,6 +101,7 @@ defmodule BerrypodWeb.ShopComponents.Layout do
|
||||
attr :footer_nav_items, :list, default: []
|
||||
attr :newsletter_enabled, :boolean, default: false
|
||||
attr :newsletter_state, :atom, default: :idle
|
||||
attr :stripe_connected, :boolean, default: true
|
||||
|
||||
slot :inner_block, required: true
|
||||
|
||||
@@ -156,6 +157,7 @@ defmodule BerrypodWeb.ShopComponents.Layout do
|
||||
shipping_estimate={@shipping_estimate}
|
||||
country_code={@country_code}
|
||||
available_countries={@available_countries}
|
||||
stripe_connected={@stripe_connected}
|
||||
/>
|
||||
|
||||
<.search_modal
|
||||
|
||||
@@ -1,23 +1,29 @@
|
||||
defmodule BerrypodWeb.CheckoutController do
|
||||
use BerrypodWeb, :controller
|
||||
|
||||
alias Berrypod.{Analytics, Cart}
|
||||
alias Berrypod.{Analytics, Cart, Settings}
|
||||
alias Berrypod.Orders
|
||||
alias Berrypod.Shipping
|
||||
|
||||
require Logger
|
||||
|
||||
def create(conn, _params) do
|
||||
cart_items = Cart.get_from_session(get_session(conn))
|
||||
hydrated = Cart.hydrate(cart_items)
|
||||
|
||||
if hydrated == [] do
|
||||
unless Settings.has_secret?("stripe_api_key") do
|
||||
conn
|
||||
|> put_flash(:error, "Your basket is empty")
|
||||
|> put_flash(:error, "Checkout isn't available yet")
|
||||
|> redirect(to: ~p"/cart")
|
||||
else
|
||||
track_checkout_start(conn)
|
||||
create_checkout(conn, hydrated)
|
||||
cart_items = Cart.get_from_session(get_session(conn))
|
||||
hydrated = Cart.hydrate(cart_items)
|
||||
|
||||
if hydrated == [] do
|
||||
conn
|
||||
|> put_flash(:error, "Your basket is empty")
|
||||
|> redirect(to: ~p"/cart")
|
||||
else
|
||||
track_checkout_start(conn)
|
||||
create_checkout(conn, hydrated)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
defmodule BerrypodWeb.Admin.OrderShow do
|
||||
use BerrypodWeb, :live_view
|
||||
|
||||
alias Berrypod.{ActivityLog, Orders}
|
||||
alias Berrypod.{ActivityLog, Mailer, Orders}
|
||||
alias Berrypod.Cart
|
||||
|
||||
@impl true
|
||||
@@ -25,6 +25,7 @@ defmodule BerrypodWeb.Admin.OrderShow do
|
||||
|> assign(:page_title, order.order_number)
|
||||
|> assign(:order, order)
|
||||
|> assign(:timeline, timeline)
|
||||
|> assign(:email_configured, Mailer.email_configured?())
|
||||
|
||||
{:ok, socket}
|
||||
end
|
||||
@@ -48,6 +49,25 @@ defmodule BerrypodWeb.Admin.OrderShow do
|
||||
</div>
|
||||
</.header>
|
||||
|
||||
<div :if={!@email_configured} class="admin-callout-warning admin-card-spaced">
|
||||
<div class="admin-callout-warning-body">
|
||||
<span class="admin-callout-warning-icon">
|
||||
<.icon name="hero-exclamation-triangle" class="size-5" />
|
||||
</span>
|
||||
<div>
|
||||
<p class="admin-callout-warning-title">
|
||||
Order confirmation emails aren't being sent
|
||||
</p>
|
||||
<p class="admin-callout-warning-desc">
|
||||
Set up an email provider to send order confirmations and shipping updates automatically.
|
||||
<.link navigate={~p"/admin/settings/email"} class="admin-link">
|
||||
Set up email →
|
||||
</.link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-grid order-detail-grid">
|
||||
<%!-- order info --%>
|
||||
<div class="admin-card">
|
||||
|
||||
@@ -603,6 +603,7 @@ defmodule BerrypodWeb.PageRenderer do
|
||||
shipping_estimate={assigns[:shipping_estimate]}
|
||||
country_code={assigns[:country_code] || "GB"}
|
||||
available_countries={assigns[:available_countries] || []}
|
||||
stripe_connected={assigns[:stripe_connected] || false}
|
||||
mode={@mode}
|
||||
/>
|
||||
<% end %>
|
||||
|
||||
Reference in New Issue
Block a user