---
description: "Generate a BlockRegistry JSON block file for a component's Twig template"
allowed_tools: ["Read", "Write", "Edit", "Bash", "Glob", "Grep"]
---

# Block Registry JSON Generator

Generate a BlockRegistry JSON block file for a component, creating the block definition with fields, template assignments, and category.

## Usage
```
/mtc-block-registry <component-name> [template-assignments]
```

### Examples
```
/mtc-block-registry faq_showcase
/mtc-block-registry faq_showcase home.twig,content.twig
```

## Arguments

- `$ARGUMENTS` — Component name (snake_case, matching the Twig template filename) and optionally comma-separated template assignments. If no templates specified, defaults to `home.twig`.

## Process

### Step 1: Parse Arguments

Extract the component name and optional template assignments from the arguments.

### Step 2: Locate and Analyze the Twig Template

Find the component template by checking these paths in order:
1. `sites/default/templates/includes/page_blocks/{name}.twig`
2. `sites/default/templates/includes/elements/{name}.twig`
3. `sites/default/templates/includes/partials/{name}.twig`
4. `sites/default/templates/includes/pages/**/{name}.twig`

Read the template and extract:
- All `banner["Field Name"]` references — these are the block fields
- All `banner["Field Name"]["rows"]` references — these indicate repeater fields
- For repeaters, find the `{% for %}` loop body to identify sub-fields from `item["SubField"]["value"]` or `faq["Question"]["value"]` patterns
- Also check included sub-templates (e.g., `{% include "includes/pages/faqs/faq.twig" %}`) for field patterns

### Step 3: Determine Field Types

Map each field to its BlockRegistry field type:

| Pattern | Field Type | Notes |
|---------|-----------|-------|
| `Title`, `Subtitle`, `Overline`, `Button Text`, `Name` | `text` | Simple text input |
| `Content`, `Description`, `Answer` | `textarea` | Add `css_class: 'simpletinymce'` for rich text |
| `Image`, `Banner Image`, `Background Image` | `image` | Image with crop/alt support |
| `Video`, `Banner Video` | `file` | Add `mime: 'video/mp4'` |
| `Button`, `Button Link`, `Link` | `link` | Internal/external link picker |
| Fields accessed via `["rows"]` or used in `{% for %}` loops | `repeater` | Create sub-fields for loop variables |
| Boolean-like fields | `checkbox` | |
| Fields with fixed options | `select` | Add `options` in `value|label` format (see below) |

### Product data — use `product_selector`, not manual fields

