defmodule PhoenixKit.Modules.Sitemap.HtmlGenerator do @moduledoc """ HTML sitemap renderer for PhoenixKit. Generates human-readable HTML sitemaps in three styles: - **hierarchical** - grouped by category, then by first letter - **grouped** - grouped by category/source - **flat** - single alphabetical list with multi-column layout HTML sitemaps are generated on-the-fly and cached in ETS via `Cache`. They are NOT written to disk (unlike XML sitemaps). """ alias PhoenixKit.Modules.Sitemap.Cache alias PhoenixKit.Modules.Sitemap.UrlEntry @doc """ Renders an HTML sitemap from pre-collected URL entries. Validation and cache lookup are handled by the caller (`Generator.generate_html/1`). This function only renders HTML and optionally caches the result. ## Parameters - `opts` - Options (`:style`, `:title`) - `entries` - Pre-collected URL entries - `cache_key` - Atom key for ETS cache storage - `cache_opts` - Optional: `[cache: false]` to skip caching """ @spec generate(keyword(), [UrlEntry.t()], atom(), keyword()) :: {:ok, String.t()} def generate(opts, entries, cache_key, cache_opts \\ []) when is_list(entries) do style = Keyword.get(opts, :style, "hierarchical") title = Keyword.get(opts, :title, "Sitemap") cache_enabled = Keyword.get(cache_opts, :cache, true) html = case style do "hierarchical" -> render_hierarchical(entries, title) "grouped" -> render_grouped(entries, title) "flat" -> render_flat(entries, title) end if cache_enabled, do: Cache.put(cache_key, html) {:ok, html} end # ── Internal: rendering ─────────────────────────────────────────── defp render_hierarchical(entries, title) do grouped = entries |> Enum.group_by(fn entry -> entry.category || "Other" end) |> Enum.sort_by(fn {category, _} -> category end) category_sections = Enum.map_join(grouped, "\n", fn {category, category_entries} -> letter_groups = category_entries |> Enum.group_by(fn entry -> t = entry.title || entry.loc String.upcase(String.at(t, 0) || "") end) |> Enum.sort_by(fn {letter, _} -> letter end) letter_sections = Enum.map_join(letter_groups, "\n", fn {letter, letter_entries} -> links = letter_entries |> Enum.sort_by(fn entry -> entry.title || entry.loc end) |> Enum.map_join("\n ", &render_link/1) """
Total: #{length(entries)} pages