refactor: extract shop_footer to shared ShopComponents module

Move the footer from ThemeLive.PreviewPages to the shared ShopComponents
module. Add mode attribute (:live vs :preview) for navigation behavior.
Preview mode uses phx-click handlers, live mode uses real URLs.
Also makes copyright year dynamic.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jamey Greenwood 2026-01-17 14:06:28 +00:00
parent 14b1af5314
commit c72f6446a4
9 changed files with 130 additions and 103 deletions

View File

@ -122,4 +122,127 @@ defmodule SimpleshopThemeWeb.ShopComponents do
</div> </div>
""" """
end end
@doc """
Renders the shop footer with newsletter signup and links.
## Attributes
* `theme_settings` - Required. The theme settings map containing site_name.
* `mode` - Optional. Either `:live` (default) for real navigation or
`:preview` for theme preview mode with phx-click handlers.
## Examples
<.shop_footer theme_settings={@theme_settings} />
<.shop_footer theme_settings={@theme_settings} mode={:preview} />
"""
attr :theme_settings, :map, required: true
attr :mode, :atom, default: :live
def shop_footer(assigns) do
assigns = assign(assigns, :current_year, Date.utc_today().year)
~H"""
<footer style="background-color: var(--t-surface-raised); border-top: 1px solid var(--t-border-default);">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
<div class="grid grid-cols-1 md:grid-cols-2 gap-12">
<!-- Newsletter -->
<div>
<h3 class="text-xl font-bold mb-2" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight);">
Stay in touch
</h3>
<p class="mb-4 text-sm" style="color: var(--t-text-secondary);">
Get 10% off your first order and be the first to know about new designs.
</p>
<form class="flex flex-wrap gap-2">
<input
type="email"
placeholder="your@email.com"
class="flex-1 min-w-0 px-4 py-2 text-sm"
style="background-color: var(--t-surface-base); color: var(--t-text-primary); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-input); min-width: 150px;"
/>
<button
type="submit"
class="px-6 py-2 font-medium transition-all text-sm whitespace-nowrap"
style="background-color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l)); color: var(--t-text-inverse); border-radius: var(--t-radius-button);"
>
Subscribe
</button>
</form>
</div>
<!-- Links -->
<div class="grid grid-cols-2 gap-8">
<div>
<h4 class="font-semibold mb-4 text-sm" style="font-family: var(--t-font-heading); color: var(--t-text-primary);">
Shop
</h4>
<ul class="space-y-2 text-sm">
<%= if @mode == :preview do %>
<li><a href="#" phx-click="change_preview_page" phx-value-page="collection" class="transition-colors hover:opacity-80" style="color: var(--t-text-secondary); cursor: pointer;">All products</a></li>
<li><a href="#" phx-click="change_preview_page" phx-value-page="collection" class="transition-colors hover:opacity-80" style="color: var(--t-text-secondary); cursor: pointer;">New arrivals</a></li>
<li><a href="#" phx-click="change_preview_page" phx-value-page="collection" class="transition-colors hover:opacity-80" style="color: var(--t-text-secondary); cursor: pointer;">Best sellers</a></li>
<% else %>
<li><a href="/products" class="transition-colors hover:opacity-80" style="color: var(--t-text-secondary);">All products</a></li>
<li><a href="/products?sort=newest" class="transition-colors hover:opacity-80" style="color: var(--t-text-secondary);">New arrivals</a></li>
<li><a href="/products?sort=popular" class="transition-colors hover:opacity-80" style="color: var(--t-text-secondary);">Best sellers</a></li>
<% end %>
</ul>
</div>
<div>
<h4 class="font-semibold mb-4 text-sm" style="font-family: var(--t-font-heading); color: var(--t-text-primary);">
Help
</h4>
<ul class="space-y-2 text-sm">
<%= if @mode == :preview do %>
<li><a href="#" phx-click="change_preview_page" phx-value-page="about" class="transition-colors hover:opacity-80" style="color: var(--t-text-secondary); cursor: pointer;">Delivery</a></li>
<li><a href="#" phx-click="change_preview_page" phx-value-page="about" class="transition-colors hover:opacity-80" style="color: var(--t-text-secondary); cursor: pointer;">Returns</a></li>
<li><a href="#" phx-click="change_preview_page" phx-value-page="contact" class="transition-colors hover:opacity-80" style="color: var(--t-text-secondary); cursor: pointer;">Contact</a></li>
<% else %>
<li><a href="/delivery" class="transition-colors hover:opacity-80" style="color: var(--t-text-secondary);">Delivery</a></li>
<li><a href="/returns" class="transition-colors hover:opacity-80" style="color: var(--t-text-secondary);">Returns</a></li>
<li><a href="/contact" class="transition-colors hover:opacity-80" style="color: var(--t-text-secondary);">Contact</a></li>
<% end %>
</ul>
</div>
</div>
</div>
<!-- Bottom Bar -->
<div class="mt-12 pt-8 flex flex-col md:flex-row justify-between items-center gap-4" style="border-top: 1px solid var(--t-border-subtle);">
<p class="text-xs" style="color: var(--t-text-tertiary);">
© {@current_year} {@theme_settings.site_name}
</p>
<div class="flex gap-2">
<a
href="#"
class="social-link w-9 h-9 flex items-center justify-center transition-all"
style="color: var(--t-text-secondary); border-radius: var(--t-radius-button);"
aria-label="Instagram"
>
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="2" y="2" width="20" height="20" rx="5" ry="5"></rect>
<path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z"></path>
<line x1="17.5" y1="6.5" x2="17.51" y2="6.5"></line>
</svg>
</a>
<a
href="#"
class="social-link w-9 h-9 flex items-center justify-center transition-all"
style="color: var(--t-text-secondary); border-radius: var(--t-radius-button);"
aria-label="Pinterest"
>
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"></circle>
<path d="M8 12c0-2.2 1.8-4 4-4s4 1.8 4 4-1.8 4-4 4"></path>
<line x1="12" y1="16" x2="9" y2="21"></line>
</svg>
</a>
</div>
</div>
</div>
</footer>
"""
end
end end

