# Legal page generator > Status: Planned > Tasks: #83–85 in PROGRESS.md > Tier: 4 (Growth & content), Phase 1 can ship earlier ## Goal Replace the static `PreviewData` placeholder content on the four policy/legal pages with generated content that's factually accurate for each shop — because Berrypod knows exactly what it does, who processes what data, and how fulfilment works. Not a generic "fill in the blanks" template. Not LLM-generated waffle. A set of conditional paragraph functions that produce correct, shop-specific content from actual settings and provider data. ## The problem with current approach All four content pages (`/privacy`, `/terms`, `/delivery`, `/about`) call `PreviewData.*_content()` which returns hardcoded placeholder text. Shop owners are expected to replace it manually — but most won't, or will copy-paste something generic that doesn't match how Berrypod actually works. Berrypod already knows: - Which providers are connected (Printify, Printful — each with different lead times) - Which countries it ships to (from the `shipping_rates` table) - Whether VAT is enabled and the shop country - Whether abandoned cart recovery is enabled - Whether a newsletter is enabled - The shop name and contact email This is enough to produce accurate, legally grounded content automatically. ## Pages covered ### 1. Privacy policy (`/privacy`) **Always included:** - What's collected: name, email, shipping address from orders (legal basis: contract performance, Article 6(1)(b) UK/EU GDPR) - Payment: processed by Stripe, card data never touches the shop - Analytics: privacy-first, no cookies, no personal data stored — server-side only, includes device type, country (derived from IP, not stored), referrer - Cookies: session cookie for cart and auth (strictly necessary, no consent required); country preference cookie for shipping rates. No tracking cookies, no third-party analytics cookies. - Sharing: shipping details shared with the connected provider(s) — names dynamically inserted - Retention: order data kept for 7 years (UK statutory accounting requirement); analytics data kept for 2 years - Contact: shop contact email from settings - Rights: right of access, rectification, deletion (with caveat: statutory retention periods apply), right to object to marketing **Conditional sections:** - Abandoned cart recovery enabled → "If you enter your email on our checkout page but don't complete payment, we may send you a single follow-up email. This is the only email you'll receive. You can unsubscribe at any time using the link in the email. We delete this data after 30 days." (UK PECR soft opt-in / EU legitimate interests — depending on shop country) - Newsletter enabled → email marketing section: subscription basis, how to unsubscribe, no third-party sharing - Stripe Tax enabled → "Tax calculation is handled by Stripe, which processes transaction and location data to determine applicable rates." **Shop country drives jurisdiction language:** - UK → "under UK GDPR and PECR" - EU country → "under the EU General Data Protection Regulation (GDPR)" - US, AU, other → generic "applicable data protection laws" --- ### 2. Delivery & returns (`/delivery`) This is the most data-rich page — Berrypod has real numbers from the DB. **Production lead times** — driven by connected provider(s): | Provider | Typical production | |----------|--------------------| | Printify | 2–7 business days | | Printful | 2–5 business days | If both providers are connected, show combined note ("production times vary by product"). **Shipping destinations** — derived from `shipping_rates` table: - Query distinct countries with rates → group into regions → list with approximate delivery windows - If no shipping data: generic placeholder (fallback) **Returns — POD-specific and legally accurate:** This is where generic templates get it wrong. The correct position for print-on-demand: - Consumer Contracts Regulations Regulation 28(1)(b) — "goods made to the consumer's specifications or clearly personalised" are **exempt** from the 14-day statutory right to cancel. Every POD product qualifies. This is the legal exemption that applies, and most shops don't cite it correctly. - Consumer Rights Act 2015 still applies to defective goods — if the item arrives damaged or with a printing defect, the customer is entitled to a repair, replacement, or refund. - The generated policy states this clearly: no change-of-mind returns (citing the exemption), but reprints/refunds for defects (citing CRA). **Contact and cancellation window:** - Contact email from settings - Cancellation window: ~2 hours after ordering (before production begins) --- ### 3. Terms of service (`/terms`) **Always included:** - Governing law: driven by shop country setting - UK → "English law" - Ireland → "Irish law and EU regulations" - etc. - Products: made to order, colour variance disclaimer, all sales final (with returns caveat) - Payment: via Stripe, orders only confirmed on successful payment - Intellectual property: designs are the property of the shop owner; customers receive a licence for personal use - Limitations: we're not liable for delays caused by the print provider or postal service - Changes: terms may be updated, current version always at this URL **Conditional:** - VAT enabled + registered → "prices include VAT where applicable" - Newsletter → marketing communications clause --- ### 4. Cookie policy Currently a section within the privacy policy. Can be a standalone page if desired, or remain embedded. **Berrypod's actual cookies (exhaustive):** | Cookie | Purpose | Duration | Consent required? | |--------|---------|----------|-------------------| | `_berrypod_session` | Session state: cart contents, auth | Session | No — strictly necessary | | `country_code` | Remember shipping country preference | 1 year | No — strictly necessary for service | That's it. No analytics cookies. No tracking. No third-party embeds. The generated cookie policy is short and accurate. --- ### 5. About page Not generated — it's the shop owner's own story, Berrypod can't write that for them. But the existing placeholder template should be clearly labelled as placeholder and easy to replace. The page editor (task #19) handles this properly. No changes needed here for Phase 1. --- ## Content block format The generator produces lists of `content_blocks` in the format already used by `<.rich_text>`: ```elixir [ %{type: :lead, text: "..."}, %{type: :heading, text: "..."}, %{type: :paragraph, text: "..."}, %{type: :list, items: ["...", "..."]}, %{type: :closing, text: "..."} ] ``` No changes to the template layer — it already knows how to render these. The generator just produces better data. --- ## Generator module `lib/berrypod/legal_pages.ex` — one public function per page: ```elixir defmodule Berrypod.LegalPages do alias Berrypod.{Settings, Shipping, Providers} def privacy_content do shop_name = Settings.get(:shop_name) || "this shop" contact_email = Settings.get(:contact_email) shop_country = Settings.get(:shop_country, "GB") abandoned_cart_enabled = Settings.get(:abandoned_cart_enabled, false) newsletter_enabled = Settings.get(:newsletter_enabled, false) base_sections() |> maybe_add_abandoned_cart(abandoned_cart_enabled) |> maybe_add_newsletter(newsletter_enabled) |> add_jurisdiction(shop_country) |> add_contact(shop_name, contact_email) end def delivery_content do providers = Providers.connected_providers() shipping_countries = Shipping.list_countries_with_rates() production_section(providers) ++ shipping_section(shipping_countries) ++ returns_section() ++ cancellation_section() end def terms_content do shop_name = Settings.get(:shop_name) || "this shop" shop_country = Settings.get(:shop_country, "GB") vat_enabled = Settings.get(:vat_enabled, false) base_terms(shop_name) |> add_governing_law(shop_country) |> maybe_add_vat_clause(vat_enabled) end end ``` --- ## Two phases ### Phase 1 — replace PreviewData (no page editor needed) Wire `LegalPages.*_content()` into the existing `Content` LiveView, replacing the three `PreviewData.*_content()` calls for privacy, delivery, and terms. The about page stays as-is (it's the shop owner's story). The generated content shows in the live shop immediately. No admin UI needed yet — the content is always accurate because it reflects real settings. ### Phase 2 — page editor integration When the page editor (task #19) ships, add: - "Regenerate from settings" button per page — reruns the generator and replaces stored content - Content marked as "auto-generated" vs "customised" — so the admin can tell what's been manually edited - Generator runs automatically when relevant settings change (provider connected, VAT toggled, abandoned cart enabled) — a PubSub broadcast triggers regeneration --- ## What the generator is and isn't **Is:** - Factually accurate based on real Berrypod behaviour - Legally grounded (cites correct UK statutes: PECR, Consumer Contracts Regulations, Consumer Rights Act) - Useful as a starting point that's better than any generic template **Isn't:** - Legal advice. The generated pages include a brief footer note: "This policy was auto-generated based on how this shop is configured. You should review it and seek independent legal advice if you're unsure." - Comprehensive for edge cases (international VAT registration, non-UK statutory frameworks beyond GDPR) - A substitute for a solicitor if the shop does complex things --- ## Files to create/modify - `lib/berrypod/legal_pages.ex` — new, generator functions for each page - `lib/berrypod_web/live/shop/content.ex` — replace three `PreviewData.*_content()` calls with `LegalPages.*_content()` - Phase 2: page editor admin UI for saved/regenerated page content --- ## Tasks | # | Task | Est | |---|------|-----| | 83 | `LegalPages` module — generate accurate privacy, delivery, and terms content from settings + provider + shipping data | 2.5h | | 84 | Wire `LegalPages` into `Content` LiveView — replace `PreviewData` calls, add tests | 45m | | 85 | Page editor integration — "Regenerate" button, auto-regenerate on settings change, customised vs auto label | 1.5h (depends on task #19) |