feat: add admin provider setup UI with improved product sync
- Add /admin/providers LiveView for connecting and managing POD providers - Implement pagination for Printify API (handles all products, not just first page) - Add parallel processing (5 concurrent) for faster product sync - Add slug-based fallback matching when provider_product_id changes - Add error recovery with try/rescue to prevent stuck sync status - Add checksum-based change detection to skip unchanged products - Add upsert tests covering race conditions and slug matching - Add Printify provider tests - Document Printify integration research (product identity, order risks, open source vs managed hosting implications) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -469,4 +469,68 @@ defmodule SimpleshopThemeWeb.CoreComponents do
|
||||
def translate_errors(errors, field) when is_list(errors) do
|
||||
for {^field, {msg, opts}} <- errors, do: translate_error({msg, opts})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Renders a modal dialog.
|
||||
|
||||
Uses daisyUI's modal component with proper accessibility.
|
||||
|
||||
## Examples
|
||||
|
||||
<.modal id="confirm-modal">
|
||||
Are you sure?
|
||||
<:actions>
|
||||
<button class="btn">Cancel</button>
|
||||
<button class="btn btn-primary">Confirm</button>
|
||||
</:actions>
|
||||
</.modal>
|
||||
|
||||
"""
|
||||
attr :id, :string, required: true
|
||||
attr :show, :boolean, default: false
|
||||
attr :on_cancel, JS, default: %JS{}
|
||||
|
||||
slot :inner_block, required: true
|
||||
slot :actions
|
||||
|
||||
def modal(assigns) do
|
||||
~H"""
|
||||
<dialog
|
||||
id={@id}
|
||||
class="modal"
|
||||
phx-mounted={@show && show_modal(@id)}
|
||||
phx-remove={hide_modal(@id)}
|
||||
>
|
||||
<div class="modal-box max-w-lg">
|
||||
<form method="dialog">
|
||||
<button
|
||||
class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"
|
||||
phx-click={@on_cancel}
|
||||
aria-label={gettext("close")}
|
||||
>
|
||||
<.icon name="hero-x-mark" class="size-5" />
|
||||
</button>
|
||||
</form>
|
||||
{render_slot(@inner_block)}
|
||||
<div :if={@actions != []} class="modal-action">
|
||||
{render_slot(@actions)}
|
||||
</div>
|
||||
</div>
|
||||
<form method="dialog" class="modal-backdrop">
|
||||
<button phx-click={@on_cancel}>close</button>
|
||||
</form>
|
||||
</dialog>
|
||||
"""
|
||||
end
|
||||
|
||||
def show_modal(js \\ %JS{}, id) when is_binary(id) do
|
||||
js
|
||||
|> JS.exec("showModal()", to: "##{id}")
|
||||
end
|
||||
|
||||
def hide_modal(js \\ %JS{}, id) when is_binary(id) do
|
||||
js
|
||||
|> JS.exec("close()", to: "##{id}")
|
||||
|> JS.pop_focus()
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user