# Template Hero Visuals

**Last Updated:** 2026-03-26

Per-template JSON definitions for Excel-style previews on template pages, index cards, carousel, and blog ResourceCards. **Published** templates listed in `v2/config/templates-index-config.php` (`$publishedTemplatesForIndex`) **must** ship a valid `hero-visual.json`; the landing stack does **not** synthesize missing rows or cells at runtime.

## Purpose

- **Clean visuals:** No raw Excel formulas (=IFERROR, =B2*C2), no placeholder text (Beispiel, Musterdaten)
- **Realistic dummy data:** Names, dates, numbers as if the template were filled in
- **One file per template:** Tailored headers and **exactly eight** data rows for the main hero (see contract below)

## Location

`v2/data/template-hero-visuals/{templateId}.json`

Example: `v2/data/template-hero-visuals/urlaubsplaner-vorlage.json`

## Row and column contract (mandatory)

| Rule | Detail |
|------|--------|
| **Row count** | Exactly **8** entries in `rows` (main hero truncates to 8 if more; **no padding** if fewer) |
| **table** | Each row length **===** `count(headers)` |
| **key_value** | Each row: exactly **2** cells (label, value). Avoid duplicate labels when possible |
| **list** | Each row: **1** cell; no “Beispiel …”, “Dummy”, “Musterdaten” in values |
| **sheet_name** | Non-empty string (formula bar) |

[`v2/components/excel-visual.php`](../../v2/components/excel-visual.php) renders at most eight data rows and **does not** call `fill_empty_cell_in_table()` to pad gaps.

## Schema

```json
{
  "sheet_name": "Anträge",
  "headers": {
    "A": "Mitarbeiter",
    "B": "Von",
    "C": "Bis",
    "D": "Tage"
  },
  "rows": [
    ["Max Müller", "06.01.2026", "10.01.2026", "5"],
    ["Anna Schmidt", "13.01.2026", "17.01.2026", "5"],
    ["Tom Weber", "20.01.2026", "22.01.2026", "3"],
    ["Lisa Fischer", "27.01.2026", "31.01.2026", "5"],
    ["Jan Becker", "03.02.2026", "07.02.2026", "5"],
    ["Maria Hoffmann", "10.02.2026", "14.02.2026", "5"],
    ["Felix Wagner", "17.02.2026", "21.02.2026", "5"],
    ["Sophie Klein", "24.02.2026", "28.02.2026", "5"]
  ],
  "template_type": "employee_management",
  "data_structure": "table"
}
```

| Field | Required | Description |
|-------|----------|-------------|
| `sheet_name` | Yes | Display name in formula bar (e.g. "Stundenzettel") |
| `headers` | Yes | Object: column letter → header label |
| `rows` | Yes | Exactly **8** row arrays; values only (no formulas) |
| `template_type` | No | Category for visual strategy (shift_planning, time_tracking, payroll, employee_management, etc.) |
| `data_structure` | No | `table` \| `key_value` \| `list` (default: table) |

## Data structure types

- **table:** Standard grid; each row aligns with `headers` column order
- **key_value:** Two columns: label (A) and value (B)
- **list:** Single column of items (still **8** rows)

## Published vs unpublished fallback

- **Published** slugs: [`load_excel_visual_data()`](../../v2/config/template-page-config.php) uses **only** `hero-visual.json`. If the file is missing or invalid (empty headers/rows), the UI shows a **placeholder** and PHP logs a warning — **no** silent extraction from the template definition.
- **Unpublished / dev:** Definition extraction and `fill_empty_cell_in_table()` may still run for tooling; do not rely on that for `/vorlagen` production pages.

## Validation and audit

```bash
php v2/scripts/templates/validate-hero-visuals.php
python3 v2/scripts/templates/audit-hero-visual-rows.py
```

`validate-hero-visuals.php` enforces the contract above, forbidden strings, and warns on duplicate key_value labels. Wired into `make validate` and `make validate-templates`.

## Consumers

- **Hero:** `v2/components/template-hero.php` → `excel-visual.php`
- **Index:** `v2/components/template-index-card.php` → `excel-visual-mini.php`
- **Carousel:** `v2/base/templates_carousel.php` → `excel-visual-mini.php`
- **Blog:** `v2/components/blog/ResourceCard.php` → `excel-visual-mini.php`

Mini previews use the **first N** rows/columns of the same payload (`excel-visual-mini.php`).

All use `load_excel_visual_data($templateId)` from `v2/config/template-page-config.php`.
