
## Adding a New Page — Step by Step

### Step 1 — Choose a layout

Declare the layout in your template's very first line using a Go comment:

```html
{{/* layout: base_public.html */}}
```

If you omit this line, `base.html` (sidebar layout) is used automatically.

**Built-in layouts:**

| Layout file | Navigation | Best for |
|---|---|---|
| `base.html` | Left sidebar | Authenticated app pages (dashboard, profile, etc.) |
| `base_public.html` | Top bar | Public/marketing pages, landing pages |
| `base_bare.html` | None | Minimal pages, print view, embeds |

**Custom layout:** Create `web/templates/layouts/my_layout.html`, then declare `{{/* layout: my_layout.html */}}` in your page. Any layout filename works as long as it lives in the `layouts/` directory and contains `{{define "base"}} ... {{end}}`.

### Step 2 — Create the template

`web/templates/pages/mypage.html`:
```html
{{/* layout: base.html */}}
{{template "base" .}}

{{define "title"}}My Page — GoApp{{end}}

{{define "content"}}
<div class="max-w-4xl mx-auto">
    <h2 class="text-2xl font-semibold text-white mb-6">{{.Title}}</h2>

    {{range .Items}}
    <div class="card">
        <p class="text-gray-300">{{.}}</p>
    </div>
    {{end}}
</div>
{{end}}

{{/* Optional: page-specific <head> additions */}}
{{define "head"}}<style>/* page CSS */</style>{{end}}

{{/* Optional: page-specific scripts */}}
{{define "scripts"}}<script>/* page JS */</script>{{end}}
```

The `{{/* layout: ... */}}` comment is read only by Go at startup — it never appears in the HTML output.

### Step 3 — Create the handler

`internal/handlers/mypage.go`:
```go
package handlers

import "net/http"

// MyPageData holds everything the template needs.
// Always embed PageData — it carries User, Nav, flash messages, etc.
type MyPageData struct {
    PageData
    Items []string // your page-specific data
}

type MyPageHandler struct {
    tmpl *Renderer
    // Add other dependencies here: *db.DB, *mailer.Mailer, *logger.Logger
}

func NewMyPageHandler(r *Renderer) *MyPageHandler {
    return &MyPageHandler{tmpl: r}
}

func (h *MyPageHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodGet {
        http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
        return
    }
    data := MyPageData{
        PageData: NewPageData(r, "My Page"),
        Items:    []string{"hello", "world"},
    }
    h.tmpl.Render(w, "mypage", data)
}
```

### Step 4 — Register the route

Add one line to `internal/router/router.go`:

```go
// Public page (no login required):
mux.Handle("/mypage", handlers.NewMyPageHandler(renderer))

// Authenticated page (redirects to /login if not signed in):
mux.Handle("/mypage", middleware.RequireAuth(handlers.NewMyPageHandler(renderer)))

// Admin-only page:
mux.Handle("/mypage", adminMW(handlers.NewMyPageHandler(renderer)))
```

### Step 5 — (Optional) Add to sidebar navigation

In `internal/handlers/renderer.go`, append to `DefaultNav`:
```go
var DefaultNav = []NavItem{
    // existing items ...
    {Label: "My Page", Href: "/mypage", Icon: "info"},              // all users
    {Label: "Admin Page", Href: "/mypage", Icon: "shield", AdminOnly: true}, // admins only
}
```

Available icon names: `home`, `info`, `mail`, `users`, `activity`, `settings`, `logout`, `shield`, `key`, `mfa`

---

## Email — Using the Mailer

### Configure in `data/config.json`

```json
"email": {
    "enabled":      true,
    "smtp_host":    "smtp.gmail.com",
    "smtp_port":    587,
    "encryption":   "starttls",
    "auth":         true,
    "username":     "you@gmail.com",
    "password":     "your-app-password",
    "from_address": "GoApp <you@gmail.com>"
}
```

**Gmail tip:** Create an [App Password](https://myaccount.google.com/apppasswords) (not your main password). Requires 2FA enabled on your Google account.

### Wire the Mailer into a handler

```go
// In router.go (m is already created from cfg.Email):
mux.Handle("/notify", handlers.NewNotifyHandler(renderer, m, log))

// In your handler:
type NotifyHandler struct {
    tmpl   *Renderer
    mailer *mailer.Mailer
    log    *logger.Logger
}
func NewNotifyHandler(r *Renderer, m *mailer.Mailer, l *logger.Logger) *NotifyHandler {
    return &NotifyHandler{tmpl: r, mailer: m, log: l}
}
```

### Send plain-text email

```go
err := h.mailer.Send(mailer.Message{
    To:      []string{"user@example.com"},
    Subject: "Welcome to GoApp",
    Body:    "Hello! Your account is ready.",
})
if err != nil {
    h.log.Warn("send welcome email", "err", err)
    // Don't abort — email failure should not break the UX
}
```

### Send HTML email

```go
err := h.mailer.Send(mailer.Message{
    To:      []string{"user@example.com"},
    Subject: "Welcome to GoApp",
    Body:    "<h1>Welcome!</h1><p>Your account is <strong>ready</strong>.</p>",
    IsHTML:  true,
})
```

### Multiple recipients + CC

```go
err := h.mailer.Send(mailer.Message{
    To:      []string{"alice@example.com", "bob@example.com"},
    CC:      []string{"manager@example.com"},
    Subject: "Team notification",
    Body:    "Something happened that you should know about.",
})
```

### Safe error handling pattern

When `email.enabled` is `false`, `Send()` returns `nil` immediately — the rest of your handler works normally. Always log email errors rather than aborting the request:

```go
if err := h.mailer.Send(msg); err != nil {
    h.log.Warn("email send failed", "err", err)
    // continue — don't return an error page to the user
}
```