feat: add preview page templates with theme styling
Implement all 7 preview pages showcasing theme customization: - Home page: hero, featured products, testimonials, categories - Collection page: product grid with filters and sorting - Product detail page (PDP): gallery, variants, add to cart - Cart page: cart items with quantity controls and order summary - About page: company story and values - Contact page: contact form and business information - 404 error page: error message with product suggestions Features: - All pages use CSS custom properties for theming - Preview data from PreviewData module (mock products, testimonials, categories) - Responsive layouts with Tailwind utilities - Grid columns respect theme settings - Colors, typography, shapes, and spacing all theme-aware - Components created as embed_templates for clean separation Technical implementation: - Created PreviewPages component module with embed_templates - Wired up preview_data in LiveView mount - Updated index.html.heex to render preview pages based on @preview_page - All pages styled with inline styles using CSS variables - Scrollable preview frame with max-height All tests passing (197 total). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
da770f121f
commit
6a3069f854
@ -2,12 +2,18 @@ defmodule SimpleshopThemeWeb.ThemeLive.Index do
|
||||
use SimpleshopThemeWeb, :live_view
|
||||
|
||||
alias SimpleshopTheme.Settings
|
||||
alias SimpleshopTheme.Theme.{CSSGenerator, Presets}
|
||||
alias SimpleshopTheme.Theme.{CSSGenerator, Presets, PreviewData}
|
||||
|
||||
@impl true
|
||||
def mount(_params, _session, socket) do
|
||||
theme_settings = Settings.get_theme_settings()
|
||||
generated_css = CSSGenerator.generate(theme_settings)
|
||||
preview_data = %{
|
||||
products: PreviewData.products(),
|
||||
cart_items: PreviewData.cart_items(),
|
||||
testimonials: PreviewData.testimonials(),
|
||||
categories: PreviewData.categories()
|
||||
}
|
||||
|
||||
socket =
|
||||
socket
|
||||
@ -15,6 +21,7 @@ defmodule SimpleshopThemeWeb.ThemeLive.Index do
|
||||
|> assign(:generated_css, generated_css)
|
||||
|> assign(:preview_page, :home)
|
||||
|> assign(:preset_names, Presets.list_names())
|
||||
|> assign(:preview_data, preview_data)
|
||||
|
||||
{:ok, socket}
|
||||
end
|
||||
|
||||
@ -103,56 +103,27 @@
|
||||
</div>
|
||||
|
||||
<!-- Preview Frame -->
|
||||
<div class="preview-frame bg-white" style="min-height: 600px;">
|
||||
<div class="preview-frame bg-white overflow-auto" style="min-height: 600px; max-height: calc(100vh - 200px);">
|
||||
<style>
|
||||
<%= Phoenix.HTML.raw(@generated_css) %>
|
||||
</style>
|
||||
|
||||
<div class="p-8 text-center">
|
||||
<h2 class="text-2xl font-bold mb-4" style="font-family: var(--t-font-heading); color: var(--t-text-primary);">
|
||||
Preview: <%= String.capitalize(to_string(@preview_page)) %>
|
||||
</h2>
|
||||
|
||||
<div class="max-w-2xl mx-auto space-y-4">
|
||||
<p style="color: var(--t-text-secondary);">
|
||||
This is a preview of the <strong><%= @preview_page %></strong> page with your current theme settings.
|
||||
</p>
|
||||
|
||||
<div class="flex gap-2 justify-center">
|
||||
<button
|
||||
class="px-4 py-2 rounded text-white font-medium"
|
||||
style="background-color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l)); border-radius: var(--t-radius-button);"
|
||||
>
|
||||
Primary Button
|
||||
</button>
|
||||
<button
|
||||
class="px-4 py-2 rounded font-medium"
|
||||
style="border: 2px solid var(--t-border-default); border-radius: var(--t-radius-button); color: var(--t-text-primary); background-color: var(--t-surface-base);"
|
||||
>
|
||||
Secondary Button
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="p-6 rounded shadow-sm"
|
||||
style="background-color: var(--t-surface-raised); border-radius: var(--t-radius-card); border: 1px solid var(--t-border-default);"
|
||||
>
|
||||
<h3
|
||||
class="text-lg font-semibold mb-2"
|
||||
style="font-family: var(--t-font-heading); color: var(--t-text-primary);"
|
||||
>
|
||||
Card Example
|
||||
</h3>
|
||||
<p style="font-family: var(--t-font-body); color: var(--t-text-secondary);">
|
||||
This card demonstrates the current surface, border, and text colors with the selected shape style.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="text-sm" style="color: var(--t-text-tertiary);">
|
||||
Detailed preview pages coming in Phase 5
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<%= case @preview_page do %>
|
||||
<% :home -> %>
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.home preview_data={@preview_data} theme_settings={@theme_settings} />
|
||||
<% :collection -> %>
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.collection preview_data={@preview_data} theme_settings={@theme_settings} />
|
||||
<% :pdp -> %>
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.pdp preview_data={@preview_data} theme_settings={@theme_settings} />
|
||||
<% :cart -> %>
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.cart preview_data={@preview_data} theme_settings={@theme_settings} />
|
||||
<% :about -> %>
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.about preview_data={@preview_data} theme_settings={@theme_settings} />
|
||||
<% :contact -> %>
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.contact preview_data={@preview_data} theme_settings={@theme_settings} />
|
||||
<% :error -> %>
|
||||
<SimpleshopThemeWeb.ThemeLive.PreviewPages.error preview_data={@preview_data} theme_settings={@theme_settings} />
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
defmodule SimpleshopThemeWeb.ThemeLive.PreviewPages do
|
||||
use Phoenix.Component
|
||||
|
||||
embed_templates "preview_pages/*"
|
||||
end
|
||||
@ -0,0 +1,67 @@
|
||||
<div class="min-h-screen" style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);">
|
||||
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
|
||||
<h1 class="text-4xl md:text-5xl font-bold mb-6 text-center" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight); letter-spacing: var(--t-heading-tracking);">
|
||||
About Us
|
||||
</h1>
|
||||
|
||||
<p class="text-lg mb-12 text-center max-w-2xl mx-auto" style="color: var(--t-text-secondary);">
|
||||
We're passionate about bringing you the finest products, handpicked with care and attention to detail.
|
||||
</p>
|
||||
|
||||
<div class="aspect-video bg-gray-200 mb-12 overflow-hidden" style="border-radius: var(--t-radius-card);">
|
||||
<img
|
||||
src="https://placehold.co/1200x675/e5e5e5/525252?text=Our+Story"
|
||||
alt="Our Story"
|
||||
class="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="prose max-w-none mb-16">
|
||||
<h2 class="text-2xl font-bold mb-4" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight);">
|
||||
Our Story
|
||||
</h2>
|
||||
<p class="mb-4 leading-relaxed" style="color: var(--t-text-secondary);">
|
||||
Founded in 2020, our journey began with a simple mission: to curate and deliver exceptional products that enhance everyday life. We believe that quality shouldn't be compromised, and that's why every item in our collection is carefully selected for its craftsmanship, sustainability, and timeless appeal.
|
||||
</p>
|
||||
<p class="mb-4 leading-relaxed" style="color: var(--t-text-secondary);">
|
||||
What started as a small passion project has grown into a community of like-minded individuals who appreciate the finer things in life. We work directly with artisans and makers who share our values of quality, authenticity, and ethical production.
|
||||
</p>
|
||||
|
||||
<h2 class="text-2xl font-bold mb-4 mt-8" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight);">
|
||||
Our Values
|
||||
</h2>
|
||||
<div class="grid gap-6 md:grid-cols-3 mb-8">
|
||||
<div class="p-6" style="background-color: var(--t-surface-raised); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-card);">
|
||||
<h3 class="font-bold mb-2" style="color: var(--t-text-primary);">Quality First</h3>
|
||||
<p class="text-sm" style="color: var(--t-text-secondary);">
|
||||
Every product is vetted for exceptional quality and durability.
|
||||
</p>
|
||||
</div>
|
||||
<div class="p-6" style="background-color: var(--t-surface-raised); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-card);">
|
||||
<h3 class="font-bold mb-2" style="color: var(--t-text-primary);">Sustainability</h3>
|
||||
<p class="text-sm" style="color: var(--t-text-secondary);">
|
||||
We prioritize eco-friendly materials and ethical production methods.
|
||||
</p>
|
||||
</div>
|
||||
<div class="p-6" style="background-color: var(--t-surface-raised); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-card);">
|
||||
<h3 class="font-bold mb-2" style="color: var(--t-text-primary);">Community</h3>
|
||||
<p class="text-sm" style="color: var(--t-text-secondary);">
|
||||
Supporting local artisans and building lasting relationships.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center">
|
||||
<h2 class="text-2xl font-bold mb-6" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight);">
|
||||
Ready to Explore?
|
||||
</h2>
|
||||
<button
|
||||
class="px-8 py-3 font-semibold transition-all"
|
||||
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);"
|
||||
>
|
||||
Shop Our Collection
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,109 @@
|
||||
<div class="min-h-screen" style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<h1 class="text-3xl md:text-4xl font-bold mb-8" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight); letter-spacing: var(--t-heading-tracking);">
|
||||
Shopping Cart
|
||||
</h1>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
<!-- Cart Items -->
|
||||
<div class="lg:col-span-2">
|
||||
<div class="space-y-4">
|
||||
<%= for item <- @preview_data.cart_items do %>
|
||||
<div
|
||||
class="flex gap-4 p-4"
|
||||
style="background-color: var(--t-surface-raised); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-card);"
|
||||
>
|
||||
<div class="w-24 h-24 flex-shrink-0 bg-gray-200 overflow-hidden" style="border-radius: var(--t-radius-image);">
|
||||
<img
|
||||
src={item.product.image_url}
|
||||
alt={item.product.name}
|
||||
class="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<h3 class="font-semibold mb-1" style="font-family: var(--t-font-heading); color: var(--t-text-primary);">
|
||||
<%= item.product.name %>
|
||||
</h3>
|
||||
<p class="text-sm mb-2" style="color: var(--t-text-secondary);">
|
||||
<%= item.variant %>
|
||||
</p>
|
||||
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex items-center" style="border: 1px solid var(--t-border-default); border-radius: var(--t-radius-input);">
|
||||
<button class="px-3 py-1" style="color: var(--t-text-primary);">−</button>
|
||||
<span class="px-3 py-1 border-x" style="border-color: var(--t-border-default); color: var(--t-text-primary);">
|
||||
<%= item.quantity %>
|
||||
</span>
|
||||
<button class="px-3 py-1" style="color: var(--t-text-primary);">+</button>
|
||||
</div>
|
||||
|
||||
<button class="text-sm" style="color: var(--t-text-tertiary);">
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-right">
|
||||
<p class="font-bold text-lg" style="color: var(--t-text-primary);">
|
||||
$<%= item.product.price / 100 * item.quantity %>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Order Summary -->
|
||||
<div>
|
||||
<div
|
||||
class="p-6 sticky top-4"
|
||||
style="background-color: var(--t-surface-raised); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-card);"
|
||||
>
|
||||
<h2 class="text-xl font-bold mb-6" style="font-family: var(--t-font-heading); color: var(--t-text-primary);">
|
||||
Order Summary
|
||||
</h2>
|
||||
|
||||
<div class="space-y-3 mb-6">
|
||||
<div class="flex justify-between">
|
||||
<span style="color: var(--t-text-secondary);">Subtotal</span>
|
||||
<span style="color: var(--t-text-primary);">
|
||||
$<%= Enum.reduce(@preview_data.cart_items, 0, fn item, acc -> acc + item.product.price * item.quantity end) / 100 %>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span style="color: var(--t-text-secondary);">Shipping</span>
|
||||
<span style="color: var(--t-text-primary);">$10.00</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span style="color: var(--t-text-secondary);">Tax</span>
|
||||
<span style="color: var(--t-text-primary);">$8.50</span>
|
||||
</div>
|
||||
<div class="border-t pt-3" style="border-color: var(--t-border-default);">
|
||||
<div class="flex justify-between text-lg">
|
||||
<span class="font-semibold" style="color: var(--t-text-primary);">Total</span>
|
||||
<span class="font-bold" style="color: var(--t-text-primary);">
|
||||
$<%= (Enum.reduce(@preview_data.cart_items, 0, fn item, acc -> acc + item.product.price * item.quantity end) / 100 + 10.00 + 8.50) |> Float.round(2) %>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="w-full px-6 py-3 font-semibold transition-all mb-3"
|
||||
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);"
|
||||
>
|
||||
Checkout
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="w-full px-6 py-3 font-semibold transition-all"
|
||||
style="border: 2px solid var(--t-border-default); color: var(--t-text-primary); border-radius: var(--t-radius-button);"
|
||||
>
|
||||
Continue Shopping
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,159 @@
|
||||
<div class="min-h-screen" style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);">
|
||||
<!-- Header -->
|
||||
<div class="border-b" style="background-color: var(--t-surface-raised); border-color: var(--t-border-default);">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<h1 class="text-3xl md:text-4xl font-bold mb-2" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight); letter-spacing: var(--t-heading-tracking);">
|
||||
All Products
|
||||
</h1>
|
||||
<p style="color: var(--t-text-secondary);">
|
||||
<%= length(@preview_data.products) %> products
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<div class="flex flex-col md:flex-row gap-8">
|
||||
<!-- Filters Sidebar -->
|
||||
<div class="w-full md:w-64 flex-shrink-0">
|
||||
<div class="p-4" style="background-color: var(--t-surface-raised); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-card);">
|
||||
<h3 class="font-semibold mb-4" style="font-family: var(--t-font-heading); color: var(--t-text-primary);">
|
||||
Filter by Category
|
||||
</h3>
|
||||
<div class="space-y-2">
|
||||
<%= for category <- @preview_data.categories do %>
|
||||
<label class="flex items-center gap-2 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="rounded"
|
||||
style="color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l)); border-radius: var(--t-radius-input);"
|
||||
/>
|
||||
<span style="color: var(--t-text-primary);"><%= category.name %></span>
|
||||
<span class="ml-auto text-sm" style="color: var(--t-text-tertiary);">
|
||||
<%= category.product_count %>
|
||||
</span>
|
||||
</label>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 pt-6" style="border-top: 1px solid var(--t-border-subtle);">
|
||||
<h3 class="font-semibold mb-4" style="font-family: var(--t-font-heading); color: var(--t-text-primary);">
|
||||
Price Range
|
||||
</h3>
|
||||
<div class="space-y-2">
|
||||
<label class="flex items-center gap-2 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="rounded"
|
||||
style="color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));"
|
||||
/>
|
||||
<span style="color: var(--t-text-primary);">Under $25</span>
|
||||
</label>
|
||||
<label class="flex items-center gap-2 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="rounded"
|
||||
style="color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));"
|
||||
/>
|
||||
<span style="color: var(--t-text-primary);">$25 - $50</span>
|
||||
</label>
|
||||
<label class="flex items-center gap-2 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="rounded"
|
||||
style="color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));"
|
||||
/>
|
||||
<span style="color: var(--t-text-primary);">$50 - $100</span>
|
||||
</label>
|
||||
<label class="flex items-center gap-2 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="rounded"
|
||||
style="color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));"
|
||||
/>
|
||||
<span style="color: var(--t-text-primary);">Over $100</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Product Grid -->
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<p style="color: var(--t-text-secondary);">
|
||||
Showing all <%= length(@preview_data.products) %> products
|
||||
</p>
|
||||
<select
|
||||
class="px-4 py-2"
|
||||
style="background-color: var(--t-surface-raised); color: var(--t-text-primary); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-input);"
|
||||
>
|
||||
<option>Sort by: Featured</option>
|
||||
<option>Price: Low to High</option>
|
||||
<option>Price: High to Low</option>
|
||||
<option>Newest</option>
|
||||
<option>Best Selling</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class={"grid gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-#{@theme_settings.grid_columns || "3"}"}>
|
||||
<%= for product <- @preview_data.products do %>
|
||||
<div
|
||||
class="group overflow-hidden transition-all"
|
||||
style="background-color: var(--t-surface-raised); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-card);"
|
||||
>
|
||||
<div class="aspect-square bg-gray-200 overflow-hidden relative">
|
||||
<%= if product.on_sale do %>
|
||||
<div class="absolute top-2 right-2 px-2 py-1 text-xs font-bold text-white rounded" style="background-color: var(--t-sale-color);">
|
||||
SALE
|
||||
</div>
|
||||
<% end %>
|
||||
<%= if not product.in_stock do %>
|
||||
<div class="absolute inset-0 bg-black bg-opacity-50 flex items-center justify-center">
|
||||
<span class="text-white font-semibold">Out of Stock</span>
|
||||
</div>
|
||||
<% end %>
|
||||
<img
|
||||
src={product.image_url}
|
||||
alt={product.name}
|
||||
class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
|
||||
/>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<p class="text-xs mb-1" style="color: var(--t-text-tertiary);">
|
||||
<%= product.category %>
|
||||
</p>
|
||||
<h3 class="font-semibold mb-2" style="font-family: var(--t-font-heading); color: var(--t-text-primary);">
|
||||
<%= product.name %>
|
||||
</h3>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<%= if product.on_sale do %>
|
||||
<span class="text-lg font-bold" style="color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));">
|
||||
$<%= product.price / 100 %>
|
||||
</span>
|
||||
<span class="text-sm line-through ml-2" style="color: var(--t-text-tertiary);">
|
||||
$<%= product.compare_at_price / 100 %>
|
||||
</span>
|
||||
<% else %>
|
||||
<span class="text-lg font-bold" style="color: var(--t-text-primary);">
|
||||
$<%= product.price / 100 %>
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
<%= if product.in_stock do %>
|
||||
<button
|
||||
class="px-3 py-1.5 text-sm font-medium transition-all"
|
||||
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);"
|
||||
>
|
||||
Add
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,124 @@
|
||||
<div class="min-h-screen" style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);">
|
||||
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
|
||||
<h1 class="text-4xl md:text-5xl font-bold mb-6 text-center" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight); letter-spacing: var(--t-heading-tracking);">
|
||||
Contact Us
|
||||
</h1>
|
||||
|
||||
<p class="text-lg mb-12 text-center max-w-2xl mx-auto" style="color: var(--t-text-secondary);">
|
||||
Have a question or comment? We'd love to hear from you. Send us a message and we'll respond as soon as possible.
|
||||
</p>
|
||||
|
||||
<div class="grid gap-8 md:grid-cols-2 mb-12">
|
||||
<!-- Contact Form -->
|
||||
<div
|
||||
class="p-8"
|
||||
style="background-color: var(--t-surface-raised); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-card);"
|
||||
>
|
||||
<h2 class="text-xl font-bold mb-6" style="font-family: var(--t-font-heading); color: var(--t-text-primary);">
|
||||
Send us a message
|
||||
</h2>
|
||||
|
||||
<form class="space-y-4">
|
||||
<div>
|
||||
<label class="block font-medium mb-2" style="color: var(--t-text-primary);">
|
||||
Name
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Your name"
|
||||
class="w-full px-4 py-2"
|
||||
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);"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block font-medium mb-2" style="color: var(--t-text-primary);">
|
||||
Email
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
placeholder="your@email.com"
|
||||
class="w-full px-4 py-2"
|
||||
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);"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block font-medium mb-2" style="color: var(--t-text-primary);">
|
||||
Subject
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="How can we help?"
|
||||
class="w-full px-4 py-2"
|
||||
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);"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block font-medium mb-2" style="color: var(--t-text-primary);">
|
||||
Message
|
||||
</label>
|
||||
<textarea
|
||||
rows="5"
|
||||
placeholder="Your message..."
|
||||
class="w-full px-4 py-2"
|
||||
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);"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="w-full px-6 py-3 font-semibold transition-all"
|
||||
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);"
|
||||
>
|
||||
Send Message
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Contact Info -->
|
||||
<div class="space-y-6">
|
||||
<div
|
||||
class="p-6"
|
||||
style="background-color: var(--t-surface-raised); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-card);"
|
||||
>
|
||||
<h3 class="font-bold mb-2" style="color: var(--t-text-primary);">Email</h3>
|
||||
<p style="color: var(--t-text-secondary);">hello@example.com</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="p-6"
|
||||
style="background-color: var(--t-surface-raised); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-card);"
|
||||
>
|
||||
<h3 class="font-bold mb-2" style="color: var(--t-text-primary);">Phone</h3>
|
||||
<p style="color: var(--t-text-secondary);">(555) 123-4567</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="p-6"
|
||||
style="background-color: var(--t-surface-raised); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-card);"
|
||||
>
|
||||
<h3 class="font-bold mb-2" style="color: var(--t-text-primary);">Address</h3>
|
||||
<p style="color: var(--t-text-secondary);">
|
||||
123 Main Street<br />
|
||||
San Francisco, CA 94102<br />
|
||||
United States
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="p-6"
|
||||
style="background-color: var(--t-surface-raised); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-card);"
|
||||
>
|
||||
<h3 class="font-bold mb-2" style="color: var(--t-text-primary);">Hours</h3>
|
||||
<p style="color: var(--t-text-secondary);">
|
||||
Monday - Friday: 9am - 6pm<br />
|
||||
Saturday: 10am - 4pm<br />
|
||||
Sunday: Closed
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,56 @@
|
||||
<div class="min-h-screen flex items-center justify-center" style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);">
|
||||
<div class="max-w-2xl mx-auto px-4 sm:px-6 lg:px-8 py-16 text-center">
|
||||
<h1 class="text-8xl md:text-9xl font-bold mb-4" style="font-family: var(--t-font-heading); color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l)); font-weight: var(--t-heading-weight);">
|
||||
404
|
||||
</h1>
|
||||
|
||||
<h2 class="text-3xl md:text-4xl font-bold mb-6" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight); letter-spacing: var(--t-heading-tracking);">
|
||||
Page Not Found
|
||||
</h2>
|
||||
|
||||
<p class="text-lg mb-8 max-w-md mx-auto" style="color: var(--t-text-secondary);">
|
||||
Sorry, we couldn't find the page you're looking for. Perhaps you've mistyped the URL or the page has been moved.
|
||||
</p>
|
||||
|
||||
<div class="flex flex-col sm:flex-row gap-4 justify-center">
|
||||
<button
|
||||
class="px-8 py-3 font-semibold transition-all"
|
||||
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);"
|
||||
>
|
||||
Go to Homepage
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="px-8 py-3 font-semibold transition-all"
|
||||
style="border: 2px solid var(--t-border-default); color: var(--t-text-primary); border-radius: var(--t-radius-button);"
|
||||
>
|
||||
Browse Products
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="mt-12 grid gap-4 grid-cols-2 md:grid-cols-4 max-w-xl mx-auto">
|
||||
<%= for product <- Enum.take(@preview_data.products, 4) do %>
|
||||
<div
|
||||
class="group overflow-hidden"
|
||||
style="background-color: var(--t-surface-raised); border: 1px solid var(--t-border-subtle); border-radius: var(--t-radius-card);"
|
||||
>
|
||||
<div class="aspect-square bg-gray-200 overflow-hidden">
|
||||
<img
|
||||
src={product.image_url}
|
||||
alt={product.name}
|
||||
class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
|
||||
/>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<p class="text-xs font-semibold truncate" style="color: var(--t-text-primary);">
|
||||
<%= product.name %>
|
||||
</p>
|
||||
<p class="text-xs" style="color: var(--t-text-secondary);">
|
||||
$<%= product.price / 100 %>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,134 @@
|
||||
<div class="min-h-screen" style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);">
|
||||
<!-- Hero Section -->
|
||||
<div class="relative" style="background-color: var(--t-surface-raised);">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16 md:py-24">
|
||||
<div class="text-center">
|
||||
<h1 class="text-4xl md:text-6xl font-bold mb-6" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight); letter-spacing: var(--t-heading-tracking);">
|
||||
Welcome to Our Store
|
||||
</h1>
|
||||
<p class="text-lg md:text-xl mb-8 max-w-2xl mx-auto" style="color: var(--t-text-secondary);">
|
||||
Discover our curated collection of handpicked products crafted with care
|
||||
</p>
|
||||
<button
|
||||
class="px-8 py-3 font-semibold transition-all"
|
||||
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);"
|
||||
>
|
||||
Shop Now
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Featured Products -->
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
|
||||
<h2 class="text-3xl font-bold mb-8" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight);">
|
||||
Featured Products
|
||||
</h2>
|
||||
|
||||
<div class={"grid gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-#{@theme_settings.grid_columns || "3"}"}>
|
||||
<%= for product <- Enum.take(@preview_data.products, 6) do %>
|
||||
<div
|
||||
class="group overflow-hidden transition-all"
|
||||
style="background-color: var(--t-surface-raised); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-card);"
|
||||
>
|
||||
<div class="aspect-square bg-gray-200 overflow-hidden">
|
||||
<img
|
||||
src={product.image_url}
|
||||
alt={product.name}
|
||||
class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
|
||||
/>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<h3 class="font-semibold mb-2" style="font-family: var(--t-font-heading); color: var(--t-text-primary);">
|
||||
<%= product.name %>
|
||||
</h3>
|
||||
<p class="text-sm mb-3" style="color: var(--t-text-secondary);">
|
||||
<%= product.description %>
|
||||
</p>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<%= if product.on_sale do %>
|
||||
<span class="text-lg font-bold" style="color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));">
|
||||
$<%= product.price / 100 %>
|
||||
</span>
|
||||
<span class="text-sm line-through ml-2" style="color: var(--t-text-tertiary);">
|
||||
$<%= product.compare_at_price / 100 %>
|
||||
</span>
|
||||
<% else %>
|
||||
<span class="text-lg font-bold" style="color: var(--t-text-primary);">
|
||||
$<%= product.price / 100 %>
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
<button
|
||||
class="px-4 py-2 text-sm font-medium transition-all"
|
||||
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);"
|
||||
>
|
||||
Add to Cart
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Testimonials -->
|
||||
<div class="py-16" style="background-color: var(--t-surface-sunken);">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<h2 class="text-3xl font-bold mb-12 text-center" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight);">
|
||||
What Our Customers Say
|
||||
</h2>
|
||||
|
||||
<div class="grid gap-6 grid-cols-1 md:grid-cols-3">
|
||||
<%= for testimonial <- Enum.take(@preview_data.testimonials, 3) do %>
|
||||
<div class="p-6" style="background-color: var(--t-surface-base); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-card);">
|
||||
<div class="flex mb-3">
|
||||
<%= for _ <- 1..testimonial.rating do %>
|
||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" style="color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));">
|
||||
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
|
||||
</svg>
|
||||
<% end %>
|
||||
</div>
|
||||
<p class="mb-4" style="color: var(--t-text-primary);">
|
||||
"<%= testimonial.content %>"
|
||||
</p>
|
||||
<p class="font-semibold" style="color: var(--t-text-secondary);">
|
||||
— <%= testimonial.author %>
|
||||
</p>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Categories -->
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
|
||||
<h2 class="text-3xl font-bold mb-8" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight);">
|
||||
Shop by Category
|
||||
</h2>
|
||||
|
||||
<div class="grid gap-6 grid-cols-2 md:grid-cols-3 lg:grid-cols-5">
|
||||
<%= for category <- @preview_data.categories do %>
|
||||
<div
|
||||
class="group cursor-pointer overflow-hidden"
|
||||
style="border-radius: var(--t-radius-card);"
|
||||
>
|
||||
<div class="aspect-square bg-gray-200 overflow-hidden mb-3">
|
||||
<img
|
||||
src={category.image_url}
|
||||
alt={category.name}
|
||||
class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
|
||||
/>
|
||||
</div>
|
||||
<h3 class="font-semibold text-center" style="font-family: var(--t-font-heading); color: var(--t-text-primary);">
|
||||
<%= category.name %>
|
||||
</h3>
|
||||
<p class="text-sm text-center" style="color: var(--t-text-tertiary);">
|
||||
<%= category.product_count %> products
|
||||
</p>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,168 @@
|
||||
<%
|
||||
product = List.first(@preview_data.products)
|
||||
%>
|
||||
<div class="min-h-screen" style="background-color: var(--t-surface-base); font-family: var(--t-font-body); color: var(--t-text-primary);">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<!-- Breadcrumb -->
|
||||
<div class="mb-8 flex items-center gap-2 text-sm" style="color: var(--t-text-secondary);">
|
||||
<span>Home</span>
|
||||
<span>/</span>
|
||||
<span><%= product.category %></span>
|
||||
<span>/</span>
|
||||
<span style="color: var(--t-text-primary);"><%= product.name %></span>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-12 mb-16">
|
||||
<!-- Product Images -->
|
||||
<div>
|
||||
<div class="aspect-square bg-gray-200 mb-4 overflow-hidden" style="border-radius: var(--t-radius-image);">
|
||||
<img
|
||||
src={product.image_url}
|
||||
alt={product.name}
|
||||
class="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-4 gap-4">
|
||||
<%= for img_url <- [product.image_url, product.hover_image_url, product.image_url, product.hover_image_url] do %>
|
||||
<div class="aspect-square bg-gray-200 cursor-pointer overflow-hidden" style="border: 2px solid var(--t-border-default); border-radius: var(--t-radius-image);">
|
||||
<img
|
||||
src={img_url}
|
||||
alt={product.name}
|
||||
class="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Product Info -->
|
||||
<div>
|
||||
<h1 class="text-3xl md:text-4xl font-bold mb-4" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight); letter-spacing: var(--t-heading-tracking);">
|
||||
<%= product.name %>
|
||||
</h1>
|
||||
|
||||
<div class="flex items-center gap-4 mb-6">
|
||||
<%= if product.on_sale do %>
|
||||
<span class="text-3xl font-bold" style="color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));">
|
||||
$<%= product.price / 100 %>
|
||||
</span>
|
||||
<span class="text-xl line-through" style="color: var(--t-text-tertiary);">
|
||||
$<%= product.compare_at_price / 100 %>
|
||||
</span>
|
||||
<span class="px-2 py-1 text-sm font-bold text-white rounded" style="background-color: var(--t-sale-color);">
|
||||
SAVE <%= round((product.compare_at_price - product.price) / product.compare_at_price * 100) %>%
|
||||
</span>
|
||||
<% else %>
|
||||
<span class="text-3xl font-bold" style="color: var(--t-text-primary);">
|
||||
$<%= product.price / 100 %>
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<p class="mb-8 leading-relaxed" style="color: var(--t-text-secondary);">
|
||||
<%= product.description %>. Crafted with attention to detail and quality materials, this product is designed to last. Perfect for everyday use or special occasions.
|
||||
</p>
|
||||
|
||||
<!-- Variant Options -->
|
||||
<div class="mb-6">
|
||||
<label class="block font-semibold mb-2" style="color: var(--t-text-primary);">
|
||||
Size
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<%= for size <- ["S", "M", "L", "XL"] do %>
|
||||
<button
|
||||
class="px-4 py-2 font-medium transition-all"
|
||||
style="border: 2px solid var(--t-border-default); border-radius: var(--t-radius-button); color: var(--t-text-primary);"
|
||||
>
|
||||
<%= size %>
|
||||
</button>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quantity -->
|
||||
<div class="mb-8">
|
||||
<label class="block font-semibold mb-2" style="color: var(--t-text-primary);">
|
||||
Quantity
|
||||
</label>
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex items-center" style="border: 2px solid var(--t-border-default); border-radius: var(--t-radius-input);">
|
||||
<button class="px-4 py-2" style="color: var(--t-text-primary);">−</button>
|
||||
<span class="px-4 py-2 border-x-2" style="border-color: var(--t-border-default); color: var(--t-text-primary);">
|
||||
1
|
||||
</span>
|
||||
<button class="px-4 py-2" style="color: var(--t-text-primary);">+</button>
|
||||
</div>
|
||||
<%= if product.in_stock do %>
|
||||
<span class="text-sm" style="color: var(--t-text-tertiary);">In stock</span>
|
||||
<% else %>
|
||||
<span class="text-sm font-semibold" style="color: var(--t-sale-color);">Out of stock</span>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add to Cart -->
|
||||
<button
|
||||
class="w-full px-6 py-4 text-lg font-semibold transition-all mb-4"
|
||||
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);"
|
||||
>
|
||||
Add to Cart
|
||||
</button>
|
||||
|
||||
<!-- Features -->
|
||||
<div class="p-4 space-y-3" style="background-color: var(--t-surface-sunken); border-radius: var(--t-radius-card);">
|
||||
<div class="flex items-start gap-3">
|
||||
<svg class="w-5 h-5 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24" style="color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
<div>
|
||||
<p class="font-semibold" style="color: var(--t-text-primary);">Free Shipping</p>
|
||||
<p class="text-sm" style="color: var(--t-text-secondary);">On orders over $50</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-3">
|
||||
<svg class="w-5 h-5 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24" style="color: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<div>
|
||||
<p class="font-semibold" style="color: var(--t-text-primary);">Easy Returns</p>
|
||||
<p class="text-sm" style="color: var(--t-text-secondary);">30-day return policy</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Related Products -->
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold mb-6" style="font-family: var(--t-font-heading); color: var(--t-text-primary); font-weight: var(--t-heading-weight);">
|
||||
You May Also Like
|
||||
</h2>
|
||||
|
||||
<div class="grid gap-6 grid-cols-2 md:grid-cols-4">
|
||||
<%= for related_product <- Enum.slice(@preview_data.products, 1, 4) do %>
|
||||
<div
|
||||
class="group overflow-hidden"
|
||||
style="background-color: var(--t-surface-raised); border: 1px solid var(--t-border-default); border-radius: var(--t-radius-card);"
|
||||
>
|
||||
<div class="aspect-square bg-gray-200 overflow-hidden">
|
||||
<img
|
||||
src={related_product.image_url}
|
||||
alt={related_product.name}
|
||||
class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
|
||||
/>
|
||||
</div>
|
||||
<div class="p-3">
|
||||
<h3 class="font-semibold text-sm mb-1" style="font-family: var(--t-font-heading); color: var(--t-text-primary);">
|
||||
<%= related_product.name %>
|
||||
</h3>
|
||||
<p class="font-bold" style="color: var(--t-text-primary);">
|
||||
$<%= related_product.price / 100 %>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -90,7 +90,7 @@ defmodule SimpleshopThemeWeb.ThemeLiveTest do
|
||||
|> element("button", "Collection")
|
||||
|> render_click()
|
||||
|
||||
assert html =~ "Preview: Collection"
|
||||
assert html =~ "All Products"
|
||||
end
|
||||
|
||||
test "save theme button works", %{conn: conn} do
|
||||
|
||||
Loading…
Reference in New Issue
Block a user