# `PhoenixKit.Email.Catalog`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v2.55.1/lib/phoenix_kit/email/catalog.ex#L1)

The emails the system knows it sends, and a preview of each.

Core's own emails come from `PhoenixKit.Email.CoreTemplates`; a module adds
its own through `c:PhoenixKit.Module.email_templates/0`. The admin preview
(`/admin/settings/email-sending/preview`) lists them and renders each one
with `preview/3`.

## An entry

    %{
      name: "billing_invoice",               # the template name the send uses
      label: "Invoice",                      # shown in the list
      description: "Sent when …",            # optional
      defaults: &MyModule.invoice_defaults/0, # optional, the send's own defaults
      variables: fn -> %{"invoice_number" => "INV-0001"} end, # optional samples
      layout: "billing"                      # optional, the send's :layout option
    }

`defaults` must be the very function the send passes to
`PhoenixKit.Mailer.send_from_template/4` (or `Content.resolve/5`), or the
preview shows copy the reader never gets. `variables` are sample values
for every placeholder the email uses; a placeholder they leave unbound is
listed by the preview, so a missing sample is visible rather than silent.
Both may be a map or a zero-arity function; functions are evaluated in the
previewed locale.

An entry whose `name` is not a valid template name (`[a-z0-9][a-z0-9_-]*` —
a leading `_` is reserved for layout parts) is dropped with a warning, and
a name already listed (core's first, then modules in registry order) is
ignored.

# `entry`

```elixir
@type entry() :: %{
  :name =&gt; String.t(),
  :label =&gt; String.t(),
  optional(:description) =&gt; String.t() | nil,
  optional(:defaults) =&gt;
    (-&gt; PhoenixKit.Templates.defaults()) | PhoenixKit.Templates.defaults(),
  optional(:variables) =&gt; (-&gt; map()) | map(),
  optional(:layout) =&gt; boolean() | String.t(),
  optional(:module) =&gt; module() | nil
}
```

One email the system sends. See the moduledoc.

# `preview`

```elixir
@type preview() :: %{
  content: PhoenixKit.Email.Content.resolved(),
  sources: PhoenixKit.Email.Content.sources(),
  variables: map(),
  missing: %{optional(atom()) =&gt; [String.t()]}
}
```

A rendered preview.

  * `content` — subject, text and HTML exactly as a send would build them
    (`t:PhoenixKit.Email.Content.resolved/0`).
  * `sources` — where each part came from (`t:PhoenixKit.Email.Content.sources/0`).
  * `variables` — the sample variables used.
  * `missing` — placeholders the samples leave unbound, by part (empty for a
    database template, which renders through its own provider).

# `entries`

```elixir
@spec entries() :: [entry()]
```

Every known email: core's, then each enabled module's.

Labels are evaluated on the call, so call it in the locale they should
read in.

# `get`

```elixir
@spec get(String.t()) :: entry() | nil
```

The entry named `name`, or `nil`.

# `preview`

```elixir
@spec preview(entry(), String.t(), keyword()) ::
  {:ok, preview()} | {:error, String.t()}
```

Renders `entry` in `locale` with its sample variables, through the same
resolution a send uses (`Content.resolve_with_sources/5`).

Returns `{:ok, preview}`, or `{:error, message}` when the entry's own
`defaults` or `variables` function raises, throws or exits — a module's
broken entry must not take the preview page down.

## Options

  * `:paths` — override roots, as in `Content.resolve/5`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
