add SEO enhancements: OG images, meta robots, FAQ block, image sitemap
All checks were successful
deploy / deploy (push) Successful in 4m59s

- Per-page SEO controls: meta robots directives, focus keyword, OG image
- Site-wide default OG image in admin settings
- FAQ block type with FAQPage JSON-LD schema
- Enhanced Organization JSON-LD with business info, contact, address
- Image sitemap with product images
- SEO preview panel with Google/social card mockups
- SEO checklist with real-time scoring
- Business info section in site editor
- GSC integration scaffolding (OAuth, client, cache)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
jamey
2026-04-17 16:47:43 +01:00
parent 9facfd926e
commit 4aa7dece0c
42 changed files with 3881 additions and 41 deletions

View File

@@ -26,7 +26,7 @@ defmodule BerrypodWeb.SiteEditorState do
state = SiteEditorState.revert(state)
"""
alias Berrypod.Site
alias Berrypod.{Settings, Site}
alias Berrypod.Site.{NavItem, SocialLink}
defstruct [
@@ -37,6 +37,8 @@ defmodule BerrypodWeb.SiteEditorState do
:footer_about,
:footer_copyright,
:show_newsletter,
# Business info (stored in Settings as JSON)
:business_info,
# List fields (separate tables)
:header_nav,
:footer_nav,
@@ -52,6 +54,7 @@ defmodule BerrypodWeb.SiteEditorState do
footer_about: String.t(),
footer_copyright: String.t(),
show_newsletter: boolean(),
business_info: map(),
header_nav: [NavItem.t()],
footer_nav: [NavItem.t()],
social_links: [SocialLink.t()],
@@ -67,6 +70,7 @@ defmodule BerrypodWeb.SiteEditorState do
header_nav = Site.list_nav_items(:header)
footer_nav = Site.list_nav_items(:footer)
social_links = Site.list_social_links()
business_info = Settings.get_business_info()
state = %__MODULE__{
announcement_text: settings.announcement_text,
@@ -75,6 +79,7 @@ defmodule BerrypodWeb.SiteEditorState do
footer_about: settings.footer_about,
footer_copyright: settings.footer_copyright,
show_newsletter: settings.show_newsletter,
business_info: business_info,
header_nav: header_nav,
footer_nav: footer_nav,
social_links: social_links
@@ -102,6 +107,14 @@ defmodule BerrypodWeb.SiteEditorState do
Map.put(state, field, value)
end
@doc """
Update a business info field. Returns updated state.
"""
@spec put_business_info(t(), String.t(), any()) :: t()
def put_business_info(%__MODULE__{business_info: info} = state, key, value) do
%{state | business_info: Map.put(info, key, value)}
end
@doc """
Add a new social link. Returns updated state.
"""
@@ -255,6 +268,11 @@ defmodule BerrypodWeb.SiteEditorState do
state.show_newsletter
)
# Save business info if changed
if state.business_info != state._original.business_info do
Settings.put_business_info(state.business_info)
end
# Sync nav items and social links
header_nav = sync_nav_items(state.header_nav, state._original.header_nav)
footer_nav = sync_nav_items(state.footer_nav, state._original.footer_nav)
@@ -278,6 +296,7 @@ defmodule BerrypodWeb.SiteEditorState do
footer_about: original.footer_about,
footer_copyright: original.footer_copyright,
show_newsletter: original.show_newsletter,
business_info: original.business_info,
header_nav: original.header_nav,
footer_nav: original.footer_nav,
social_links: original.social_links
@@ -301,6 +320,7 @@ defmodule BerrypodWeb.SiteEditorState do
footer_about: state.footer_about,
footer_copyright: state.footer_copyright,
show_newsletter: state.show_newsletter,
business_info: state.business_info,
header_nav: Enum.map(state.header_nav, &nav_item_snapshot/1),
footer_nav: Enum.map(state.footer_nav, &nav_item_snapshot/1),
social_links: Enum.map(state.social_links, &social_link_snapshot/1)