berrypod/lib/berrypod_web/live/admin/theme/index.html.heex
jamey 22d3e36ed5 format refactored admin templates
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 19:39:56 +00:00

1233 lines
44 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<div class="theme-layout">
<!-- Controls Sidebar -->
<div
id="theme-sidebar"
class={[
"theme-sidebar",
if(@sidebar_collapsed,
do: "theme-sidebar-collapsed",
else: "theme-sidebar-expanded"
)
]}
>
<!-- Collapsed state: just show expand button -->
<%= if @sidebar_collapsed do %>
<div class="theme-sidebar-collapsed-inner">
<button
type="button"
phx-click="toggle_sidebar"
class="theme-collapse-btn"
aria-label="Expand sidebar"
aria-expanded="false"
aria-controls="theme-sidebar"
>
<svg
class="theme-collapse-icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</button>
</div>
<% else %>
<.link href={~p"/admin"} class="theme-back-link">
<.icon name="hero-arrow-left-mini" class="size-4" /> Admin
</.link>
<!-- Header -->
<div class="theme-header">
<div style="flex: 1;">
<h1 class="theme-title">Theme Studio</h1>
<p class="theme-subtitle">
One theme, infinite possibilities. Every combination is designed to work beautifully.
</p>
</div>
<button
type="button"
phx-click="toggle_sidebar"
class="theme-collapse-btn"
style="margin: -0.25rem -0.5rem 0 0;"
aria-label="Collapse sidebar"
aria-expanded="true"
aria-controls="theme-sidebar"
>
<svg
class="theme-collapse-icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<polyline points="15 18 9 12 15 6"></polyline>
</svg>
</button>
</div>
<!-- Site Name -->
<div class="theme-field" style="margin-bottom: 1.5rem;">
<label class="theme-section-label">Shop name</label>
<form phx-change="update_setting" phx-value-field="site_name">
<input
type="text"
name="site_name"
value={@theme_settings.site_name}
placeholder="Your shop name"
class="admin-input"
style="padding: 0.75rem 1rem; font-size: 1rem;"
/>
</form>
</div>
<!-- Branding Section -->
<div class="theme-panel">
<label class="theme-section-label" style="margin-bottom: 1rem;">Logo & header</label>
<!-- Logo Mode Radio Cards -->
<div class="admin-stack" style="--admin-stack-gap: 0.5rem; margin-bottom: 1rem;">
<%= for {value, title, desc} <- [
{"text-only", "Shop name only", "Your name in the heading font"},
{"logo-text", "Logo + shop name", "Your logo image with name beside it"},
{"logo-only", "Logo only", "Just your logo (with text built in)"}
] do %>
<label class={[
"theme-radio-card",
if(@theme_settings.logo_mode == value,
do: "theme-radio-card-active",
else: ""
)
]}>
<input
type="radio"
name="logo_mode"
value={value}
checked={@theme_settings.logo_mode == value}
phx-click="update_setting"
phx-value-field="logo_mode"
phx-value-setting_value={value}
class="hidden"
/>
<span class={[
"theme-radio-dot",
@theme_settings.logo_mode == value && "theme-radio-dot-active"
]}>
<span class={[
"theme-radio-dot-inner",
if(@theme_settings.logo_mode == value,
do: "theme-radio-dot-inner-active",
else: ""
)
]}>
</span>
</span>
<div style="flex: 1;">
<div class="theme-radio-title">{title}</div>
<div class="admin-text-secondary">{desc}</div>
</div>
</label>
<% end %>
</div>
<!-- Logo Upload (for logo-text and logo-only modes) -->
<%= if @theme_settings.logo_mode in ["logo-text", "logo-only"] do %>
<div class="theme-subsection">
<span class="theme-slider-label" style="display: block; margin-bottom: 0.5rem;">
Upload logo (SVG or PNG)
</span>
<div class="admin-row" style="--admin-row-gap: 0.75rem;">
<form phx-change="noop" phx-submit="noop" style="flex: 1;">
<label class="theme-upload-label">
<span>Choose file...</span>
<.live_file_input upload={@uploads.logo_upload} class="hidden" />
</label>
</form>
<%= if @logo_image do %>
<div class="theme-thumb theme-thumb-logo">
<img
src={"/image_cache/#{@logo_image.id}.webp"}
alt="Current logo"
/>
<button
type="button"
phx-click="remove_logo"
class="theme-remove-btn"
title="Remove logo"
>
×
</button>
</div>
<form
phx-change="update_image_alt"
phx-value-image-id={@logo_image.id}
style="margin-top: 0.5rem;"
>
<label class="admin-row" style="--admin-row-gap: 0.5rem;">
<span class="admin-text-secondary shrink-0">Alt text</span>
<input
type="text"
name="alt"
value={@logo_image.alt || ""}
placeholder="Describe this image"
class="admin-input admin-input-sm"
style="flex: 1;"
phx-debounce="blur"
/>
</label>
<p
:if={!@logo_image.alt || @logo_image.alt == ""}
class="theme-alt-warning"
>
Missing alt text — add a description for accessibility
</p>
</form>
<% end %>
</div>
<%= for entry <- @uploads.logo_upload.entries do %>
<div class="theme-progress">
<div class="theme-progress-bar">
<div
class="theme-progress-fill"
style={"width: #{entry.progress}%"}
>
</div>
</div>
<span class="admin-text-secondary">{entry.progress}%</span>
<button
type="button"
phx-click="cancel_upload"
phx-value-ref={entry.ref}
phx-value-upload="logo_upload"
class="theme-upload-cancel"
>
×
</button>
</div>
<%= for err <- upload_errors(@uploads.logo_upload, entry) do %>
<p class="theme-error-text">{error_to_string(err)}</p>
<% end %>
<% end %>
<%= for err <- upload_errors(@uploads.logo_upload) do %>
<p class="theme-error-text">{error_to_string(err)}</p>
<% end %>
<!-- Logo Size Slider -->
<%= if @logo_image do %>
<form
phx-change="update_setting"
phx-value-field="logo_size"
style="margin-top: 0.75rem;"
>
<div class="theme-slider-header">
<span class="theme-slider-label">Logo size</span>
<span class="theme-slider-value">{@theme_settings.logo_size}px</span>
</div>
<input
type="range"
min="24"
max="120"
value={@theme_settings.logo_size}
name="logo_size"
class="admin-range"
style="width: 100%;"
/>
</form>
<!-- SVG Recolor Toggle (only for SVG logos) -->
<%= if @logo_image.is_svg do %>
<div style="margin-top: 0.75rem;">
<label class="admin-toggle-label">
<input
type="checkbox"
checked={@theme_settings.logo_recolor}
phx-click="update_setting"
phx-value-field="logo_recolor"
phx-value-setting_value={
if @theme_settings.logo_recolor, do: "false", else: "true"
}
class="admin-toggle admin-toggle-sm"
/>
<span class="theme-slider-label">Recolour logo</span>
</label>
<%= if @theme_settings.logo_recolor do %>
<form
id="logo-color-form"
phx-change="update_color"
phx-value-field="logo_color"
phx-hook="ColorSync"
class="theme-color-row"
style="margin-top: 0.5rem;"
>
<input
type="color"
name="value"
value={@theme_settings.logo_color}
class="theme-color-swatch theme-color-swatch-sm"
/>
<span class="theme-color-value">{@theme_settings.logo_color}</span>
</form>
<% end %>
</div>
<% end %>
<% end %>
</div>
<% end %>
</div>
<!-- Site Icon / Favicon -->
<div class="theme-panel">
<label class="theme-section-label">Site icon</label>
<p class="admin-text-tertiary" style="margin-bottom: 1rem;">
Your icon appears in browser tabs and on home screens.
</p>
<!-- Use logo as icon toggle -->
<label class="admin-toggle-label" style="margin-bottom: 1rem;">
<input
type="checkbox"
checked={@theme_settings.use_logo_as_icon}
phx-click="toggle_setting"
phx-value-field="use_logo_as_icon"
class="admin-toggle admin-toggle-sm"
/>
<span class="theme-slider-label">Use logo as favicon</span>
</label>
<!-- Icon upload (only when not using logo) -->
<%= if !@theme_settings.use_logo_as_icon do %>
<div style="padding-top: 0.75rem; border-top: 1px solid var(--t-border-default);">
<span class="theme-slider-label" style="display: block; margin-bottom: 0.5rem;">
Upload icon (PNG or SVG, 512×512+)
</span>
<div class="admin-row" style="--admin-row-gap: 0.75rem;">
<form phx-change="noop" phx-submit="noop" style="flex: 1;">
<label class="theme-upload-label">
<span>Choose file...</span>
<.live_file_input upload={@uploads.icon_upload} class="hidden" />
</label>
</form>
<%= if @icon_image do %>
<div class="theme-thumb theme-thumb-icon">
<%= if @icon_image.is_svg do %>
<img
src={"/images/#{@icon_image.id}/recolored/000000"}
alt="Current icon"
/>
<% else %>
<img
src={"/image_cache/#{@icon_image.id}.webp"}
alt="Current icon"
/>
<% end %>
<button
type="button"
phx-click="remove_icon"
class="theme-remove-btn"
title="Remove icon"
>
×
</button>
</div>
<% end %>
</div>
<%= for entry <- @uploads.icon_upload.entries do %>
<div class="theme-progress">
<div class="theme-progress-bar">
<div
class="theme-progress-fill"
style={"width: #{entry.progress}%"}
>
</div>
</div>
<span class="admin-text-secondary">{entry.progress}%</span>
<button
type="button"
phx-click="cancel_upload"
phx-value-ref={entry.ref}
phx-value-upload="icon_upload"
class="theme-upload-cancel"
>
×
</button>
</div>
<%= for err <- upload_errors(@uploads.icon_upload, entry) do %>
<p class="theme-error-text">{error_to_string(err)}</p>
<% end %>
<% end %>
<%= for err <- upload_errors(@uploads.icon_upload) do %>
<p class="theme-error-text">{error_to_string(err)}</p>
<% end %>
</div>
<% end %>
<!-- Short name -->
<div class="theme-subsection" style="padding-top: 0.75rem;">
<form phx-change="update_setting" phx-value-field="favicon_short_name">
<div class="theme-slider-header" style="margin-bottom: 0.25rem;">
<span class="theme-slider-label">Short name</span>
<span class="admin-text-tertiary">Home screen label</span>
</div>
<input
type="text"
name="favicon_short_name"
value={@theme_settings.favicon_short_name}
placeholder={String.slice(@theme_settings.site_name, 0, 12)}
maxlength="12"
class="admin-input admin-input-sm"
/>
</form>
</div>
<!-- Icon background colour -->
<div style="margin-top: 0.75rem;">
<form
id="icon-bg-color-form"
phx-change="update_color"
phx-value-field="icon_background_color"
phx-hook="ColorSync"
class="theme-color-row"
>
<input
type="color"
name="value"
value={@theme_settings.icon_background_color}
class="theme-color-swatch theme-color-swatch-sm"
/>
<div>
<span class="theme-slider-label" style="display: block;">Icon background</span>
<span class="theme-slider-value" style="font-size: 0.75rem;">
{@theme_settings.icon_background_color}
</span>
</div>
</form>
</div>
</div>
<!-- Header Background Toggle -->
<div style="margin-bottom: 1.5rem;">
<label class="admin-toggle-label">
<input
type="checkbox"
checked={@theme_settings.header_background_enabled}
phx-click="update_setting"
phx-value-field="header_background_enabled"
phx-value-setting_value={
if @theme_settings.header_background_enabled, do: "false", else: "true"
}
class="admin-toggle admin-toggle-sm"
/>
<span style="font-size: 0.875rem; color: color-mix(in oklch, var(--t-text-primary) 80%, transparent);">
Header background image
</span>
</label>
</div>
<!-- Header Image Upload (only when enabled) -->
<%= if @theme_settings.header_background_enabled do %>
<div class="theme-panel">
<span class="theme-slider-label" style="display: block; margin-bottom: 0.5rem;">
Upload header image
</span>
<form phx-change="noop" phx-submit="noop">
<label class="theme-upload-label">
<span>Choose file...</span>
<.live_file_input upload={@uploads.header_upload} class="hidden" />
</label>
</form>
<%= if @header_image do %>
<div class="theme-thumb theme-thumb-cover theme-thumb-header">
<img
src={"/image_cache/#{@header_image.id}.webp"}
alt="Current header background"
/>
<button
type="button"
phx-click="remove_header"
class="theme-remove-btn"
title="Remove header background"
>
×
</button>
</div>
<form
phx-change="update_image_alt"
phx-value-image-id={@header_image.id}
style="margin-top: 0.5rem;"
>
<label class="admin-row" style="--admin-row-gap: 0.5rem;">
<span class="admin-text-secondary shrink-0">Alt text</span>
<input
type="text"
name="alt"
value={@header_image.alt || ""}
placeholder="Describe this image"
class="admin-input admin-input-sm"
style="flex: 1;"
phx-debounce="blur"
/>
</label>
<p
:if={!@header_image.alt || @header_image.alt == ""}
class="theme-alt-warning"
>
Missing alt text — add a description for accessibility
</p>
</form>
<!-- Header Image Controls -->
<div class="admin-stack" style="--admin-stack-gap: 0.75rem; margin-top: 0.75rem;">
<form phx-change="update_setting" phx-value-field="header_zoom">
<div class="theme-slider-header">
<span class="theme-slider-label">Zoom</span>
<span class="theme-slider-value">{@theme_settings.header_zoom}%</span>
</div>
<input
type="range"
min="100"
max="200"
value={@theme_settings.header_zoom}
name="header_zoom"
class="admin-range"
style="width: 100%;"
/>
</form>
<form phx-change="update_setting" phx-value-field="header_position_x">
<div class="theme-slider-header">
<span class="theme-slider-label">Horizontal position</span>
<span class="theme-slider-value">{@theme_settings.header_position_x}%</span>
</div>
<input
type="range"
min="0"
max="100"
value={@theme_settings.header_position_x}
name="header_position_x"
class="admin-range"
style="width: 100%;"
/>
</form>
<form phx-change="update_setting" phx-value-field="header_position_y">
<div class="theme-slider-header">
<span class="theme-slider-label">Vertical position</span>
<span class="theme-slider-value">{@theme_settings.header_position_y}%</span>
</div>
<input
type="range"
min="0"
max="100"
value={@theme_settings.header_position_y}
name="header_position_y"
class="admin-range"
style="width: 100%;"
/>
</form>
</div>
<% end %>
<%= for entry <- @uploads.header_upload.entries do %>
<div class="theme-progress">
<div class="theme-progress-bar">
<div
class="theme-progress-fill"
style={"width: #{entry.progress}%"}
>
</div>
</div>
<span class="admin-text-secondary">{entry.progress}%</span>
<button
type="button"
phx-click="cancel_upload"
phx-value-ref={entry.ref}
phx-value-upload="header_upload"
class="theme-upload-cancel"
>
×
</button>
</div>
<%= for err <- upload_errors(@uploads.header_upload, entry) do %>
<p class="theme-error-text">{error_to_string(err)}</p>
<% end %>
<% end %>
<%= for err <- upload_errors(@uploads.header_upload) do %>
<p class="theme-error-text">{error_to_string(err)}</p>
<% end %>
</div>
<% end %>
<!-- Presets Section -->
<div style="margin-bottom: 1.5rem;">
<label class="theme-section-label">Start with a preset</label>
<div class="theme-presets">
<%= for {preset_name, description} <- @presets_with_descriptions do %>
<button
type="button"
phx-click="apply_preset"
phx-value-preset={preset_name}
class={[
"theme-preset",
@active_preset == preset_name && "theme-preset-active"
]}
>
<div class="theme-preset-name">{preset_name}</div>
<div class="theme-preset-desc">{description}</div>
</button>
<% end %>
</div>
</div>
<!-- Accent Colors -->
<div style="margin-bottom: 1.5rem;">
<label class="theme-section-label">Accent colour</label>
<form
id="accent-color-form"
phx-change="update_color"
phx-value-field="accent_color"
phx-hook="ColorSync"
>
<div class="theme-color-row">
<input
type="color"
id="accent-color-picker"
name="value"
value={@theme_settings.accent_color}
class="theme-color-swatch"
/>
<span class="theme-color-value">{@theme_settings.accent_color}</span>
</div>
</form>
</div>
<div style="margin-bottom: 1.5rem;">
<label class="theme-section-label">Hover colour</label>
<form
id="secondary-accent-color-form"
phx-change="update_color"
phx-value-field="secondary_accent_color"
phx-hook="ColorSync"
>
<div class="theme-color-row">
<input
type="color"
id="secondary-accent-color-picker"
name="value"
value={@theme_settings.secondary_accent_color}
class="theme-color-swatch"
/>
<span class="theme-color-value">{@theme_settings.secondary_accent_color}</span>
</div>
</form>
</div>
<div style="margin-bottom: 1.5rem;">
<label class="theme-section-label">Sale colour</label>
<form
id="sale-color-form"
phx-change="update_color"
phx-value-field="sale_color"
phx-hook="ColorSync"
>
<div class="theme-color-row">
<input
type="color"
id="sale-color-picker"
name="value"
value={@theme_settings.sale_color}
class="theme-color-swatch"
/>
<span class="theme-color-value">{@theme_settings.sale_color}</span>
</div>
</form>
</div>
<!-- Customise Section -->
<details
class="theme-customise"
id="customise-section"
open={@customise_open}
>
<summary class="theme-customise-summary" phx-click="toggle_customise">
<span class="theme-customise-label">Customise</span>
<svg
class="theme-customise-chevron"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</summary>
<div style="padding-top: 1rem;">
<!-- Typography Group -->
<div class="theme-group">
<div class="theme-group-header">
<svg
class="theme-group-icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<polyline points="4 7 4 4 20 4 20 7"></polyline>
<line x1="9" y1="20" x2="15" y2="20"></line>
<line x1="12" y1="4" x2="12" y2="20"></line>
</svg>
<span class="theme-group-title">Typography</span>
</div>
<div class="theme-field">
<label class="theme-section-label">Font style</label>
<div class="theme-chips">
<%= for typo <- ["clean", "editorial", "modern", "classic", "friendly", "minimal"] do %>
<button
type="button"
phx-click="update_setting"
phx-value-field="typography"
phx-value-setting_value={typo}
class={[
"theme-chip",
@theme_settings.typography == typo && "theme-chip-active"
]}
>
{typo}
</button>
<% end %>
</div>
</div>
<div class="theme-field">
<label class="theme-section-label">Font size</label>
<div class="theme-chips">
<%= for {value, label} <- [{"small", "Small"}, {"medium", "Medium"}, {"large", "Large"}] do %>
<button
type="button"
phx-click="update_setting"
phx-value-field="font_size"
phx-value-setting_value={value}
class={[
"theme-chip",
@theme_settings.font_size == value && "theme-chip-active"
]}
>
{label}
</button>
<% end %>
</div>
</div>
<div class="theme-field">
<label class="theme-section-label">Heading weight</label>
<div class="theme-chips">
<%= for {value, label} <- [{"regular", "Regular"}, {"medium", "Medium"}, {"bold", "Bold"}] do %>
<button
type="button"
phx-click="update_setting"
phx-value-field="heading_weight"
phx-value-setting_value={value}
class={[
"theme-chip",
@theme_settings.heading_weight == value && "theme-chip-active"
]}
>
{label}
</button>
<% end %>
</div>
</div>
</div>
<!-- Colours Group -->
<div class="theme-group">
<div class="theme-group-header">
<svg
class="theme-group-icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<circle cx="12" cy="12" r="10"></circle>
<circle cx="12" cy="12" r="3"></circle>
</svg>
<span class="theme-group-title">Colours</span>
</div>
<div class="theme-field">
<label class="theme-section-label">Colour mood</label>
<div class="theme-chips">
<%= for mood <- ["warm", "neutral", "cool", "dark"] do %>
<button
type="button"
phx-click="update_setting"
phx-value-field="mood"
phx-value-setting_value={mood}
class={["theme-chip", @theme_settings.mood == mood && "theme-chip-active"]}
>
{mood}
</button>
<% end %>
</div>
</div>
</div>
<!-- Layout Group -->
<div class="theme-group">
<div class="theme-group-header">
<svg
class="theme-group-icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
<line x1="3" y1="9" x2="21" y2="9"></line>
<line x1="9" y1="21" x2="9" y2="9"></line>
</svg>
<span class="theme-group-title">Layout</span>
</div>
<div class="theme-field">
<label class="theme-section-label">Product grid</label>
<div class="theme-chips">
<%= for cols <- ["2", "3", "4"] do %>
<button
type="button"
phx-click="update_setting"
phx-value-field="grid_columns"
phx-value-setting_value={cols}
class={[
"theme-chip",
@theme_settings.grid_columns == cols && "theme-chip-active"
]}
>
{cols} columns
</button>
<% end %>
</div>
</div>
<div class="theme-field">
<label class="theme-section-label">Density</label>
<div class="theme-chips">
<%= for density <- ["spacious", "balanced", "compact"] do %>
<button
type="button"
phx-click="update_setting"
phx-value-field="density"
phx-value-setting_value={density}
class={[
"theme-chip",
@theme_settings.density == density && "theme-chip-active"
]}
>
{density}
</button>
<% end %>
</div>
</div>
<div class="theme-field">
<label class="theme-section-label">Header layout</label>
<div class="theme-chips">
<%= for layout <- ["standard", "centered", "left"] do %>
<button
type="button"
phx-click="update_setting"
phx-value-field="header_layout"
phx-value-setting_value={layout}
class={[
"theme-chip",
@theme_settings.header_layout == layout && "theme-chip-active"
]}
>
{layout}
</button>
<% end %>
</div>
</div>
<div class="theme-field">
<label class="admin-check-label">
<input
type="checkbox"
checked={@theme_settings.announcement_bar}
phx-click="toggle_setting"
phx-value-field="announcement_bar"
class="admin-checkbox admin-checkbox-sm"
/>
<span style="font-size: 0.875rem;">Announcement bar</span>
</label>
</div>
<div class="theme-field">
<label class="admin-check-label">
<input
type="checkbox"
checked={@theme_settings.sticky_header}
phx-click="toggle_setting"
phx-value-field="sticky_header"
class="admin-checkbox admin-checkbox-sm"
/>
<span style="font-size: 0.875rem;">Sticky header</span>
</label>
</div>
</div>
<!-- Shape Group -->
<div style="margin-bottom: 1rem;">
<div class="theme-group-header">
<svg
class="theme-group-icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
</svg>
<span class="theme-group-title">Shape</span>
</div>
<div class="theme-field">
<label class="theme-section-label">Corner style</label>
<div class="theme-chips">
<%= for shape <- ["sharp", "soft", "round", "pill"] do %>
<button
type="button"
phx-click="update_setting"
phx-value-field="shape"
phx-value-setting_value={shape}
class={["theme-chip", @theme_settings.shape == shape && "theme-chip-active"]}
>
{shape}
</button>
<% end %>
</div>
</div>
<div class="theme-field">
<label class="theme-section-label">Card shadow</label>
<div class="theme-chips">
<%= for {value, label} <- [{"none", "None"}, {"sm", "Subtle"}, {"md", "Medium"}, {"lg", "Strong"}] do %>
<button
type="button"
phx-click="update_setting"
phx-value-field="card_shadow"
phx-value-setting_value={value}
class={[
"theme-chip",
@theme_settings.card_shadow == value && "theme-chip-active"
]}
>
{label}
</button>
<% end %>
</div>
</div>
<div class="theme-field">
<label class="theme-section-label">Button style</label>
<div class="theme-chips">
<%= for {value, label} <- [{"filled", "Filled"}, {"outline", "Outline"}, {"soft", "Soft"}] do %>
<button
type="button"
phx-click="update_setting"
phx-value-field="button_style"
phx-value-setting_value={value}
class={[
"theme-chip",
@theme_settings.button_style == value && "theme-chip-active"
]}
>
{label}
</button>
<% end %>
</div>
</div>
</div>
<!-- Products Group -->
<div class="theme-group">
<div class="theme-group-header">
<svg
class="theme-group-icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<rect x="3" y="3" width="7" height="7"></rect>
<rect x="14" y="3" width="7" height="7"></rect>
<rect x="14" y="14" width="7" height="7"></rect>
<rect x="3" y="14" width="7" height="7"></rect>
</svg>
<span class="theme-group-title">Products</span>
</div>
<div class="theme-field">
<label class="theme-section-label">Content width</label>
<div class="theme-chips">
<%= for width <- ["contained", "wide", "full"] do %>
<button
type="button"
phx-click="update_setting"
phx-value-field="layout_width"
phx-value-setting_value={width}
class={[
"theme-chip",
@theme_settings.layout_width == width && "theme-chip-active"
]}
>
{width}
</button>
<% end %>
</div>
</div>
<div class="theme-field">
<label class="theme-section-label">Image aspect ratio</label>
<div class="theme-chips">
<%= for {value, label} <- [{"square", "Square"}, {"portrait", "Portrait"}, {"landscape", "Landscape"}] do %>
<button
type="button"
phx-click="update_setting"
phx-value-field="image_aspect_ratio"
phx-value-setting_value={value}
class={[
"theme-chip",
@theme_settings.image_aspect_ratio == value && "theme-chip-active"
]}
>
{label}
</button>
<% end %>
</div>
</div>
<div class="theme-field">
<label class="theme-section-label">Product text alignment</label>
<div class="theme-chips">
<%= for {value, label} <- [{"left", "Left"}, {"center", "Centre"}] do %>
<button
type="button"
phx-click="update_setting"
phx-value-field="product_text_align"
phx-value-setting_value={value}
class={[
"theme-chip",
@theme_settings.product_text_align == value && "theme-chip-active"
]}
>
{label}
</button>
<% end %>
</div>
</div>
<div class="theme-field">
<label class="admin-check-label">
<input
type="checkbox"
checked={@theme_settings.hover_image}
phx-click="toggle_setting"
phx-value-field="hover_image"
class="admin-checkbox admin-checkbox-sm"
/>
<span style="font-size: 0.875rem;">Second image on hover</span>
</label>
</div>
<div class="theme-field">
<label class="admin-check-label">
<input
type="checkbox"
checked={@theme_settings.show_prices}
phx-click="toggle_setting"
phx-value-field="show_prices"
class="admin-checkbox admin-checkbox-sm"
/>
<span style="font-size: 0.875rem;">Show prices</span>
</label>
</div>
</div>
<!-- Product Page Group -->
<div style="margin-bottom: 1rem;">
<div class="theme-group-header">
<svg
class="theme-group-icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
<line x1="3" y1="9" x2="21" y2="9"></line>
</svg>
<span class="theme-group-title">Product page</span>
</div>
<div class="theme-field">
<label class="admin-check-label">
<input
type="checkbox"
checked={@theme_settings.pdp_trust_badges}
phx-click="toggle_setting"
phx-value-field="pdp_trust_badges"
class="admin-checkbox admin-checkbox-sm"
/>
<span style="font-size: 0.875rem;">Trust badges</span>
</label>
</div>
<div class="theme-field">
<label class="admin-check-label">
<input
type="checkbox"
checked={@theme_settings.pdp_reviews}
phx-click="toggle_setting"
phx-value-field="pdp_reviews"
class="admin-checkbox admin-checkbox-sm"
/>
<span style="font-size: 0.875rem;">Reviews section</span>
</label>
</div>
<div class="theme-field">
<label class="admin-check-label">
<input
type="checkbox"
checked={@theme_settings.pdp_related_products}
phx-click="toggle_setting"
phx-value-field="pdp_related_products"
class="admin-checkbox admin-checkbox-sm"
/>
<span style="font-size: 0.875rem;">Related products</span>
</label>
</div>
</div>
</div>
</details>
<!-- Current Combination Display -->
<div class="theme-combination">
<div class="theme-combination-label">Current combination</div>
<div class="theme-combination-value">
{String.capitalize(@theme_settings.mood)} · {String.capitalize(
@theme_settings.typography
)} · {String.capitalize(@theme_settings.shape)} · {String.capitalize(
@theme_settings.density
)} · {@theme_settings.grid_columns}-up · {String.capitalize(
@theme_settings.header_layout
)}
</div>
<div class="theme-combination-footnote">
One of 100,000+ possible combinations
</div>
</div>
<% end %>
</div>
<!-- Preview Area -->
<div class="theme-preview-area">
<div class="theme-preview-container">
<!-- Preview Page Switcher -->
<div class="theme-preview-tabs">
<%= for {page_name, label} <- [
{:home, "Home"},
{:collection, "Collection"},
{:pdp, "Product"},
{:cart, "Cart"},
{:about, "About"},
{:delivery, "Delivery"},
{:privacy, "Privacy"},
{:terms, "Terms"},
{:contact, "Contact"},
{:error, "404"}
] do %>
<button
type="button"
phx-click="change_preview_page"
phx-value-page={page_name}
class={[
"theme-preview-tab",
@preview_page == page_name && "theme-preview-tab-active"
]}
>
{label}
</button>
<% end %>
</div>
<!-- Browser Chrome -->
<div class="theme-browser-chrome">
<div class="theme-browser-dots">
<div class="theme-browser-dot theme-browser-dot-close"></div>
<div class="theme-browser-dot theme-browser-dot-min"></div>
<div class="theme-browser-dot theme-browser-dot-max"></div>
</div>
<div class="theme-browser-url">
<svg
class="theme-browser-url-icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
<path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
</svg>
<span class="theme-browser-url-text truncate">
{@theme_settings.site_name |> String.downcase() |> String.replace(" ", "")}.myshopify.com
</span>
</div>
</div>
<!-- Preview Frame -->
<div
class="themed theme-preview-frame"
data-mood={@theme_settings.mood}
data-typography={@theme_settings.typography}
data-shape={@theme_settings.shape}
data-density={@theme_settings.density}
data-grid={@theme_settings.grid_columns}
data-header={@theme_settings.header_layout}
data-sticky={to_string(@theme_settings.sticky_header)}
data-layout={@theme_settings.layout_width}
data-shadow={@theme_settings.card_shadow}
data-button-style={@theme_settings.button_style}
>
<style>
/* All font faces for theme switching */
<%= Phoenix.HTML.raw(Berrypod.Theme.Fonts.generate_all_font_faces(
&BerrypodWeb.Endpoint.static_path/1
)) %>
/* Generated theme CSS */
<%= Phoenix.HTML.raw(@generated_css) %>
</style>
<.preview_page
page={@preview_page}
preview_data={@preview_data}
theme_settings={@theme_settings}
logo_image={@logo_image}
header_image={@header_image}
cart_drawer_open={@cart_drawer_open}
/>
</div>
</div>
</div>
</div>