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""" + + """ + 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