fix content image double-suffix, clean up page defaults and editor UX
All checks were successful
deploy / deploy (push) Successful in 1m22s
All checks were successful
deploy / deploy (push) Successful in 1m22s
- Fix resolve_content_image returning base path (not full URL) so responsive_image doesn't double-append width/extension - Remove legacy image fields (image_src, image_alt, image_url) from block settings schemas - Remove demo/mockup fallbacks from renderer and defaults — blank fields stay blank instead of showing preview content - Replace demo text in defaults with instructional placeholders that guide new shop owners - Remove redundant X button from editor sidebar, add unsaved-changes confirmation to Done button - Fix block card name overflow on mobile (display: block, flex-wrap) - Add onboarding UX improvement plan (10 tasks) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -96,7 +96,6 @@ defmodule Berrypod.Pages.BlockTypes do
|
||||
%SettingsField{key: "title", label: "Title", type: :text, default: ""},
|
||||
%SettingsField{key: "description", label: "Description", type: :textarea, default: ""},
|
||||
%SettingsField{key: "image_id", label: "Image", type: :image, default: nil},
|
||||
%SettingsField{key: "image_url", label: "Image URL (legacy)", type: :text, default: ""},
|
||||
%SettingsField{key: "link_text", label: "Link text", type: :text, default: ""},
|
||||
%SettingsField{key: "link_href", label: "Link URL", type: :text, default: ""}
|
||||
]
|
||||
@@ -344,14 +343,7 @@ defmodule Berrypod.Pages.BlockTypes do
|
||||
allowed_on: :all,
|
||||
settings_schema: [
|
||||
%SettingsField{key: "content", label: "Content", type: :textarea, default: ""},
|
||||
%SettingsField{key: "image_id", label: "Image", type: :image, default: nil},
|
||||
%SettingsField{key: "image_src", label: "Image URL (legacy)", type: :text, default: ""},
|
||||
%SettingsField{
|
||||
key: "image_alt",
|
||||
label: "Image alt text (legacy)",
|
||||
type: :text,
|
||||
default: ""
|
||||
}
|
||||
%SettingsField{key: "image_id", label: "Image", type: :image, default: nil}
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
@@ -42,9 +42,9 @@ defmodule Berrypod.Pages.Defaults do
|
||||
defp blocks("home") do
|
||||
[
|
||||
block("hero", %{
|
||||
"title" => "Original designs, printed on demand",
|
||||
"title" => "Your headline goes here",
|
||||
"description" =>
|
||||
"Welcome to the Berrypod demo store. This is where your hero text goes \u2013 something short and punchy about what makes your shop worth a browse.",
|
||||
"Write something short and punchy about your shop \u2013 what you sell and why it's worth a look.",
|
||||
"cta_text" => "Shop the collection",
|
||||
"cta_href" => "/collections/all",
|
||||
"variant" => "default"
|
||||
@@ -55,11 +55,10 @@ defmodule Berrypod.Pages.Defaults do
|
||||
"product_count" => 8
|
||||
}),
|
||||
block("image_text", %{
|
||||
"title" => "Made with passion, printed with care",
|
||||
"title" => "Your story in a nutshell",
|
||||
"description" =>
|
||||
"This is an example content section. Use it to share your story, highlight what makes your products special, or link to your about page.",
|
||||
"image_url" => "/mockups/mountain-sunrise-print-3-800.webp",
|
||||
"link_text" => "Learn more about the studio \u2192",
|
||||
"Use this section to share what makes your products special, your creative process, or a bit about you. Pick an image from your media library to go alongside it.",
|
||||
"link_text" => "Read more about us \u2192",
|
||||
"link_href" => "/about"
|
||||
})
|
||||
]
|
||||
@@ -68,13 +67,14 @@ defmodule Berrypod.Pages.Defaults do
|
||||
defp blocks("about") do
|
||||
[
|
||||
block("hero", %{
|
||||
"title" => "About the studio",
|
||||
"description" => "Your story goes here \u2013 this is sample content for the demo shop",
|
||||
"title" => "About us",
|
||||
"description" => "Share a bit about who you are and what you do",
|
||||
"variant" => "sunken"
|
||||
}),
|
||||
block("content_body", %{
|
||||
"image_src" => "/mockups/night-sky-blanket-3",
|
||||
"image_alt" => "Night sky blanket draped over a chair"
|
||||
"content" =>
|
||||
"Tell your customers who you are and what inspired your shop. What's the story behind your designs?\n\n" <>
|
||||
"This is placeholder text \u2013 edit it to make it your own. You can also add an image from your media library using the block settings."
|
||||
})
|
||||
]
|
||||
end
|
||||
@@ -116,8 +116,7 @@ defmodule Berrypod.Pages.Defaults do
|
||||
[
|
||||
block("hero", %{
|
||||
"title" => "Get in touch",
|
||||
"description" =>
|
||||
"Sample contact page for the demo store. Add your own message here \u2013 something friendly about how customers can reach you.",
|
||||
"description" => "Add a friendly message about how customers can reach you",
|
||||
"variant" => "page"
|
||||
}),
|
||||
block("contact_form", %{"email" => "hello@example.com"}),
|
||||
@@ -125,9 +124,9 @@ defmodule Berrypod.Pages.Defaults do
|
||||
block("info_card", %{
|
||||
"title" => "Handy to know",
|
||||
"items" => [
|
||||
%{"label" => "Printing", "value" => "Example: 2-5 business days"},
|
||||
%{"label" => "Delivery", "value" => "Example: 3-7 business days after printing"},
|
||||
%{"label" => "Issues", "value" => "Example: Reprints for any defects"}
|
||||
%{"label" => "Printing", "value" => "Update with your printing times"},
|
||||
%{"label" => "Delivery", "value" => "Update with your delivery times"},
|
||||
%{"label" => "Issues", "value" => "Update with your returns policy"}
|
||||
]
|
||||
}),
|
||||
block("newsletter_card"),
|
||||
|
||||
@@ -112,15 +112,12 @@ defmodule BerrypodWeb.PageRenderer do
|
||||
>
|
||||
Reset
|
||||
</button>
|
||||
<button phx-click="editor_done" class="admin-btn admin-btn-sm admin-btn-ghost">
|
||||
Done
|
||||
</button>
|
||||
<button
|
||||
phx-click="editor_toggle_sidebar"
|
||||
phx-click="editor_done"
|
||||
class="admin-btn admin-btn-sm admin-btn-ghost"
|
||||
aria-label="Close sidebar"
|
||||
data-confirm={@editor_dirty && "You have unsaved changes. Leave without saving?"}
|
||||
>
|
||||
<.icon name="hero-x-mark" class="size-5" />
|
||||
Done
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -280,7 +277,7 @@ defmodule BerrypodWeb.PageRenderer do
|
||||
|
||||
defp render_block(%{block: %{"type" => "image_text"}} = assigns) do
|
||||
settings = assigns.block["settings"] || %{}
|
||||
image_url = resolve_block_image_url(settings["image_id"], settings["image_url"])
|
||||
image_url = resolve_block_image_url(settings["image_id"])
|
||||
|
||||
assigns =
|
||||
assigns
|
||||
@@ -634,11 +631,12 @@ defmodule BerrypodWeb.PageRenderer do
|
||||
defp render_block(%{block: %{"type" => "content_body"}} = assigns) do
|
||||
settings = assigns.block["settings"] || %{}
|
||||
content = settings["content"] || ""
|
||||
{image_src, image_alt} = resolve_content_image(settings)
|
||||
{image_src, source_width, image_alt} = resolve_content_image(settings)
|
||||
|
||||
assigns =
|
||||
assigns
|
||||
|> assign(:image_src, image_src)
|
||||
|> assign(:image_source_width, source_width || 1200)
|
||||
|> assign(:image_alt, image_alt)
|
||||
|> assign(:content, content)
|
||||
|
||||
@@ -648,7 +646,7 @@ defmodule BerrypodWeb.PageRenderer do
|
||||
<div class="content-image">
|
||||
<.responsive_image
|
||||
src={@image_src}
|
||||
source_width={1200}
|
||||
source_width={@image_source_width}
|
||||
alt={@image_alt}
|
||||
sizes="(max-width: 800px) 100vw, 800px"
|
||||
class="content-hero-image"
|
||||
@@ -1154,47 +1152,41 @@ defmodule BerrypodWeb.PageRenderer do
|
||||
defp collection_path(slug, "featured"), do: ~p"/collections/#{slug}"
|
||||
defp collection_path(slug, sort), do: ~p"/collections/#{slug}?sort=#{sort}"
|
||||
|
||||
# Resolves an image_id to a URL, falling back to a legacy URL string
|
||||
defp resolve_block_image_url(image_id, fallback_url) do
|
||||
# Resolves an image_id to a full URL for blocks that need a single URL (e.g. background-image).
|
||||
defp resolve_block_image_url(image_id) do
|
||||
case resolve_image(image_id) do
|
||||
{url, _alt} -> url
|
||||
nil -> fallback_url || ""
|
||||
nil ->
|
||||
""
|
||||
|
||||
image ->
|
||||
if image.is_svg do
|
||||
"/image_cache/#{image.id}.webp"
|
||||
else
|
||||
width =
|
||||
image.source_width
|
||||
|> Berrypod.Images.Optimizer.applicable_widths()
|
||||
|> List.last()
|
||||
|
||||
"/image_cache/#{image.id}-#{width || 400}.webp"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Resolves image_id for content_body blocks, returning {src, alt}
|
||||
# Resolves image_id for content_body blocks, returning {base_path, source_width, alt}.
|
||||
# base_path has no width/extension suffix — responsive_image adds those.
|
||||
defp resolve_content_image(settings) do
|
||||
case resolve_image(settings["image_id"]) do
|
||||
{src, alt} -> {src, alt}
|
||||
nil -> {settings["image_src"], settings["image_alt"] || ""}
|
||||
nil ->
|
||||
{nil, nil, ""}
|
||||
|
||||
image ->
|
||||
{"/image_cache/#{image.id}", image.source_width, image.alt || image.filename}
|
||||
end
|
||||
end
|
||||
|
||||
defp resolve_image(nil), do: nil
|
||||
defp resolve_image(""), do: nil
|
||||
|
||||
defp resolve_image(image_id) do
|
||||
case Berrypod.Media.get_image(image_id) do
|
||||
nil ->
|
||||
nil
|
||||
|
||||
image ->
|
||||
url =
|
||||
if image.is_svg do
|
||||
"/image_cache/#{image.id}.webp"
|
||||
else
|
||||
# Pick the largest variant that was actually generated
|
||||
width =
|
||||
image.source_width
|
||||
|> Berrypod.Images.Optimizer.applicable_widths()
|
||||
|> List.last()
|
||||
|
||||
"/image_cache/#{image.id}-#{width || 400}.webp"
|
||||
end
|
||||
|
||||
{url, image.alt || image.filename}
|
||||
end
|
||||
end
|
||||
defp resolve_image(image_id), do: Berrypod.Media.get_image(image_id)
|
||||
|
||||
@youtube_re ~r/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([a-zA-Z0-9_-]{11})/
|
||||
@vimeo_re ~r/vimeo\.com\/(?:video\/)?(\d+)/
|
||||
|
||||
Reference in New Issue
Block a user