add block previews, picker thumbnails and newsletter settings
All checks were successful
deploy / deploy (push) Successful in 1m30s
All checks were successful
deploy / deploy (push) Successful in 1m30s
Block cards now show a one-line content summary below the name. Block picker items include SVG wireframe thumbnails. Newsletter block marked as decorative with configurable title/description and form submission prevented on the shop side. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,7 @@ defmodule BerrypodWeb.BlockEditorComponents do
|
||||
import BerrypodWeb.CoreComponents, only: [icon: 1]
|
||||
|
||||
alias Berrypod.Pages.{BlockEditor, BlockTypes}
|
||||
alias BerrypodWeb.BlockThumbnails
|
||||
|
||||
# ── Block card ─────────────────────────────────────────────────
|
||||
|
||||
@@ -28,12 +29,14 @@ defmodule BerrypodWeb.BlockEditorComponents do
|
||||
block_type = BlockTypes.get(assigns.block["type"])
|
||||
has_settings = BlockEditor.has_settings?(assigns.block)
|
||||
expanded = MapSet.member?(assigns.expanded, assigns.block["id"])
|
||||
preview = block_preview(assigns.block, block_type)
|
||||
|
||||
assigns =
|
||||
assigns
|
||||
|> assign(:block_type, block_type)
|
||||
|> assign(:has_settings, has_settings)
|
||||
|> assign(:is_expanded, expanded)
|
||||
|> assign(:preview, preview)
|
||||
|
||||
~H"""
|
||||
<div
|
||||
@@ -49,9 +52,12 @@ defmodule BerrypodWeb.BlockEditorComponents do
|
||||
<.icon name={(@block_type && @block_type.icon) || "hero-puzzle-piece"} class="size-5" />
|
||||
</span>
|
||||
|
||||
<span class="block-card-name">
|
||||
{(@block_type && @block_type.name) || @block["type"]}
|
||||
</span>
|
||||
<div class="block-card-info">
|
||||
<span class="block-card-name">
|
||||
{(@block_type && @block_type.name) || @block["type"]}
|
||||
</span>
|
||||
<span :if={@preview} class="block-card-preview">{@preview}</span>
|
||||
</div>
|
||||
|
||||
<span class="block-card-controls">
|
||||
<button
|
||||
@@ -111,6 +117,7 @@ defmodule BerrypodWeb.BlockEditorComponents do
|
||||
:if={@is_expanded}
|
||||
block={@block}
|
||||
schema={@block_type.settings_schema}
|
||||
hint={@block_type[:hint]}
|
||||
event_prefix={@event_prefix}
|
||||
/>
|
||||
</div>
|
||||
@@ -121,6 +128,7 @@ defmodule BerrypodWeb.BlockEditorComponents do
|
||||
|
||||
attr :block, :map, required: true
|
||||
attr :schema, :list, required: true
|
||||
attr :hint, :string, default: nil
|
||||
attr :event_prefix, :string, default: ""
|
||||
|
||||
def block_settings_form(assigns) do
|
||||
@@ -129,6 +137,9 @@ defmodule BerrypodWeb.BlockEditorComponents do
|
||||
|
||||
~H"""
|
||||
<div class="block-card-settings" id={"block-settings-#{@block["id"]}"}>
|
||||
<p :if={@hint} class="block-settings-hint">
|
||||
<.icon name="hero-information-circle-mini" class="size-4" /> {@hint}
|
||||
</p>
|
||||
<form phx-change={"#{@event_prefix}update_block_settings"}>
|
||||
<input type="hidden" name="block_id" value={@block["id"]} />
|
||||
|
||||
@@ -454,6 +465,7 @@ defmodule BerrypodWeb.BlockEditorComponents do
|
||||
phx-value-type={type}
|
||||
class="block-picker-item"
|
||||
>
|
||||
<BlockThumbnails.block_thumbnail type={type} />
|
||||
<.icon name={def.icon} class="size-5" />
|
||||
<span class="block-picker-item-name">{def.name}</span>
|
||||
<span :if={def[:description]} class="block-picker-item-desc">
|
||||
@@ -564,4 +576,69 @@ defmodule BerrypodWeb.BlockEditorComponents do
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
# ── Block preview ──────────────────────────────────────────────
|
||||
|
||||
# Extracts a short one-line preview from block settings for display in the card
|
||||
defp block_preview(_block, nil), do: nil
|
||||
|
||||
defp block_preview(block, block_type) do
|
||||
settings = block["settings"] || %{}
|
||||
schema = block_type.settings_schema
|
||||
|
||||
# Try text/textarea fields first, then selects, then repeaters
|
||||
find_text_preview(settings, schema) ||
|
||||
find_select_preview(settings, schema) ||
|
||||
find_repeater_preview(settings, schema)
|
||||
end
|
||||
|
||||
defp find_text_preview(settings, schema) do
|
||||
schema
|
||||
|> Enum.filter(&(&1.type in [:text, :textarea]))
|
||||
|> Enum.find_value(fn field ->
|
||||
case settings[field.key] do
|
||||
val when is_binary(val) and val != "" ->
|
||||
truncate(val, 60)
|
||||
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
defp find_select_preview(settings, schema) do
|
||||
schema
|
||||
|> Enum.filter(&(&1.type == :select))
|
||||
|> Enum.find_value(fn field ->
|
||||
case settings[field.key] do
|
||||
val when is_binary(val) and val != "" -> "#{field.label}: #{val}"
|
||||
_ -> nil
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
defp find_repeater_preview(settings, schema) do
|
||||
schema
|
||||
|> Enum.filter(&(&1.type == :repeater))
|
||||
|> Enum.find_value(fn field ->
|
||||
case settings[field.key] do
|
||||
items when is_list(items) and items != [] ->
|
||||
count = length(items)
|
||||
"#{count} #{if count == 1, do: "item", else: "items"}"
|
||||
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
defp truncate(str, max) do
|
||||
str = String.replace(str, ~r/\s+/, " ") |> String.trim()
|
||||
|
||||
if String.length(str) > max do
|
||||
String.slice(str, 0, max) <> "..."
|
||||
else
|
||||
str
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user