2026-02-18 21:23:15 +00:00
|
|
|
defmodule BerrypodWeb.Admin.Settings do
|
|
|
|
|
use BerrypodWeb, :live_view
|
2026-02-07 17:12:53 +00:00
|
|
|
|
2026-02-18 21:23:15 +00:00
|
|
|
alias Berrypod.Accounts
|
|
|
|
|
alias Berrypod.Products
|
|
|
|
|
alias Berrypod.Settings
|
|
|
|
|
alias Berrypod.Stripe.Setup, as: StripeSetup
|
2026-02-07 17:12:53 +00:00
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
|
def mount(_params, _session, socket) do
|
2026-02-12 09:04:51 +00:00
|
|
|
user = socket.assigns.current_scope.user
|
|
|
|
|
|
2026-02-11 22:58:58 +00:00
|
|
|
{:ok,
|
|
|
|
|
socket
|
|
|
|
|
|> assign(:page_title, "Settings")
|
|
|
|
|
|> assign(:site_live, Settings.site_live?())
|
add abandoned cart recovery
When a Stripe checkout session expires without payment, if the customer
entered their email, we record an AbandonedCart and schedule a single
plain-text recovery email (1h delay via Oban).
Privacy design:
- feature is off by default; shop owner opts in via admin settings
- only contacts customers who entered their email at Stripe checkout
- single email, never more (emailed_at timestamp gate)
- suppression list blocks repeat contact; one-click unsubscribe via
signed token (/unsubscribe/:token)
- records pruned after 30 days (nightly Oban cron)
- no tracking pixels, no redirected links, no HTML
Legal notes:
- custom_text added to Stripe session footer when recovery is on
- UK PECR soft opt-in; EU legitimate interests both satisfied by this design
Files:
- migration: abandoned_carts + email_suppressions tables
- schemas: AbandonedCart, EmailSuppression
- context: Orders.create_abandoned_cart, check_suppression, add_suppression,
has_recent_paid_order?, get_abandoned_cart_by_session, mark_abandoned_cart_emailed
- workers: AbandonedCartEmailWorker (checkout queue), AbandonedCartPruneWorker (cron)
- notifier: OrderNotifier.deliver_cart_recovery/3
- webhook: extended checkout.session.expired handler
- controller: UnsubscribeController, admin settings toggle
- tests: 28 new tests across context, workers, and controller
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 10:02:37 +00:00
|
|
|
|> assign(:cart_recovery_enabled, Settings.abandoned_cart_recovery_enabled?())
|
2026-02-12 09:04:51 +00:00
|
|
|
|> assign_stripe_state()
|
|
|
|
|
|> assign_products_state()
|
|
|
|
|
|> assign_account_state(user)}
|
2026-02-07 17:12:53 +00:00
|
|
|
end
|
|
|
|
|
|
2026-02-12 09:04:51 +00:00
|
|
|
# -- Stripe assigns --
|
|
|
|
|
|
2026-02-07 17:12:53 +00:00
|
|
|
defp assign_stripe_state(socket) do
|
|
|
|
|
has_key = Settings.has_secret?("stripe_api_key")
|
|
|
|
|
has_signing = Settings.has_secret?("stripe_signing_secret")
|
|
|
|
|
|
|
|
|
|
status =
|
|
|
|
|
cond do
|
|
|
|
|
!has_key -> :not_configured
|
|
|
|
|
has_key && StripeSetup.localhost?() -> :connected_localhost
|
|
|
|
|
true -> :connected
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
socket
|
|
|
|
|
|> assign(:stripe_status, status)
|
|
|
|
|
|> assign(:stripe_api_key_hint, Settings.secret_hint("stripe_api_key"))
|
|
|
|
|
|> assign(:stripe_signing_secret_hint, Settings.secret_hint("stripe_signing_secret"))
|
|
|
|
|
|> assign(:stripe_webhook_url, StripeSetup.webhook_url())
|
|
|
|
|
|> assign(:stripe_has_signing_secret, has_signing)
|
|
|
|
|
|> assign(:connect_form, to_form(%{"api_key" => ""}, as: :stripe))
|
|
|
|
|
|> assign(:secret_form, to_form(%{"signing_secret" => ""}, as: :webhook))
|
2026-02-12 09:04:51 +00:00
|
|
|
|> assign(:stripe_advanced_open, false)
|
2026-02-07 17:12:53 +00:00
|
|
|
|> assign(:connecting, false)
|
|
|
|
|
end
|
|
|
|
|
|
2026-02-12 09:04:51 +00:00
|
|
|
# -- Products assigns --
|
|
|
|
|
|
|
|
|
|
defp assign_products_state(socket) do
|
|
|
|
|
connections = Products.list_provider_connections()
|
|
|
|
|
|
|
|
|
|
connection_info =
|
|
|
|
|
case connections do
|
|
|
|
|
[conn | _] ->
|
|
|
|
|
product_count = Products.count_products_for_connection(conn.id)
|
|
|
|
|
%{connection: conn, product_count: product_count}
|
|
|
|
|
|
|
|
|
|
[] ->
|
|
|
|
|
nil
|
|
|
|
|
end
|
|
|
|
|
|
2026-02-15 10:53:15 +00:00
|
|
|
assign(socket, :provider, connection_info)
|
2026-02-12 09:04:51 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# -- Account assigns --
|
|
|
|
|
|
|
|
|
|
defp assign_account_state(socket, user) do
|
|
|
|
|
email_changeset = Accounts.change_user_email(user, %{}, validate_unique: false)
|
|
|
|
|
password_changeset = Accounts.change_user_password(user, %{}, hash_password: false)
|
|
|
|
|
|
|
|
|
|
socket
|
|
|
|
|
|> assign(:current_email, user.email)
|
|
|
|
|
|> assign(:email_form, to_form(email_changeset))
|
|
|
|
|
|> assign(:password_form, to_form(password_changeset))
|
|
|
|
|
|> assign(:trigger_submit, false)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# -- Events: shop status --
|
|
|
|
|
|
2026-02-07 17:12:53 +00:00
|
|
|
@impl true
|
2026-02-12 09:04:51 +00:00
|
|
|
def handle_event("toggle_site_live", _params, socket) do
|
|
|
|
|
new_value = !socket.assigns.site_live
|
|
|
|
|
{:ok, _} = Settings.set_site_live(new_value)
|
|
|
|
|
|
|
|
|
|
message = if new_value, do: "Shop is now live", else: "Shop taken offline"
|
|
|
|
|
|
|
|
|
|
{:noreply,
|
|
|
|
|
socket
|
|
|
|
|
|> assign(:site_live, new_value)
|
|
|
|
|
|> put_flash(:info, message)}
|
|
|
|
|
end
|
|
|
|
|
|
add abandoned cart recovery
When a Stripe checkout session expires without payment, if the customer
entered their email, we record an AbandonedCart and schedule a single
plain-text recovery email (1h delay via Oban).
Privacy design:
- feature is off by default; shop owner opts in via admin settings
- only contacts customers who entered their email at Stripe checkout
- single email, never more (emailed_at timestamp gate)
- suppression list blocks repeat contact; one-click unsubscribe via
signed token (/unsubscribe/:token)
- records pruned after 30 days (nightly Oban cron)
- no tracking pixels, no redirected links, no HTML
Legal notes:
- custom_text added to Stripe session footer when recovery is on
- UK PECR soft opt-in; EU legitimate interests both satisfied by this design
Files:
- migration: abandoned_carts + email_suppressions tables
- schemas: AbandonedCart, EmailSuppression
- context: Orders.create_abandoned_cart, check_suppression, add_suppression,
has_recent_paid_order?, get_abandoned_cart_by_session, mark_abandoned_cart_emailed
- workers: AbandonedCartEmailWorker (checkout queue), AbandonedCartPruneWorker (cron)
- notifier: OrderNotifier.deliver_cart_recovery/3
- webhook: extended checkout.session.expired handler
- controller: UnsubscribeController, admin settings toggle
- tests: 28 new tests across context, workers, and controller
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 10:02:37 +00:00
|
|
|
# -- Events: cart recovery --
|
|
|
|
|
|
|
|
|
|
def handle_event("toggle_cart_recovery", _params, socket) do
|
|
|
|
|
new_value = !socket.assigns.cart_recovery_enabled
|
|
|
|
|
{:ok, _} = Settings.set_abandoned_cart_recovery(new_value)
|
|
|
|
|
|
|
|
|
|
message =
|
|
|
|
|
if new_value,
|
|
|
|
|
do: "Cart recovery emails enabled",
|
|
|
|
|
else: "Cart recovery emails disabled"
|
|
|
|
|
|
|
|
|
|
{:noreply,
|
|
|
|
|
socket
|
|
|
|
|
|> assign(:cart_recovery_enabled, new_value)
|
|
|
|
|
|> put_flash(:info, message)}
|
|
|
|
|
end
|
|
|
|
|
|
2026-02-12 09:04:51 +00:00
|
|
|
# -- Events: Stripe --
|
|
|
|
|
|
2026-02-07 17:12:53 +00:00
|
|
|
def handle_event("connect_stripe", %{"stripe" => %{"api_key" => api_key}}, socket) do
|
|
|
|
|
if api_key == "" do
|
|
|
|
|
{:noreply, put_flash(socket, :error, "Please enter your Stripe secret key")}
|
|
|
|
|
else
|
|
|
|
|
socket = assign(socket, :connecting, true)
|
|
|
|
|
|
|
|
|
|
case StripeSetup.connect(api_key) do
|
|
|
|
|
{:ok, :webhook_created} ->
|
|
|
|
|
socket =
|
|
|
|
|
socket
|
|
|
|
|
|> assign_stripe_state()
|
|
|
|
|
|> put_flash(:info, "Stripe connected and webhook endpoint created")
|
|
|
|
|
|
|
|
|
|
{:noreply, socket}
|
|
|
|
|
|
|
|
|
|
{:ok, :localhost} ->
|
|
|
|
|
socket =
|
|
|
|
|
socket
|
|
|
|
|
|> assign_stripe_state()
|
|
|
|
|
|> put_flash(
|
|
|
|
|
:info,
|
|
|
|
|
"API key saved. Enter a webhook signing secret below for local testing."
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
{:noreply, socket}
|
|
|
|
|
|
|
|
|
|
{:error, message} ->
|
|
|
|
|
socket =
|
|
|
|
|
socket
|
|
|
|
|
|> assign(:connecting, false)
|
|
|
|
|
|> put_flash(:error, "Stripe connection failed: #{message}")
|
|
|
|
|
|
|
|
|
|
{:noreply, socket}
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def handle_event("disconnect_stripe", _params, socket) do
|
|
|
|
|
StripeSetup.disconnect()
|
|
|
|
|
|
|
|
|
|
socket =
|
|
|
|
|
socket
|
|
|
|
|
|> assign_stripe_state()
|
|
|
|
|
|> put_flash(:info, "Stripe disconnected")
|
|
|
|
|
|
|
|
|
|
{:noreply, socket}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def handle_event("save_signing_secret", %{"webhook" => %{"signing_secret" => secret}}, socket) do
|
|
|
|
|
if secret == "" do
|
|
|
|
|
{:noreply, put_flash(socket, :error, "Please enter a signing secret")}
|
|
|
|
|
else
|
|
|
|
|
StripeSetup.save_signing_secret(secret)
|
|
|
|
|
|
|
|
|
|
socket =
|
|
|
|
|
socket
|
|
|
|
|
|> assign_stripe_state()
|
|
|
|
|
|> put_flash(:info, "Webhook signing secret saved")
|
|
|
|
|
|
|
|
|
|
{:noreply, socket}
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2026-02-12 09:04:51 +00:00
|
|
|
def handle_event("toggle_stripe_advanced", _params, socket) do
|
|
|
|
|
{:noreply, assign(socket, :stripe_advanced_open, !socket.assigns.stripe_advanced_open)}
|
|
|
|
|
end
|
2026-02-11 22:58:58 +00:00
|
|
|
|
2026-02-12 09:04:51 +00:00
|
|
|
# -- Events: products --
|
|
|
|
|
|
|
|
|
|
def handle_event("sync", %{"id" => id}, socket) do
|
|
|
|
|
connection = Products.get_provider_connection!(id)
|
|
|
|
|
|
|
|
|
|
case Products.enqueue_sync(connection) do
|
|
|
|
|
{:ok, _job} ->
|
|
|
|
|
{:noreply,
|
|
|
|
|
socket
|
|
|
|
|
|> assign_products_state()
|
|
|
|
|
|> put_flash(:info, "Sync started for #{connection.name}")}
|
|
|
|
|
|
|
|
|
|
{:error, _reason} ->
|
|
|
|
|
{:noreply, put_flash(socket, :error, "Failed to start sync")}
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def handle_event("delete_connection", %{"id" => id}, socket) do
|
|
|
|
|
connection = Products.get_provider_connection!(id)
|
|
|
|
|
{:ok, _} = Products.delete_provider_connection(connection)
|
2026-02-11 22:58:58 +00:00
|
|
|
|
|
|
|
|
{:noreply,
|
|
|
|
|
socket
|
2026-02-12 09:04:51 +00:00
|
|
|
|> assign_products_state()
|
|
|
|
|
|> put_flash(:info, "Provider connection deleted")}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# -- Events: account --
|
|
|
|
|
|
|
|
|
|
def handle_event("validate_email", %{"user" => user_params}, socket) do
|
|
|
|
|
email_form =
|
|
|
|
|
socket.assigns.current_scope.user
|
|
|
|
|
|> Accounts.change_user_email(user_params, validate_unique: false)
|
|
|
|
|
|> Map.put(:action, :validate)
|
|
|
|
|
|> to_form()
|
|
|
|
|
|
|
|
|
|
{:noreply, assign(socket, email_form: email_form)}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def handle_event("update_email", %{"user" => user_params}, socket) do
|
|
|
|
|
user = socket.assigns.current_scope.user
|
|
|
|
|
|
|
|
|
|
unless Accounts.sudo_mode?(user) do
|
|
|
|
|
{:noreply,
|
|
|
|
|
socket
|
|
|
|
|
|> put_flash(:error, "Please log in again to change account settings.")
|
|
|
|
|
|> redirect(to: ~p"/users/log-in")}
|
|
|
|
|
else
|
|
|
|
|
case Accounts.change_user_email(user, user_params) do
|
|
|
|
|
%{valid?: true} = changeset ->
|
|
|
|
|
Accounts.deliver_user_update_email_instructions(
|
|
|
|
|
Ecto.Changeset.apply_action!(changeset, :insert),
|
|
|
|
|
user.email,
|
|
|
|
|
&url(~p"/users/settings/confirm-email/#{&1}")
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
info = "A link to confirm your email change has been sent to the new address."
|
|
|
|
|
{:noreply, put_flash(socket, :info, info)}
|
|
|
|
|
|
|
|
|
|
changeset ->
|
|
|
|
|
{:noreply, assign(socket, :email_form, to_form(changeset, action: :insert))}
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def handle_event("validate_password", %{"user" => user_params}, socket) do
|
|
|
|
|
password_form =
|
|
|
|
|
socket.assigns.current_scope.user
|
|
|
|
|
|> Accounts.change_user_password(user_params, hash_password: false)
|
|
|
|
|
|> Map.put(:action, :validate)
|
|
|
|
|
|> to_form()
|
|
|
|
|
|
|
|
|
|
{:noreply, assign(socket, password_form: password_form)}
|
2026-02-11 22:58:58 +00:00
|
|
|
end
|
|
|
|
|
|
2026-02-12 09:04:51 +00:00
|
|
|
def handle_event("update_password", %{"user" => user_params}, socket) do
|
|
|
|
|
user = socket.assigns.current_scope.user
|
|
|
|
|
|
|
|
|
|
unless Accounts.sudo_mode?(user) do
|
|
|
|
|
{:noreply,
|
|
|
|
|
socket
|
|
|
|
|
|> put_flash(:error, "Please log in again to change account settings.")
|
|
|
|
|
|> redirect(to: ~p"/users/log-in")}
|
|
|
|
|
else
|
|
|
|
|
case Accounts.change_user_password(user, user_params) do
|
|
|
|
|
%{valid?: true} = changeset ->
|
|
|
|
|
{:noreply, assign(socket, trigger_submit: true, password_form: to_form(changeset))}
|
|
|
|
|
|
|
|
|
|
changeset ->
|
|
|
|
|
{:noreply, assign(socket, password_form: to_form(changeset, action: :insert))}
|
|
|
|
|
end
|
|
|
|
|
end
|
2026-02-07 17:12:53 +00:00
|
|
|
end
|
|
|
|
|
|
2026-02-12 09:04:51 +00:00
|
|
|
# -- Render --
|
|
|
|
|
|
2026-02-07 17:12:53 +00:00
|
|
|
@impl true
|
|
|
|
|
def render(assigns) do
|
|
|
|
|
~H"""
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<div class="admin-settings">
|
2026-02-12 08:35:22 +00:00
|
|
|
<.header>
|
|
|
|
|
Settings
|
|
|
|
|
</.header>
|
|
|
|
|
|
2026-02-12 09:04:51 +00:00
|
|
|
<%!-- Shop status --%>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<section class="admin-section">
|
|
|
|
|
<div class="admin-section-header">
|
|
|
|
|
<h2 class="admin-section-title">Shop status</h2>
|
2026-02-12 08:35:22 +00:00
|
|
|
<%= if @site_live do %>
|
2026-02-12 09:04:51 +00:00
|
|
|
<.status_pill color="green">
|
2026-02-12 08:35:22 +00:00
|
|
|
<.icon name="hero-check-circle-mini" class="size-3" /> Live
|
2026-02-12 09:04:51 +00:00
|
|
|
</.status_pill>
|
2026-02-12 08:35:22 +00:00
|
|
|
<% else %>
|
2026-02-12 09:04:51 +00:00
|
|
|
<.status_pill color="zinc">Offline</.status_pill>
|
2026-02-12 08:35:22 +00:00
|
|
|
<% end %>
|
|
|
|
|
</div>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<p class="admin-section-desc">
|
2026-02-12 08:35:22 +00:00
|
|
|
<%= if @site_live do %>
|
|
|
|
|
Your shop is visible to the public.
|
|
|
|
|
<% else %>
|
|
|
|
|
Your shop is offline. Visitors see a "coming soon" page.
|
|
|
|
|
<% end %>
|
|
|
|
|
</p>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<div class="admin-section-body">
|
2026-02-12 08:35:22 +00:00
|
|
|
<button
|
|
|
|
|
phx-click="toggle_site_live"
|
|
|
|
|
class={[
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
"admin-btn admin-btn-sm",
|
|
|
|
|
if(@site_live, do: "admin-btn-outline", else: "admin-btn-primary")
|
2026-02-12 08:35:22 +00:00
|
|
|
]}
|
|
|
|
|
>
|
2026-02-11 22:58:58 +00:00
|
|
|
<%= if @site_live do %>
|
2026-02-12 08:35:22 +00:00
|
|
|
<.icon name="hero-eye-slash-mini" class="size-4" /> Take offline
|
|
|
|
|
<% else %>
|
|
|
|
|
<.icon name="hero-eye-mini" class="size-4" /> Go live
|
|
|
|
|
<% end %>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
|
2026-02-12 09:04:51 +00:00
|
|
|
<%!-- Payments --%>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<section class="admin-section">
|
|
|
|
|
<div class="admin-section-header">
|
|
|
|
|
<h2 class="admin-section-title">Payments</h2>
|
2026-02-12 08:35:22 +00:00
|
|
|
<%= case @stripe_status do %>
|
|
|
|
|
<% :connected -> %>
|
2026-02-12 09:04:51 +00:00
|
|
|
<.status_pill color="green">
|
2026-02-12 08:35:22 +00:00
|
|
|
<.icon name="hero-check-circle-mini" class="size-3" /> Connected
|
2026-02-12 09:04:51 +00:00
|
|
|
</.status_pill>
|
2026-02-12 08:35:22 +00:00
|
|
|
<% :connected_localhost -> %>
|
2026-02-12 09:04:51 +00:00
|
|
|
<.status_pill color="amber">
|
2026-02-12 08:35:22 +00:00
|
|
|
<.icon name="hero-exclamation-triangle-mini" class="size-3" /> Dev mode
|
2026-02-12 09:04:51 +00:00
|
|
|
</.status_pill>
|
2026-02-12 08:35:22 +00:00
|
|
|
<% :not_configured -> %>
|
2026-02-12 09:04:51 +00:00
|
|
|
<.status_pill color="zinc">Not connected</.status_pill>
|
2026-02-07 17:12:53 +00:00
|
|
|
<% end %>
|
2026-02-12 08:35:22 +00:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<%= if @stripe_status == :not_configured do %>
|
|
|
|
|
<.stripe_setup_form connect_form={@connect_form} connecting={@connecting} />
|
|
|
|
|
<% else %>
|
|
|
|
|
<.stripe_connected_view
|
|
|
|
|
stripe_status={@stripe_status}
|
|
|
|
|
stripe_api_key_hint={@stripe_api_key_hint}
|
|
|
|
|
stripe_webhook_url={@stripe_webhook_url}
|
|
|
|
|
stripe_signing_secret_hint={@stripe_signing_secret_hint}
|
|
|
|
|
stripe_has_signing_secret={@stripe_has_signing_secret}
|
|
|
|
|
secret_form={@secret_form}
|
2026-02-12 09:04:51 +00:00
|
|
|
advanced_open={@stripe_advanced_open}
|
2026-02-12 08:35:22 +00:00
|
|
|
/>
|
|
|
|
|
<% end %>
|
|
|
|
|
</section>
|
2026-02-12 09:04:51 +00:00
|
|
|
|
|
|
|
|
<%!-- Products --%>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<section class="admin-section">
|
|
|
|
|
<div class="admin-section-header">
|
|
|
|
|
<h2 class="admin-section-title">Products</h2>
|
2026-02-15 10:53:15 +00:00
|
|
|
<%= if @provider do %>
|
2026-02-12 09:04:51 +00:00
|
|
|
<.status_pill color="green">
|
|
|
|
|
<.icon name="hero-check-circle-mini" class="size-3" /> Connected
|
|
|
|
|
</.status_pill>
|
|
|
|
|
<% else %>
|
|
|
|
|
<.status_pill color="zinc">Not connected</.status_pill>
|
|
|
|
|
<% end %>
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-02-15 10:53:15 +00:00
|
|
|
<%= if @provider do %>
|
|
|
|
|
<.provider_connected provider={@provider} />
|
2026-02-12 09:04:51 +00:00
|
|
|
<% else %>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<div class="admin-section-body">
|
|
|
|
|
<p class="admin-section-desc" style="margin-top: 0;">
|
2026-02-12 09:04:51 +00:00
|
|
|
Connect a print-on-demand provider to import products into your shop.
|
|
|
|
|
</p>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<div class="admin-section-body">
|
|
|
|
|
<.link navigate={~p"/admin/providers"} class="admin-btn admin-btn-primary admin-btn-sm">
|
2026-02-15 10:53:15 +00:00
|
|
|
<.icon name="hero-plus-mini" class="size-4" /> Connect a provider
|
2026-02-12 09:04:51 +00:00
|
|
|
</.link>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<% end %>
|
|
|
|
|
</section>
|
|
|
|
|
|
add abandoned cart recovery
When a Stripe checkout session expires without payment, if the customer
entered their email, we record an AbandonedCart and schedule a single
plain-text recovery email (1h delay via Oban).
Privacy design:
- feature is off by default; shop owner opts in via admin settings
- only contacts customers who entered their email at Stripe checkout
- single email, never more (emailed_at timestamp gate)
- suppression list blocks repeat contact; one-click unsubscribe via
signed token (/unsubscribe/:token)
- records pruned after 30 days (nightly Oban cron)
- no tracking pixels, no redirected links, no HTML
Legal notes:
- custom_text added to Stripe session footer when recovery is on
- UK PECR soft opt-in; EU legitimate interests both satisfied by this design
Files:
- migration: abandoned_carts + email_suppressions tables
- schemas: AbandonedCart, EmailSuppression
- context: Orders.create_abandoned_cart, check_suppression, add_suppression,
has_recent_paid_order?, get_abandoned_cart_by_session, mark_abandoned_cart_emailed
- workers: AbandonedCartEmailWorker (checkout queue), AbandonedCartPruneWorker (cron)
- notifier: OrderNotifier.deliver_cart_recovery/3
- webhook: extended checkout.session.expired handler
- controller: UnsubscribeController, admin settings toggle
- tests: 28 new tests across context, workers, and controller
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 10:02:37 +00:00
|
|
|
<%!-- Cart recovery --%>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<section class="admin-section">
|
|
|
|
|
<div class="admin-section-header">
|
|
|
|
|
<h2 class="admin-section-title">Cart recovery</h2>
|
add abandoned cart recovery
When a Stripe checkout session expires without payment, if the customer
entered their email, we record an AbandonedCart and schedule a single
plain-text recovery email (1h delay via Oban).
Privacy design:
- feature is off by default; shop owner opts in via admin settings
- only contacts customers who entered their email at Stripe checkout
- single email, never more (emailed_at timestamp gate)
- suppression list blocks repeat contact; one-click unsubscribe via
signed token (/unsubscribe/:token)
- records pruned after 30 days (nightly Oban cron)
- no tracking pixels, no redirected links, no HTML
Legal notes:
- custom_text added to Stripe session footer when recovery is on
- UK PECR soft opt-in; EU legitimate interests both satisfied by this design
Files:
- migration: abandoned_carts + email_suppressions tables
- schemas: AbandonedCart, EmailSuppression
- context: Orders.create_abandoned_cart, check_suppression, add_suppression,
has_recent_paid_order?, get_abandoned_cart_by_session, mark_abandoned_cart_emailed
- workers: AbandonedCartEmailWorker (checkout queue), AbandonedCartPruneWorker (cron)
- notifier: OrderNotifier.deliver_cart_recovery/3
- webhook: extended checkout.session.expired handler
- controller: UnsubscribeController, admin settings toggle
- tests: 28 new tests across context, workers, and controller
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 10:02:37 +00:00
|
|
|
<%= if @cart_recovery_enabled do %>
|
|
|
|
|
<.status_pill color="green">
|
|
|
|
|
<.icon name="hero-check-circle-mini" class="size-3" /> On
|
|
|
|
|
</.status_pill>
|
|
|
|
|
<% else %>
|
|
|
|
|
<.status_pill color="zinc">Off</.status_pill>
|
|
|
|
|
<% end %>
|
|
|
|
|
</div>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<p class="admin-section-desc">
|
add abandoned cart recovery
When a Stripe checkout session expires without payment, if the customer
entered their email, we record an AbandonedCart and schedule a single
plain-text recovery email (1h delay via Oban).
Privacy design:
- feature is off by default; shop owner opts in via admin settings
- only contacts customers who entered their email at Stripe checkout
- single email, never more (emailed_at timestamp gate)
- suppression list blocks repeat contact; one-click unsubscribe via
signed token (/unsubscribe/:token)
- records pruned after 30 days (nightly Oban cron)
- no tracking pixels, no redirected links, no HTML
Legal notes:
- custom_text added to Stripe session footer when recovery is on
- UK PECR soft opt-in; EU legitimate interests both satisfied by this design
Files:
- migration: abandoned_carts + email_suppressions tables
- schemas: AbandonedCart, EmailSuppression
- context: Orders.create_abandoned_cart, check_suppression, add_suppression,
has_recent_paid_order?, get_abandoned_cart_by_session, mark_abandoned_cart_emailed
- workers: AbandonedCartEmailWorker (checkout queue), AbandonedCartPruneWorker (cron)
- notifier: OrderNotifier.deliver_cart_recovery/3
- webhook: extended checkout.session.expired handler
- controller: UnsubscribeController, admin settings toggle
- tests: 28 new tests across context, workers, and controller
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 10:02:37 +00:00
|
|
|
When on, customers who entered their email at Stripe checkout but didn't complete
|
|
|
|
|
payment receive a single plain-text recovery email one hour later.
|
|
|
|
|
No tracking pixels. One email, never more.
|
|
|
|
|
</p>
|
|
|
|
|
<%= if @cart_recovery_enabled do %>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<p class="admin-section-desc" style="color: #b45309;">
|
add abandoned cart recovery
When a Stripe checkout session expires without payment, if the customer
entered their email, we record an AbandonedCart and schedule a single
plain-text recovery email (1h delay via Oban).
Privacy design:
- feature is off by default; shop owner opts in via admin settings
- only contacts customers who entered their email at Stripe checkout
- single email, never more (emailed_at timestamp gate)
- suppression list blocks repeat contact; one-click unsubscribe via
signed token (/unsubscribe/:token)
- records pruned after 30 days (nightly Oban cron)
- no tracking pixels, no redirected links, no HTML
Legal notes:
- custom_text added to Stripe session footer when recovery is on
- UK PECR soft opt-in; EU legitimate interests both satisfied by this design
Files:
- migration: abandoned_carts + email_suppressions tables
- schemas: AbandonedCart, EmailSuppression
- context: Orders.create_abandoned_cart, check_suppression, add_suppression,
has_recent_paid_order?, get_abandoned_cart_by_session, mark_abandoned_cart_emailed
- workers: AbandonedCartEmailWorker (checkout queue), AbandonedCartPruneWorker (cron)
- notifier: OrderNotifier.deliver_cart_recovery/3
- webhook: extended checkout.session.expired handler
- controller: UnsubscribeController, admin settings toggle
- tests: 28 new tests across context, workers, and controller
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 10:02:37 +00:00
|
|
|
Make sure your privacy policy mentions that a single recovery email may be sent,
|
|
|
|
|
and that customers can unsubscribe at any time.
|
|
|
|
|
</p>
|
|
|
|
|
<% end %>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<div class="admin-section-body">
|
add abandoned cart recovery
When a Stripe checkout session expires without payment, if the customer
entered their email, we record an AbandonedCart and schedule a single
plain-text recovery email (1h delay via Oban).
Privacy design:
- feature is off by default; shop owner opts in via admin settings
- only contacts customers who entered their email at Stripe checkout
- single email, never more (emailed_at timestamp gate)
- suppression list blocks repeat contact; one-click unsubscribe via
signed token (/unsubscribe/:token)
- records pruned after 30 days (nightly Oban cron)
- no tracking pixels, no redirected links, no HTML
Legal notes:
- custom_text added to Stripe session footer when recovery is on
- UK PECR soft opt-in; EU legitimate interests both satisfied by this design
Files:
- migration: abandoned_carts + email_suppressions tables
- schemas: AbandonedCart, EmailSuppression
- context: Orders.create_abandoned_cart, check_suppression, add_suppression,
has_recent_paid_order?, get_abandoned_cart_by_session, mark_abandoned_cart_emailed
- workers: AbandonedCartEmailWorker (checkout queue), AbandonedCartPruneWorker (cron)
- notifier: OrderNotifier.deliver_cart_recovery/3
- webhook: extended checkout.session.expired handler
- controller: UnsubscribeController, admin settings toggle
- tests: 28 new tests across context, workers, and controller
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 10:02:37 +00:00
|
|
|
<button
|
|
|
|
|
phx-click="toggle_cart_recovery"
|
|
|
|
|
class={[
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
"admin-btn admin-btn-sm",
|
|
|
|
|
if(@cart_recovery_enabled, do: "admin-btn-outline", else: "admin-btn-primary")
|
add abandoned cart recovery
When a Stripe checkout session expires without payment, if the customer
entered their email, we record an AbandonedCart and schedule a single
plain-text recovery email (1h delay via Oban).
Privacy design:
- feature is off by default; shop owner opts in via admin settings
- only contacts customers who entered their email at Stripe checkout
- single email, never more (emailed_at timestamp gate)
- suppression list blocks repeat contact; one-click unsubscribe via
signed token (/unsubscribe/:token)
- records pruned after 30 days (nightly Oban cron)
- no tracking pixels, no redirected links, no HTML
Legal notes:
- custom_text added to Stripe session footer when recovery is on
- UK PECR soft opt-in; EU legitimate interests both satisfied by this design
Files:
- migration: abandoned_carts + email_suppressions tables
- schemas: AbandonedCart, EmailSuppression
- context: Orders.create_abandoned_cart, check_suppression, add_suppression,
has_recent_paid_order?, get_abandoned_cart_by_session, mark_abandoned_cart_emailed
- workers: AbandonedCartEmailWorker (checkout queue), AbandonedCartPruneWorker (cron)
- notifier: OrderNotifier.deliver_cart_recovery/3
- webhook: extended checkout.session.expired handler
- controller: UnsubscribeController, admin settings toggle
- tests: 28 new tests across context, workers, and controller
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 10:02:37 +00:00
|
|
|
]}
|
|
|
|
|
>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
{if @cart_recovery_enabled, do: "Turn off", else: "Turn on"}
|
add abandoned cart recovery
When a Stripe checkout session expires without payment, if the customer
entered their email, we record an AbandonedCart and schedule a single
plain-text recovery email (1h delay via Oban).
Privacy design:
- feature is off by default; shop owner opts in via admin settings
- only contacts customers who entered their email at Stripe checkout
- single email, never more (emailed_at timestamp gate)
- suppression list blocks repeat contact; one-click unsubscribe via
signed token (/unsubscribe/:token)
- records pruned after 30 days (nightly Oban cron)
- no tracking pixels, no redirected links, no HTML
Legal notes:
- custom_text added to Stripe session footer when recovery is on
- UK PECR soft opt-in; EU legitimate interests both satisfied by this design
Files:
- migration: abandoned_carts + email_suppressions tables
- schemas: AbandonedCart, EmailSuppression
- context: Orders.create_abandoned_cart, check_suppression, add_suppression,
has_recent_paid_order?, get_abandoned_cart_by_session, mark_abandoned_cart_emailed
- workers: AbandonedCartEmailWorker (checkout queue), AbandonedCartPruneWorker (cron)
- notifier: OrderNotifier.deliver_cart_recovery/3
- webhook: extended checkout.session.expired handler
- controller: UnsubscribeController, admin settings toggle
- tests: 28 new tests across context, workers, and controller
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 10:02:37 +00:00
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
|
2026-02-12 09:04:51 +00:00
|
|
|
<%!-- Account --%>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<section class="admin-section">
|
|
|
|
|
<h2 class="admin-section-title">Account</h2>
|
2026-02-12 09:04:51 +00:00
|
|
|
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<div class="admin-stack" style="margin-top: 1rem; --admin-stack-gap: 1.5rem;">
|
2026-02-12 09:04:51 +00:00
|
|
|
<.form
|
|
|
|
|
for={@email_form}
|
|
|
|
|
id="email_form"
|
|
|
|
|
phx-submit="update_email"
|
|
|
|
|
phx-change="validate_email"
|
|
|
|
|
>
|
|
|
|
|
<.input
|
|
|
|
|
field={@email_form[:email]}
|
|
|
|
|
type="email"
|
|
|
|
|
label="Email"
|
|
|
|
|
autocomplete="username"
|
|
|
|
|
required
|
|
|
|
|
/>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<div style="margin-top: 0.75rem;">
|
2026-02-12 09:04:51 +00:00
|
|
|
<.button phx-disable-with="Saving...">Change email</.button>
|
|
|
|
|
</div>
|
|
|
|
|
</.form>
|
|
|
|
|
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<div style="border-top: 1px solid var(--t-surface-sunken); padding-top: 1.5rem;">
|
2026-02-12 09:04:51 +00:00
|
|
|
<.form
|
|
|
|
|
for={@password_form}
|
|
|
|
|
id="password_form"
|
|
|
|
|
action={~p"/users/update-password"}
|
|
|
|
|
method="post"
|
|
|
|
|
phx-change="validate_password"
|
|
|
|
|
phx-submit="update_password"
|
|
|
|
|
phx-trigger-action={@trigger_submit}
|
|
|
|
|
>
|
|
|
|
|
<input
|
|
|
|
|
name={@password_form[:email].name}
|
|
|
|
|
type="hidden"
|
|
|
|
|
id="hidden_user_email"
|
|
|
|
|
autocomplete="username"
|
|
|
|
|
value={@current_email}
|
|
|
|
|
/>
|
|
|
|
|
<.input
|
|
|
|
|
field={@password_form[:password]}
|
|
|
|
|
type="password"
|
|
|
|
|
label="New password"
|
|
|
|
|
autocomplete="new-password"
|
|
|
|
|
required
|
|
|
|
|
/>
|
|
|
|
|
<.input
|
|
|
|
|
field={@password_form[:password_confirmation]}
|
|
|
|
|
type="password"
|
|
|
|
|
label="Confirm new password"
|
|
|
|
|
autocomplete="new-password"
|
|
|
|
|
/>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<div style="margin-top: 0.75rem;">
|
2026-02-12 09:04:51 +00:00
|
|
|
<.button phx-disable-with="Saving...">Change password</.button>
|
|
|
|
|
</div>
|
|
|
|
|
</.form>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<%!-- Advanced --%>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<section class="admin-section" style="padding-bottom: 2.5rem;">
|
|
|
|
|
<h2 class="admin-section-title">Advanced</h2>
|
2026-02-12 09:04:51 +00:00
|
|
|
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<div class="admin-stack" style="margin-top: 1rem; --admin-stack-gap: 0.5rem;">
|
|
|
|
|
<.link href={~p"/admin/dashboard"} class="admin-link-subtle">
|
2026-02-12 09:04:51 +00:00
|
|
|
<.icon name="hero-chart-bar" class="size-4 inline" /> System dashboard
|
|
|
|
|
</.link>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<.link href={~p"/admin/errors"} class="admin-link-subtle">
|
2026-02-12 09:04:51 +00:00
|
|
|
<.icon name="hero-bug-ant" class="size-4 inline" /> Error tracker
|
|
|
|
|
</.link>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
</div>
|
|
|
|
|
"""
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# -- Function components --
|
|
|
|
|
|
|
|
|
|
attr :color, :string, required: true
|
|
|
|
|
slot :inner_block, required: true
|
|
|
|
|
|
|
|
|
|
defp status_pill(assigns) do
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
modifier =
|
2026-02-12 09:04:51 +00:00
|
|
|
case assigns.color do
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
"green" -> "admin-status-pill-green"
|
|
|
|
|
"amber" -> "admin-status-pill-amber"
|
|
|
|
|
_ -> "admin-status-pill-zinc"
|
2026-02-12 09:04:51 +00:00
|
|
|
end
|
|
|
|
|
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
assigns = assign(assigns, :modifier, modifier)
|
2026-02-12 09:04:51 +00:00
|
|
|
|
|
|
|
|
~H"""
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<span class={["admin-status-pill", @modifier]}>
|
2026-02-12 09:04:51 +00:00
|
|
|
{render_slot(@inner_block)}
|
|
|
|
|
</span>
|
|
|
|
|
"""
|
|
|
|
|
end
|
|
|
|
|
|
2026-02-15 10:53:15 +00:00
|
|
|
attr :provider, :map, required: true
|
2026-02-12 09:04:51 +00:00
|
|
|
|
2026-02-15 10:53:15 +00:00
|
|
|
defp provider_connected(assigns) do
|
|
|
|
|
conn = assigns.provider.connection
|
2026-02-12 09:04:51 +00:00
|
|
|
|
|
|
|
|
assigns =
|
|
|
|
|
assigns
|
|
|
|
|
|> assign(:connection, conn)
|
2026-02-15 10:53:15 +00:00
|
|
|
|> assign(:product_count, assigns.provider.product_count)
|
2026-02-12 09:04:51 +00:00
|
|
|
|> assign(:syncing, conn.sync_status == "syncing")
|
2026-02-15 10:53:15 +00:00
|
|
|
|> assign(:provider_label, String.capitalize(conn.provider_type))
|
2026-02-12 09:04:51 +00:00
|
|
|
|
|
|
|
|
~H"""
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<div class="admin-section-body">
|
|
|
|
|
<dl class="admin-dl">
|
|
|
|
|
<div class="admin-dl-row">
|
|
|
|
|
<dt class="admin-dl-term">Provider</dt>
|
|
|
|
|
<dd class="admin-dl-value">{@provider_label}</dd>
|
2026-02-12 09:04:51 +00:00
|
|
|
</div>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<div class="admin-dl-row">
|
|
|
|
|
<dt class="admin-dl-term">Shop</dt>
|
|
|
|
|
<dd class="admin-dl-value">{@connection.name}</dd>
|
2026-02-12 09:04:51 +00:00
|
|
|
</div>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<div class="admin-dl-row">
|
|
|
|
|
<dt class="admin-dl-term">Products</dt>
|
|
|
|
|
<dd class="admin-dl-value">{@product_count}</dd>
|
2026-02-12 09:04:51 +00:00
|
|
|
</div>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<div class="admin-dl-row">
|
|
|
|
|
<dt class="admin-dl-term">Last synced</dt>
|
|
|
|
|
<dd class="admin-dl-value">
|
2026-02-12 09:04:51 +00:00
|
|
|
<%= if @connection.last_synced_at do %>
|
|
|
|
|
{format_relative_time(@connection.last_synced_at)}
|
|
|
|
|
<% else %>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<span style="color: #d97706;">Never</span>
|
2026-02-12 09:04:51 +00:00
|
|
|
<% end %>
|
|
|
|
|
</dd>
|
|
|
|
|
</div>
|
|
|
|
|
</dl>
|
|
|
|
|
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<div class="admin-cluster" style="margin-top: 1rem;">
|
2026-02-12 09:04:51 +00:00
|
|
|
<button
|
|
|
|
|
phx-click="sync"
|
|
|
|
|
phx-value-id={@connection.id}
|
|
|
|
|
disabled={@syncing}
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
class="admin-btn admin-btn-outline admin-btn-sm"
|
2026-02-12 09:04:51 +00:00
|
|
|
>
|
|
|
|
|
<.icon
|
|
|
|
|
name="hero-arrow-path"
|
|
|
|
|
class={if @syncing, do: "size-4 animate-spin", else: "size-4"}
|
|
|
|
|
/>
|
|
|
|
|
{if @syncing, do: "Syncing...", else: "Sync products"}
|
|
|
|
|
</button>
|
|
|
|
|
<.link
|
|
|
|
|
navigate={~p"/admin/providers/#{@connection.id}/edit"}
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
class="admin-btn admin-btn-outline admin-btn-sm"
|
2026-02-12 09:04:51 +00:00
|
|
|
>
|
|
|
|
|
<.icon name="hero-cog-6-tooth" class="size-4" /> Settings
|
|
|
|
|
</.link>
|
|
|
|
|
<button
|
|
|
|
|
phx-click="delete_connection"
|
|
|
|
|
phx-value-id={@connection.id}
|
2026-02-15 10:53:15 +00:00
|
|
|
data-confirm={"Disconnect from #{@provider_label}? Your synced products will remain in your shop."}
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
class="admin-link-danger"
|
|
|
|
|
style="padding: 0.375rem 0.5rem;"
|
2026-02-12 09:04:51 +00:00
|
|
|
>
|
|
|
|
|
Disconnect
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
2026-02-12 08:35:22 +00:00
|
|
|
</div>
|
2026-02-07 17:12:53 +00:00
|
|
|
"""
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
defp stripe_setup_form(assigns) do
|
|
|
|
|
~H"""
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<div class="admin-section-body">
|
|
|
|
|
<p class="admin-section-desc" style="margin-top: 0;">
|
2026-02-07 17:12:53 +00:00
|
|
|
To accept payments, connect your Stripe account by entering your secret key.
|
|
|
|
|
You can find it in your
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<a href="https://dashboard.stripe.com/apikeys" target="_blank" rel="noopener" class="admin-link">
|
2026-02-07 17:12:53 +00:00
|
|
|
Stripe dashboard
|
|
|
|
|
</a>
|
|
|
|
|
under Developers → API keys.
|
|
|
|
|
</p>
|
|
|
|
|
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<.form for={@connect_form} phx-submit="connect_stripe" style="margin-top: 1.5rem;">
|
2026-02-07 17:12:53 +00:00
|
|
|
<.input
|
|
|
|
|
field={@connect_form[:api_key]}
|
|
|
|
|
type="password"
|
|
|
|
|
label="Secret key"
|
|
|
|
|
autocomplete="off"
|
|
|
|
|
placeholder="sk_test_... or sk_live_..."
|
|
|
|
|
/>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<p class="admin-text-tertiary" style="margin-top: 0.25rem;">
|
2026-02-07 17:12:53 +00:00
|
|
|
Starts with <code>sk_test_</code> (test mode) or <code>sk_live_</code> (live mode).
|
|
|
|
|
This key is encrypted at rest in the database.
|
|
|
|
|
</p>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<div class="admin-section-body">
|
2026-02-07 17:12:53 +00:00
|
|
|
<.button phx-disable-with="Connecting...">
|
|
|
|
|
Connect Stripe
|
|
|
|
|
</.button>
|
|
|
|
|
</div>
|
|
|
|
|
</.form>
|
|
|
|
|
</div>
|
|
|
|
|
"""
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
defp stripe_connected_view(assigns) do
|
|
|
|
|
~H"""
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<div class="admin-stack" style="margin-top: 1rem;">
|
|
|
|
|
<dl class="admin-dl">
|
|
|
|
|
<div class="admin-dl-row">
|
|
|
|
|
<dt class="admin-dl-term">API key</dt>
|
|
|
|
|
<dd class="admin-dl-value"><code>{@stripe_api_key_hint}</code></dd>
|
2026-02-07 17:12:53 +00:00
|
|
|
</div>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<div class="admin-dl-row">
|
|
|
|
|
<dt class="admin-dl-term">Webhook URL</dt>
|
|
|
|
|
<dd class="admin-dl-value"><code style="font-size: 0.75rem; word-break: break-all;">{@stripe_webhook_url}</code></dd>
|
2026-02-07 17:12:53 +00:00
|
|
|
</div>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<div class="admin-dl-row">
|
|
|
|
|
<dt class="admin-dl-term">Webhook secret</dt>
|
|
|
|
|
<dd class="admin-dl-value">
|
2026-02-07 17:12:53 +00:00
|
|
|
<%= if @stripe_has_signing_secret do %>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<code>{@stripe_signing_secret_hint}</code>
|
2026-02-07 17:12:53 +00:00
|
|
|
<% else %>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<span style="color: #d97706;">Not set</span>
|
2026-02-07 17:12:53 +00:00
|
|
|
<% end %>
|
|
|
|
|
</dd>
|
|
|
|
|
</div>
|
|
|
|
|
</dl>
|
|
|
|
|
|
|
|
|
|
<%= if @stripe_status == :connected_localhost do %>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<div class="admin-info-box admin-info-box-amber">
|
|
|
|
|
<p>
|
2026-02-07 17:12:53 +00:00
|
|
|
Stripe can't reach localhost for webhooks. For local testing, run the Stripe CLI:
|
|
|
|
|
</p>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<pre>stripe listen --forward-to localhost:4000/webhooks/stripe</pre>
|
|
|
|
|
<p style="margin-top: 0.5rem; font-size: 0.75rem; color: #b45309;">
|
2026-02-07 17:12:53 +00:00
|
|
|
The CLI will output a signing secret starting with <code>whsec_</code>. Enter it below.
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<.form for={@secret_form} phx-submit="save_signing_secret" style="margin-top: 0.5rem;">
|
2026-02-07 17:12:53 +00:00
|
|
|
<.input
|
|
|
|
|
field={@secret_form[:signing_secret]}
|
|
|
|
|
type="password"
|
|
|
|
|
label="Webhook signing secret"
|
|
|
|
|
autocomplete="off"
|
|
|
|
|
placeholder="whsec_..."
|
|
|
|
|
/>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<div style="margin-top: 0.75rem;">
|
2026-02-07 17:12:53 +00:00
|
|
|
<.button phx-disable-with="Saving...">Save signing secret</.button>
|
|
|
|
|
</div>
|
|
|
|
|
</.form>
|
|
|
|
|
<% else %>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<div style="border-top: 1px solid var(--t-surface-sunken); padding-top: 0.75rem;">
|
|
|
|
|
<button phx-click="toggle_stripe_advanced" class="admin-link-subtle admin-row" style="--admin-row-gap: 0.25rem;">
|
2026-02-07 17:12:53 +00:00
|
|
|
<.icon
|
|
|
|
|
name={if @advanced_open, do: "hero-chevron-down-mini", else: "hero-chevron-right-mini"}
|
|
|
|
|
class="size-4"
|
|
|
|
|
/> Advanced
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
<%= if @advanced_open do %>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<div style="margin-top: 0.75rem;">
|
|
|
|
|
<p class="admin-text-tertiary" style="margin-bottom: 0.75rem;">
|
2026-02-07 17:12:53 +00:00
|
|
|
Override the webhook signing secret if you need to use a custom endpoint or the Stripe CLI.
|
|
|
|
|
</p>
|
|
|
|
|
<.form for={@secret_form} phx-submit="save_signing_secret">
|
|
|
|
|
<.input
|
|
|
|
|
field={@secret_form[:signing_secret]}
|
|
|
|
|
type="password"
|
|
|
|
|
label="Webhook signing secret"
|
|
|
|
|
autocomplete="off"
|
|
|
|
|
placeholder="whsec_..."
|
|
|
|
|
/>
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<div style="margin-top: 0.75rem;">
|
2026-02-07 17:12:53 +00:00
|
|
|
<.button phx-disable-with="Saving...">Save signing secret</.button>
|
|
|
|
|
</div>
|
|
|
|
|
</.form>
|
|
|
|
|
</div>
|
|
|
|
|
<% end %>
|
|
|
|
|
</div>
|
|
|
|
|
<% end %>
|
|
|
|
|
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
<div style="border-top: 1px solid var(--t-surface-sunken); padding-top: 1rem;">
|
2026-02-07 17:12:53 +00:00
|
|
|
<button
|
|
|
|
|
phx-click="disconnect_stripe"
|
|
|
|
|
data-confirm="This will remove your Stripe API key and delete the webhook endpoint. Are you sure?"
|
refactor admin CSS: replace utility classes with semantic styles
Replace Tailwind utility soup across admin templates with semantic
CSS classes. Add layout primitives (stack, row, cluster, grid),
extract JS transition helpers into transitions.css, and refactor
core_components, layouts, settings, newsletter, order_show, providers,
and theme editor templates.
Utility occurrences reduced from 290+ to 127 across admin files.
All 1679 tests pass.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:15:25 +00:00
|
|
|
class="admin-link-danger"
|
2026-02-07 17:12:53 +00:00
|
|
|
>
|
|
|
|
|
Disconnect Stripe
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
"""
|
|
|
|
|
end
|
2026-02-12 09:04:51 +00:00
|
|
|
|
|
|
|
|
defp format_relative_time(datetime) do
|
|
|
|
|
diff = DateTime.diff(DateTime.utc_now(), datetime, :second)
|
|
|
|
|
|
|
|
|
|
cond do
|
|
|
|
|
diff < 60 -> "just now"
|
|
|
|
|
diff < 3600 -> "#{div(diff, 60)} min ago"
|
|
|
|
|
diff < 86400 -> "#{div(diff, 3600)} hours ago"
|
|
|
|
|
true -> "#{div(diff, 86400)} days ago"
|
|
|
|
|
end
|
|
|
|
|
end
|
2026-02-07 17:12:53 +00:00
|
|
|
end
|