View File

@ -116,102 +116,6 @@ defmodule SimpleshopThemeWeb.ThemeLive.PreviewPages do
"background-repeat: no-repeat; z-index: 0;" "background-repeat: no-repeat; z-index: 0;"
end end
@doc """
Renders the shop footer with newsletter and links.
"""
attr :theme_settings, :map, required: true
def shop_footer(assigns) do
~H"""
<footer style="background-color: var(--t-surface-raised); border-top: 1px solid var(--t-border-default);">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
<div class="grid grid-cols-1 md:grid-cols-2 gap-12">
<!-- Newsletter -->
<div>
<h3 class="text-xl font-bold mb-2" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight);">
Stay in touch
</h3>
<p class="mb-4 text-sm" style="color: var(--t-text-secondary);">
Get 10% off your first order and be the first to know about new designs.
</p>
<form class="flex flex-wrap gap-2">
<input
type="email"
placeholder="your@email.com"
class="flex-1 min-w-0 px-4 py-2 text-sm"
style="background-color: var(--t-surface-base); color: var(--t-text-primary); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-input); min-width: 150px;"
/>
<button
type="submit"
class="px-6 py-2 font-medium transition-all text-sm whitespace-nowrap"
style="background-color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l)); color: var(--t-text-inverse); border-radius: var(--t-radius-button);"
>
Subscribe
</button>
</form>
</div>
<!-- Links -->
<div class="grid grid-cols-2 gap-8">
<div>
<h4 class="font-semibold mb-4 text-sm" style="font-family: var(--t-font-heading); color: var(--t-text-primary);">
Shop
</h4>
<ul class="space-y-2 text-sm">
<li><a href="#" phx-click="change_preview_page" phx-value-page="collection" class="transition-colors hover:opacity-80" style="color: var(--t-text-secondary); cursor: pointer;">All products</a></li>
<li><a href="#" phx-click="change_preview_page" phx-value-page="collection" class="transition-colors hover:opacity-80" style="color: var(--t-text-secondary); cursor: pointer;">New arrivals</a></li>
<li><a href="#" phx-click="change_preview_page" phx-value-page="collection" class="transition-colors hover:opacity-80" style="color: var(--t-text-secondary); cursor: pointer;">Best sellers</a></li>
</ul>
</div>
<div>
<h4 class="font-semibold mb-4 text-sm" style="font-family: var(--t-font-heading); color: var(--t-text-primary);">
Help
</h4>
<ul class="space-y-2 text-sm">
<li><a href="#" phx-click="change_preview_page" phx-value-page="about" class="transition-colors hover:opacity-80" style="color: var(--t-text-secondary); cursor: pointer;">Delivery</a></li>
<li><a href="#" phx-click="change_preview_page" phx-value-page="about" class="transition-colors hover:opacity-80" style="color: var(--t-text-secondary); cursor: pointer;">Returns</a></li>
<li><a href="#" phx-click="change_preview_page" phx-value-page="contact" class="transition-colors hover:opacity-80" style="color: var(--t-text-secondary); cursor: pointer;">Contact</a></li>
</ul>
</div>
</div>
</div>
<!-- Bottom Bar -->
<div class="mt-12 pt-8 flex flex-col md:flex-row justify-between items-center gap-4" style="border-top: 1px solid var(--t-border-subtle);">
<p class="text-xs" style="color: var(--t-text-tertiary);">
© 2025 <%= @theme_settings.site_name %>
</p>
<div class="flex gap-2">
<a
href="#"
class="social-link w-9 h-9 flex items-center justify-center transition-all"
style="color: var(--t-text-secondary); border-radius: var(--t-radius-button);"
aria-label="Instagram"
>
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="2" y="2" width="20" height="20" rx="5" ry="5"></rect>
<path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z"></path>
<line x1="17.5" y1="6.5" x2="17.51" y2="6.5"></line>
</svg>
</a>
<a
href="#"
class="social-link w-9 h-9 flex items-center justify-center transition-all"
style="color: var(--t-text-secondary); border-radius: var(--t-radius-button);"
aria-label="Pinterest"
>
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"></circle>
<path d="M8 12c0-2.2 1.8-4 4-4s4 1.8 4 4-1.8 4-4 4"></path>
<line x1="12" y1="16" x2="9" y2="21"></line>
</svg>
</a>
</div>
</div>
</div>
</footer>
"""
end
@doc """ @doc """
Renders the cart drawer (floating sidebar). Renders the cart drawer (floating sidebar).

