diff --git a/lib/simpleshop_theme/theme/preview_data.ex b/lib/simpleshop_theme/theme/preview_data.ex
index e6e211b..f73f8ee 100644
--- a/lib/simpleshop_theme/theme/preview_data.ex
+++ b/lib/simpleshop_theme/theme/preview_data.ex
@@ -133,6 +133,171 @@ defmodule SimpleshopTheme.Theme.PreviewData do
]
end
+ @doc """
+ Returns delivery & returns page content for preview.
+ """
+ def delivery_content do
+ [
+ %{
+ type: :lead,
+ text:
+ "This is sample content for the demo shop. Replace it with your own delivery and returns information."
+ },
+ %{type: :heading, text: "Shipping"},
+ %{
+ type: :paragraph,
+ text:
+ "All products are printed on demand and shipped directly from the print facility. Typical delivery times depend on your location:"
+ },
+ %{
+ type: :list,
+ items: [
+ "United Kingdom: 5–8 business days",
+ "Europe: 8–12 business days",
+ "United States & Canada: 8–14 business days",
+ "Rest of world: 10–20 business days"
+ ]
+ },
+ %{
+ type: :paragraph,
+ text:
+ "You'll receive a shipping confirmation email with tracking details once your order has been dispatched."
+ },
+ %{type: :heading, text: "Returns & exchanges"},
+ %{
+ type: :paragraph,
+ text:
+ "Because every item is made to order, we can't accept returns for change of mind. However, if your order arrives damaged or with a printing defect, we'll sort it out — just get in touch within 14 days of delivery."
+ },
+ %{
+ type: :paragraph,
+ text:
+ "Please include your order number and a photo of the issue so we can resolve things quickly."
+ },
+ %{type: :heading, text: "Cancellations"},
+ %{
+ type: :paragraph,
+ text:
+ "Orders can be cancelled within 2 hours of being placed. After that, production will have started and we won't be able to make changes. Contact us as soon as possible if you need to cancel."
+ }
+ ]
+ end
+
+ @doc """
+ Returns privacy policy page content for preview.
+ """
+ def privacy_content do
+ [
+ %{
+ type: :lead,
+ text: "This is sample content for the demo shop. Replace it with your own privacy policy."
+ },
+ %{type: :heading, text: "What we collect"},
+ %{
+ type: :paragraph,
+ text:
+ "When you place an order, we collect the information needed to fulfil it — your name, email address, shipping address, and payment details. Payment is processed securely by Stripe; we never see or store your card number."
+ },
+ %{type: :heading, text: "How we use it"},
+ %{
+ type: :paragraph,
+ text: "We use your information to:"
+ },
+ %{
+ type: :list,
+ items: [
+ "Process and deliver your order",
+ "Send order confirmation and shipping updates",
+ "Respond to any queries or issues you raise"
+ ]
+ },
+ %{
+ type: :paragraph,
+ text:
+ "We won't send you marketing emails unless you've opted in, and we'll never sell your data to third parties."
+ },
+ %{type: :heading, text: "Third parties"},
+ %{
+ type: :paragraph,
+ text:
+ "To fulfil orders, we share your shipping details with our print-on-demand provider. Payment is handled by Stripe. Both process data in accordance with their own privacy policies."
+ },
+ %{type: :heading, text: "Your rights"},
+ %{
+ type: :paragraph,
+ text:
+ "You can request a copy of your data, ask us to correct it, or ask us to delete it at any time. Just get in touch and we'll sort it out."
+ },
+ %{type: :heading, text: "Cookies"},
+ %{
+ type: :paragraph,
+ text:
+ "We use essential cookies to keep your session and cart working. We don't use tracking cookies or third-party analytics that follow you around the web."
+ }
+ ]
+ end
+
+ @doc """
+ Returns terms of service page content for preview.
+ """
+ def terms_content do
+ [
+ %{
+ type: :lead,
+ text:
+ "This is sample content for the demo shop. Replace it with your own terms of service."
+ },
+ %{type: :heading, text: "Overview"},
+ %{
+ type: :paragraph,
+ text:
+ "By placing an order through this shop, you agree to the following terms. Please read them before making a purchase."
+ },
+ %{type: :heading, text: "Products"},
+ %{
+ type: :paragraph,
+ text:
+ "All products are made to order using print-on-demand services. Colours may vary slightly between screens and the finished product. We do our best to represent products accurately, but minor differences are normal."
+ },
+ %{type: :heading, text: "Orders & payment"},
+ %{
+ type: :paragraph,
+ text:
+ "Payment is taken at the time of purchase via Stripe. Once an order is confirmed and production has started, it cannot be modified. Prices include VAT where applicable."
+ },
+ %{type: :heading, text: "Delivery"},
+ %{
+ type: :paragraph,
+ text:
+ "Delivery times are estimates and may vary. We're not responsible for delays caused by postal services or customs. See our delivery page for current timeframes."
+ },
+ %{type: :heading, text: "Returns"},
+ %{
+ type: :paragraph,
+ text:
+ "As items are made to order, we don't accept returns for change of mind. If an item arrives damaged or defective, contact us within 14 days for a replacement. See our delivery & returns page for full details."
+ },
+ %{type: :heading, text: "Intellectual property"},
+ %{
+ type: :paragraph,
+ text:
+ "All designs, images, and content on this site are owned by the shop owner and may not be reproduced without permission."
+ },
+ %{type: :heading, text: "Liability"},
+ %{
+ type: :paragraph,
+ text:
+ "We do our best to keep the shop running smoothly, but we can't guarantee uninterrupted service. Our liability is limited to the value of your order."
+ },
+ %{type: :heading, text: "Changes"},
+ %{
+ type: :paragraph,
+ text:
+ "We may update these terms from time to time. Any changes apply to orders placed after the update."
+ }
+ ]
+ end
+
@doc """
Returns categories for preview.
diff --git a/lib/simpleshop_theme_web/components/page_templates/about.html.heex b/lib/simpleshop_theme_web/components/page_templates/about.html.heex
deleted file mode 100644
index 11da3f8..0000000
--- a/lib/simpleshop_theme_web/components/page_templates/about.html.heex
+++ /dev/null
@@ -1,51 +0,0 @@
-
- <.skip_link />
-
- <%= if @theme_settings.announcement_bar do %>
- <.announcement_bar theme_settings={@theme_settings} />
- <% end %>
-
- <.shop_header
- theme_settings={@theme_settings}
- logo_image={@logo_image}
- header_image={@header_image}
- active_page="about"
- mode={@mode}
- cart_count={@cart_count}
- />
-
-
- <.hero_section
- title="About the studio"
- description="Your story goes here – this is sample content for the demo shop"
- background={:sunken}
- />
-
- <.content_body
- image_src="/mockups/night-sky-blanket-3"
- image_alt="Night sky blanket draped over a chair"
- >
- <.rich_text blocks={SimpleshopTheme.Theme.PreviewData.about_content()} />
-
-
-
- <.shop_footer theme_settings={@theme_settings} mode={@mode} />
-
- <.cart_drawer
- cart_items={@cart_items}
- subtotal={@cart_subtotal}
- cart_count={@cart_count}
- mode={@mode}
- open={assigns[:cart_drawer_open] || false}
- cart_status={assigns[:cart_status]}
- />
-
- <.search_modal hint_text={~s(Try a search – e.g. "mountain" or "notebook")} />
-
- <.mobile_bottom_nav active_page="about" mode={@mode} />
-
diff --git a/lib/simpleshop_theme_web/components/page_templates/content.html.heex b/lib/simpleshop_theme_web/components/page_templates/content.html.heex
new file mode 100644
index 0000000..41c0af2
--- /dev/null
+++ b/lib/simpleshop_theme_web/components/page_templates/content.html.heex
@@ -0,0 +1,71 @@
+
+ <.skip_link />
+
+ <%= if @theme_settings.announcement_bar do %>
+ <.announcement_bar theme_settings={@theme_settings} />
+ <% end %>
+
+ <.shop_header
+ theme_settings={@theme_settings}
+ logo_image={@logo_image}
+ header_image={@header_image}
+ active_page={@active_page}
+ mode={@mode}
+ cart_count={@cart_count}
+ />
+
+
+ <%= if assigns[:hero_background] do %>
+ <.hero_section
+ title={@hero_title}
+ description={@hero_description}
+ background={@hero_background}
+ />
+ <% else %>
+ <.hero_section
+ variant={:page}
+ title={@hero_title}
+ description={@hero_description}
+ />
+ <% end %>
+
+
+ <%= if assigns[:image_src] do %>
+
+ <.responsive_image
+ src={@image_src}
+ source_width={1200}
+ alt={@image_alt}
+ sizes="(max-width: 800px) 100vw, 800px"
+ class="w-full h-[300px] object-cover"
+ />
+
+ <% end %>
+
+ <.rich_text blocks={@content_blocks} />
+
+
+
+ <.shop_footer theme_settings={@theme_settings} mode={@mode} />
+
+ <.cart_drawer
+ cart_items={@cart_items}
+ subtotal={@cart_subtotal}
+ cart_count={@cart_count}
+ mode={@mode}
+ open={assigns[:cart_drawer_open] || false}
+ cart_status={assigns[:cart_status]}
+ />
+
+ <.search_modal hint_text={~s(Try a search – e.g. "mountain" or "notebook")} />
+
+ <.mobile_bottom_nav active_page={@active_page} mode={@mode} />
+
diff --git a/lib/simpleshop_theme_web/components/shop_components.ex b/lib/simpleshop_theme_web/components/shop_components.ex
index 07c7eb6..a543712 100644
--- a/lib/simpleshop_theme_web/components/shop_components.ex
+++ b/lib/simpleshop_theme_web/components/shop_components.ex
@@ -691,22 +691,33 @@ defmodule SimpleshopThemeWeb.ShopComponents do
- Delivery
+ Delivery & returns
- Returns
+ Privacy policy
+
+
+
+
+ Terms of service
@@ -723,20 +734,29 @@ defmodule SimpleshopThemeWeb.ShopComponents do
<% else %>
- Delivery
+ Delivery & returns
- Returns
+ Privacy policy
+
+
+
+
+ Terms of service
@@ -4205,6 +4225,16 @@ defmodule SimpleshopThemeWeb.ShopComponents do
"""
end
+ defp rich_text_block(%{block: %{type: :list}} = assigns) do
+ ~H"""
+
+ <%= for item <- @block.items do %>
+ - {item}
+ <% end %>
+
+ """
+ end
+
defp rich_text_block(assigns) do
~H"""
diff --git a/lib/simpleshop_theme_web/live/shop_live/about.ex b/lib/simpleshop_theme_web/live/shop_live/about.ex
deleted file mode 100644
index a35839f..0000000
--- a/lib/simpleshop_theme_web/live/shop_live/about.ex
+++ /dev/null
@@ -1,54 +0,0 @@
-defmodule SimpleshopThemeWeb.ShopLive.About do
- use SimpleshopThemeWeb, :live_view
-
- alias SimpleshopTheme.Settings
- alias SimpleshopTheme.Media
- alias SimpleshopTheme.Theme.{CSSCache, CSSGenerator}
-
- @impl true
- def mount(_params, _session, socket) do
- theme_settings = Settings.get_theme_settings()
-
- generated_css =
- case CSSCache.get() do
- {:ok, css} ->
- css
-
- :miss ->
- css = CSSGenerator.generate(theme_settings)
- CSSCache.put(css)
- css
- end
-
- logo_image = Media.get_logo()
- header_image = Media.get_header()
-
- socket =
- socket
- |> assign(:page_title, "About")
- |> assign(:theme_settings, theme_settings)
- |> assign(:generated_css, generated_css)
- |> assign(:logo_image, logo_image)
- |> assign(:header_image, header_image)
- |> assign(:mode, :shop)
-
- {:ok, socket}
- end
-
- @impl true
- def render(assigns) do
- ~H"""
-
- """
- end
-end
diff --git a/lib/simpleshop_theme_web/live/shop_live/content.ex b/lib/simpleshop_theme_web/live/shop_live/content.ex
new file mode 100644
index 0000000..65e71ea
--- /dev/null
+++ b/lib/simpleshop_theme_web/live/shop_live/content.ex
@@ -0,0 +1,88 @@
+defmodule SimpleshopThemeWeb.ShopLive.Content do
+ use SimpleshopThemeWeb, :live_view
+
+ alias SimpleshopTheme.{Settings, Media}
+ alias SimpleshopTheme.Theme.{CSSCache, CSSGenerator, PreviewData}
+
+ @impl true
+ def mount(_params, _session, socket) do
+ theme_settings = Settings.get_theme_settings()
+
+ generated_css =
+ case CSSCache.get() do
+ {:ok, css} ->
+ css
+
+ :miss ->
+ css = CSSGenerator.generate(theme_settings)
+ CSSCache.put(css)
+ css
+ end
+
+ socket =
+ socket
+ |> assign(:theme_settings, theme_settings)
+ |> assign(:generated_css, generated_css)
+ |> assign(:logo_image, Media.get_logo())
+ |> assign(:header_image, Media.get_header())
+ |> assign(:mode, :shop)
+
+ {:ok, socket}
+ end
+
+ @impl true
+ def handle_params(_params, _uri, socket) do
+ config = page_config(socket.assigns.live_action)
+ {:noreply, assign(socket, config)}
+ end
+
+ @impl true
+ def render(assigns) do
+ ~H"""
+
+ """
+ end
+
+ defp page_config(:about) do
+ %{
+ page_title: "About",
+ active_page: "about",
+ hero_title: "About the studio",
+ hero_description: "Your story goes here – this is sample content for the demo shop",
+ hero_background: :sunken,
+ image_src: "/mockups/night-sky-blanket-3",
+ image_alt: "Night sky blanket draped over a chair",
+ content_blocks: PreviewData.about_content()
+ }
+ end
+
+ defp page_config(:delivery) do
+ %{
+ page_title: "Delivery & returns",
+ active_page: "delivery",
+ hero_title: "Delivery & returns",
+ hero_description: "Everything you need to know about shipping and returns",
+ content_blocks: PreviewData.delivery_content()
+ }
+ end
+
+ defp page_config(:privacy) do
+ %{
+ page_title: "Privacy policy",
+ active_page: "privacy",
+ hero_title: "Privacy policy",
+ hero_description: "How we handle your personal information",
+ content_blocks: PreviewData.privacy_content()
+ }
+ end
+
+ defp page_config(:terms) do
+ %{
+ page_title: "Terms of service",
+ active_page: "terms",
+ hero_title: "Terms of service",
+ hero_description: "The legal bits",
+ content_blocks: PreviewData.terms_content()
+ }
+ end
+end
diff --git a/lib/simpleshop_theme_web/live/theme_live/index.ex b/lib/simpleshop_theme_web/live/theme_live/index.ex
index cbf710d..01dbcff 100644
--- a/lib/simpleshop_theme_web/live/theme_live/index.ex
+++ b/lib/simpleshop_theme_web/live/theme_live/index.ex
@@ -431,11 +431,75 @@ defmodule SimpleshopThemeWeb.ThemeLive.Index do
defp preview_page(%{page: :about} = assigns) do
~H"""
-
+ """
+ end
+
+ defp preview_page(%{page: :delivery} = assigns) do
+ ~H"""
+
+ """
+ end
+
+ defp preview_page(%{page: :privacy} = assigns) do
+ ~H"""
+
+ """
+ end
+
+ defp preview_page(%{page: :terms} = assigns) do
+ ~H"""
+
diff --git a/lib/simpleshop_theme_web/router.ex b/lib/simpleshop_theme_web/router.ex
index 7b96870..7640b2b 100644
--- a/lib/simpleshop_theme_web/router.ex
+++ b/lib/simpleshop_theme_web/router.ex
@@ -34,7 +34,10 @@ defmodule SimpleshopThemeWeb.Router do
layout: {SimpleshopThemeWeb.Layouts, :shop},
on_mount: [{SimpleshopThemeWeb.CartHook, :mount_cart}] do
live "/", ShopLive.Home, :index
- live "/about", ShopLive.About, :index
+ live "/about", ShopLive.Content, :about
+ live "/delivery", ShopLive.Content, :delivery
+ live "/privacy", ShopLive.Content, :privacy
+ live "/terms", ShopLive.Content, :terms
live "/contact", ShopLive.Contact, :index
live "/collections/:slug", ShopLive.Collection, :show
live "/products/:id", ShopLive.ProductShow, :show
diff --git a/test/simpleshop_theme_web/live/shop_live/content_test.exs b/test/simpleshop_theme_web/live/shop_live/content_test.exs
new file mode 100644
index 0000000..984a5fb
--- /dev/null
+++ b/test/simpleshop_theme_web/live/shop_live/content_test.exs
@@ -0,0 +1,89 @@
+defmodule SimpleshopThemeWeb.ShopLive.ContentTest do
+ use SimpleshopThemeWeb.ConnCase, async: false
+
+ import Phoenix.LiveViewTest
+
+ describe "About page" do
+ test "renders about page", %{conn: conn} do
+ {:ok, _view, html} = live(conn, ~p"/about")
+
+ assert html =~ "About the studio"
+ assert html =~ "sample about page"
+ end
+
+ test "displays about image", %{conn: conn} do
+ {:ok, _view, html} = live(conn, ~p"/about")
+
+ assert html =~ "night-sky-blanket"
+ end
+ end
+
+ describe "Delivery page" do
+ test "renders delivery page", %{conn: conn} do
+ {:ok, _view, html} = live(conn, ~p"/delivery")
+
+ assert html =~ "Delivery & returns"
+ assert html =~ "shipping and returns"
+ end
+
+ test "displays delivery content", %{conn: conn} do
+ {:ok, _view, html} = live(conn, ~p"/delivery")
+
+ assert html =~ "Shipping"
+ assert html =~ "Returns & exchanges"
+ assert html =~ "Cancellations"
+ end
+
+ test "displays list items", %{conn: conn} do
+ {:ok, _view, html} = live(conn, ~p"/delivery")
+
+ assert html =~ "United Kingdom"
+ assert html =~ "5–8 business days"
+ end
+ end
+
+ describe "Privacy page" do
+ test "renders privacy page", %{conn: conn} do
+ {:ok, _view, html} = live(conn, ~p"/privacy")
+
+ assert html =~ "Privacy policy"
+ assert html =~ "personal information"
+ end
+
+ test "displays privacy content", %{conn: conn} do
+ {:ok, _view, html} = live(conn, ~p"/privacy")
+
+ assert html =~ "What we collect"
+ assert html =~ "Cookies"
+ assert html =~ "Your rights"
+ end
+ end
+
+ describe "Terms page" do
+ test "renders terms page", %{conn: conn} do
+ {:ok, _view, html} = live(conn, ~p"/terms")
+
+ assert html =~ "Terms of service"
+ assert html =~ "The legal bits"
+ end
+
+ test "displays terms content", %{conn: conn} do
+ {:ok, _view, html} = live(conn, ~p"/terms")
+
+ assert html =~ "Products"
+ assert html =~ "Orders & payment"
+ assert html =~ "Intellectual property"
+ end
+ end
+
+ describe "Footer links" do
+ test "footer contains policy page links", %{conn: conn} do
+ {:ok, _view, html} = live(conn, ~p"/about")
+
+ assert html =~ ~s(href="/delivery")
+ assert html =~ ~s(href="/privacy")
+ assert html =~ ~s(href="/terms")
+ assert html =~ ~s(href="/contact")
+ end
+ end
+end