If a block displays product information (title, description, image, price, URL), **do not** create manual text/image fields for that data. Instead, use a `product_selector` field and resolve the product at render time via `get_products_by_ids()`. Only add manual fields for data that is **not part of the product** (e.g. step labels, feature icons, custom descriptions specific to this block's context).

**Pattern in Twig:**
```twig
{# Collect product IDs from repeater rows #}
{% set product_ids = [] %}
{% for row in block_data["Items"]["rows"] | default([]) %}
    {% if row["Product"]["value"] is defined and row["Product"]["value"] is not empty %}
        {% set product_ids = product_ids | merge([row["Product"]["value"]]) %}
    {% endif %}
{% endfor %}
{% set resolved_products = get_products_by_ids(product_ids) %}

{# Index by ID for lookup #}
{% set products_by_id = {} %}
{% for product in resolved_products %}
    {% set products_by_id = products_by_id | merge({ (product.id ~ ''): product }) %}
{% endfor %}

{# Use in loop #}
{% set product = products_by_id[row["Product"]["value"] ~ ''] | default(null) %}
{% if product %}
    {{ product.name }}
    {{ product.getImagePath('small') }}
    {{ product.getUrlAttribute() }}
    {{ product.getFromPrice() }}
{% endif %}
```

### Step 4: Generate the JSON Block File

Create the JSON file at:
```
modules/BlockRegistry/blocks/{slug}.json
```

Where `{slug}` is the kebab-case version of the component name (e.g., `faq_showcase` → `faq-showcase`).

The JSON must match this exact schema (used by `BlockSyncService`):

```json
{
    "name": "Human Readable Name",
    "slug": "kebab-case-slug",
    "description": "Brief description of what the block does",
    "icon": "fa-icon-class",
    "template": "page_blocks/component_name.twig",
    "is_system": false,
    "is_active": true,
    "order_index": 0,
    "category": null,
    "fields": [
        {
            "name": "Field Name",
            "label": "Field Name",
            "type": "text",
            "order_index": 0,
            "required": false,
            "css_class": null,
            "settings": null,
            "options": null,
            "regex": null,
            "mime": null,
            "default_value": null,
            "info": null
        },
        {
            "name": "Repeater Name",
            "label": "Repeater Name",
            "type": "repeater",
            "order_index": 1,
            "required": false,
            "css_class": null,
            "settings": null,
            "options": null,
            "regex": null,
            "mime": null,
            "default_value": null,
            "info": null,
            "repeater_min": 1,
            "repeater_max": 10,
            "repeater_text": "Add Item",
            "sub_fields": [
                {
                    "name": "Sub Field Name",
                    "label": "Sub Field Name",
                    "type": "text",
                    "order_index": 0,
                    "required": false,
                    "css_class": null,
                    "settings": null,
                    "options": null,
                    "regex": null,
                    "mime": null,
                    "default_value": null,
                    "info": null
                }
            ]
        }
    ],
    "template_assignments": [
        {
            "template_name": "home.twig",
            "is_default": false,
            "order_index": 0
        }
    ]
}
```

**Key rules for the JSON:**
- `template` path should NOT include `includes/` prefix or `sites/default/templates/` — just the path from `page_blocks/` (e.g., `page_blocks/faq_showcase.twig`)
- `category` is `null` for uncategorized, or an object: `{"name": "Category Name", "slug": "category-slug", "icon": "fa-icon", "order_index": 0}`
- Repeater fields include `repeater_min`, `repeater_max`, `repeater_text`, and `sub_fields` array
- Non-repeater fields omit repeater-specific properties
- All nullable fields should be explicitly set to `null`
- Field `name` values must match exactly what the Twig template references in `banner["Field Name"]`

### Step 5: Import the Block

Import the JSON file into the database:
```bash
docker exec mtc-pharmacy-web php artisan blocks:import {slug} --force
```

### Step 6: Verify

Confirm the block was imported and is in sync:
```bash
docker exec mtc-pharmacy-web php artisan blocks:diff {slug}
```

This should show the block as "In Sync".

## Output

**Created File:**
- `modules/BlockRegistry/blocks/{slug}.json`

**Database Records (after import):**
- 1 row in `block_registry`
- N rows in `block_registry_fields` (top-level + repeater sub-fields)
- 1+ rows in `block_registry_template_assignments`

## Field Type Reference

### Supported Types
- `text` — Single-line text input
- `textarea` — Multi-line text (add `css_class: 'simpletinymce'` for WYSIWYG)
- `image` — Image upload with crop/alt/srcset support
- `file` — File upload (specify `mime` for validation)
- `checkbox` — Boolean toggle
- `select` — Dropdown (requires `options` in pipe-delimited format: `"value1|Label 1\r\nvalue2|Label 2"`, NOT JSON)
- `link` — Internal/external link picker
- `product_selector` — Product search/select
- `repeater` — Repeatable group of sub-fields (no nesting)
- `datetime` — Date/time picker
- `colorpicker` — Color selection

### Repeater Configuration
- `repeater_min` — Minimum rows (typically 1)
- `repeater_max` — Maximum rows
- `repeater_text` — "Add" button label (e.g., "Add FAQ", "Add Card")

## Critical Rules

- Follow the **exact JSON schema** shown above — it must match what `BlockSyncService::blockToArray()` produces
- Reference existing JSON files in `modules/BlockRegistry/blocks/` for examples
- Field `name` values must match exactly what the Twig template references in `banner["Field Name"]`
- Template path format: `page_blocks/{name}.twig` (no `includes/` prefix)
- Always run `blocks:import` after creating the JSON to populate the database
- Always run `blocks:diff` to verify the block is in sync

## Template Fix-Up (MANDATORY after generating JSON)

After generating the JSON, you MUST also update the Twig template to be BlockRegistry-compatible. The `/mtc-extract` command generates templates with dummy-data-only patterns that break when fed real BlockRegistry data. Apply these fixes:

### Image fields — use `["url"]` with fallback chain
BlockRegistry image fields provide a full URL in `["url"]`, NOT just a filename in `["value"]`. Always use this pattern for image fields (both top-level and in repeaters):

```twig
{% if item["Image"]["url"] is defined and item["Image"]["url"] is not empty %}
    {% set item_image = item["Image"]["url"] %}
{% elseif show_dummy_data is defined and show_dummy_data == true %}
    {% set item_image = "/sites/default/images/placeholders/" ~ item["Image"]["value"] %}
{% else %}
    {% set item_image = item["Image"]["value"] | default("") %}
{% endif %}

<img src="{{ item_image }}"
     alt="{{ item['Image']['alt'] | default('') }}"
     {% if item["Image"]["srcset"] is defined and item["Image"]["srcset"] is not empty %}srcset="{{ item["Image"]["srcset"] }}"{% endif %}
     loading="lazy">
```

**NEVER** use `src="/sites/default/images/{{ item['Image']['value'] }}"` — this only works for dummy data and breaks with real BlockRegistry uploads.

### Rich text fields — use `|raw` filter and `<div>` wrapper
Any field with `css_class: 'simpletinymce'` contains HTML (e.g. `<p>` tags). You MUST:
1. Use `|raw` filter: `{{ item["Content"]["value"] | default("") | raw }}`
2. Use `<div>` not `<p>` as the wrapper element (TinyMCE outputs `<p>` tags, nesting `<p>` inside `<p>` is invalid HTML)

## Reference

- **Existing JSON blocks**: `modules/BlockRegistry/blocks/*.json`
- **Block Registry module**: `modules/BlockRegistry/`
- **Sync service**: `modules/BlockRegistry/Services/BlockSyncService.php`
- **Template rendering**: `modules/BlockRegistry/Services/EntityBlockService.php`

ARGUMENTS: $ARGUMENTS