View File

@ -66,7 +66,7 @@
</main> </main>
<!-- Footer --> <!-- Footer -->
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_footer theme_settings={@theme_settings} /> <.shop_footer theme_settings={@theme_settings} mode={:preview} />
<!-- Search Modal --> <!-- Search Modal -->
<!-- Cart Drawer --> <!-- Cart Drawer -->

View File

@ -119,7 +119,7 @@
</main> </main>
<!-- Footer --> <!-- Footer -->
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_footer theme_settings={@theme_settings} /> <.shop_footer theme_settings={@theme_settings} mode={:preview} />
<!-- Search Modal --> <!-- Search Modal -->
<!-- Cart Drawer --> <!-- Cart Drawer -->

View File

@ -130,7 +130,7 @@
</main> </main>
<!-- Footer --> <!-- Footer -->
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_footer theme_settings={@theme_settings} /> <.shop_footer theme_settings={@theme_settings} mode={:preview} />
<!-- Search Modal --> <!-- Search Modal -->
<!-- Cart Drawer --> <!-- Cart Drawer -->

View File

@ -186,7 +186,7 @@
</main> </main>
<!-- Footer --> <!-- Footer -->
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_footer theme_settings={@theme_settings} /> <.shop_footer theme_settings={@theme_settings} mode={:preview} />
<!-- Search Modal --> <!-- Search Modal -->
<!-- Cart Drawer --> <!-- Cart Drawer -->

View File

@ -77,7 +77,7 @@
</main> </main>
<!-- Footer --> <!-- Footer -->
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_footer theme_settings={@theme_settings} /> <.shop_footer theme_settings={@theme_settings} mode={:preview} />
<!-- Search Modal --> <!-- Search Modal -->
<!-- Cart Drawer --> <!-- Cart Drawer -->

View File

@ -151,7 +151,7 @@
</main> </main>
<!-- Footer --> <!-- Footer -->
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_footer theme_settings={@theme_settings} /> <.shop_footer theme_settings={@theme_settings} mode={:preview} />
<!-- Cart Drawer --> <!-- Cart Drawer -->
<SimpleshopThemeWeb.ThemeLive.PreviewPages.cart_drawer cart_items={SimpleshopTheme.Theme.PreviewData.cart_drawer_items()} /> <SimpleshopThemeWeb.ThemeLive.PreviewPages.cart_drawer cart_items={SimpleshopTheme.Theme.PreviewData.cart_drawer_items()} />

View File

@ -447,7 +447,7 @@
</main> </main>
<!-- Footer --> <!-- Footer -->
<SimpleshopThemeWeb.ThemeLive.PreviewPages.shop_footer theme_settings={@theme_settings} /> <.shop_footer theme_settings={@theme_settings} mode={:preview} />
<!-- Search Modal --> <!-- Search Modal -->
<!-- Cart Drawer --> <!-- Cart Drawer -->