add no-JS contact form and noscript banner
All checks were successful
deploy / deploy (push) Successful in 1m21s

Wire up the contact form with action/method/name attrs so it works
without JavaScript. Add ContactNotifier, ContactController, and a
noscript info banner in the shop root layout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jamey
2026-02-28 18:57:51 +00:00
parent af069c2bca
commit ca01f43d70
8 changed files with 256 additions and 8 deletions

View File

@@ -74,6 +74,11 @@
</style>
</head>
<body>
<noscript>
<div style="background: #fef3c7; color: #92400e; padding: 0.75rem 1rem; text-align: center; font-size: 0.875rem;">
This shop works without JavaScript, but some features work better with it enabled.
</div>
</noscript>
<div
class="themed shop-root"
data-mood={@theme_settings.mood}

View File

@@ -100,29 +100,31 @@ defmodule BerrypodWeb.ShopComponents.Content do
<div class="contact-form-spacer"></div>
<% end %>
<form class="contact-form">
<form action="/contact/send" method="post" phx-submit="send_contact" class="contact-form">
<input type="hidden" name="_csrf_token" value={Phoenix.Controller.get_csrf_token()} />
<div>
<label class="contact-form-label">Name</label>
<.shop_input type="text" placeholder="Your name" />
<.shop_input type="text" name="name" placeholder="Your name" required />
</div>
<div>
<label class="contact-form-label">Email</label>
<.shop_input type="email" placeholder="your@email.com" />
<.shop_input type="email" name="email" placeholder="your@email.com" required />
</div>
<div>
<label class="contact-form-label">Subject</label>
<.shop_input type="text" placeholder="How can I help?" />
<.shop_input type="text" name="subject" placeholder="How can I help?" />
</div>
<div>
<label class="contact-form-label">Message</label>
<.shop_textarea rows="5" placeholder="Your message..." />
<.shop_textarea name="message" rows="5" placeholder="Your message..." required />
</div>
<.shop_button type="submit" class="contact-form-submit">
Send Message
Send message
</.shop_button>
</form>
</.shop_card>

View File

@@ -0,0 +1,27 @@
defmodule BerrypodWeb.ContactController do
use BerrypodWeb, :controller
alias Berrypod.ContactNotifier
@doc """
Handles contact form submission (no-JS fallback).
"""
def create(conn, params) do
case ContactNotifier.deliver_contact_message(params) do
{:ok, _} ->
conn
|> put_flash(:info, "Message sent! We'll get back to you soon.")
|> redirect(to: ~p"/contact")
{:error, :invalid_params} ->
conn
|> put_flash(:error, "Please fill in all required fields.")
|> redirect(to: ~p"/contact")
{:error, _} ->
conn
|> put_flash(:error, "Sorry, something went wrong. Please try again.")
|> redirect(to: ~p"/contact")
end
end
end

View File

@@ -1,7 +1,7 @@
defmodule BerrypodWeb.Shop.Contact do
use BerrypodWeb, :live_view
alias Berrypod.Orders
alias Berrypod.{ContactNotifier, Orders}
alias Berrypod.Orders.OrderNotifier
alias Berrypod.Pages
alias BerrypodWeb.OrderLookupController
@@ -39,6 +39,23 @@ defmodule BerrypodWeb.Shop.Contact do
{:noreply, assign(socket, :tracking_state, state)}
end
@impl true
def handle_event("send_contact", params, socket) do
case ContactNotifier.deliver_contact_message(params) do
{:ok, _} ->
{:noreply,
socket
|> put_flash(:info, "Message sent! We'll get back to you soon.")
|> push_navigate(to: ~p"/contact")}
{:error, :invalid_params} ->
{:noreply, put_flash(socket, :error, "Please fill in all required fields.")}
{:error, _} ->
{:noreply, put_flash(socket, :error, "Sorry, something went wrong. Please try again.")}
end
end
@impl true
def handle_event("reset_tracking", _params, socket) do
{:noreply, assign(socket, :tracking_state, :idle)}

View File

@@ -270,7 +270,8 @@ defmodule BerrypodWeb.Router do
# Checkout (POST — creates Stripe session and redirects)
post "/checkout", CheckoutController, :create
# Order lookup (no-JS fallback for contact page form)
# Contact form + order lookup (no-JS fallbacks for contact page forms)
post "/contact/send", ContactController, :create
post "/contact/lookup", OrderLookupController, :lookup
# Cart form actions (no-JS fallbacks for LiveView cart events)