# HTMX Templates and Patterns

This directory contains HTMX-specific templates and components for ThrillWiki.

## Overview

HTMX is used throughout ThrillWiki for dynamic content updates without full page reloads. This guide documents the standardized patterns and conventions.

## Directory Structure

```
htmx/
├── components/           # Reusable HTMX components
│   ├── confirm_dialog.html    # Confirmation modal
│   ├── error_message.html     # Error display
│   ├── filter_badge.html      # Filter tag/badge
│   ├── inline_edit_field.html # Inline editing
│   ├── loading_indicator.html # Loading spinners
│   └── success_toast.html     # Success notification
├── partials/             # HTMX response partials
└── README.md             # This documentation
```

## Swap Strategies

Use consistent swap strategies across the application:

| Strategy | Use Case | Example |
|----------|----------|---------|
| `innerHTML` | Replace content inside container | List updates, search results |
| `outerHTML` | Replace entire element | Status badges, table rows |
| `beforeend` | Append items | Infinite scroll, new items |
| `afterbegin` | Prepend items | New items at top of list |

### Examples

```html
<!-- Replace content inside container -->
<div id="results"
     hx-get="/api/search"
     hx-swap="innerHTML">

<!-- Replace entire element (e.g., status badge) -->
<span id="park-status"
      hx-get="/api/park/status"
      hx-swap="outerHTML">

<!-- Infinite scroll - append items -->
<div id="item-list"
     hx-get="/api/items?page=2"
     hx-trigger="revealed"
     hx-swap="beforeend">
```

## Target Naming Conventions

Follow these naming patterns for `hx-target`:

| Pattern | Use Case | Example |
|---------|----------|---------|
| `#object-type-id` | Specific objects | `#park-123`, `#ride-456` |
| `#section-name` | Page sections | `#results`, `#filters`, `#stats` |
| `#modal-container` | Modal content | `#modal-container` |
| `this` | Self-replacement | Status badges, inline edits |

### Examples

```html
<!-- Target specific object -->
<button hx-post="/api/parks/123/status"
        hx-target="#park-123"
        hx-swap="outerHTML">

<!-- Target page section -->
<form hx-post="/api/search"
      hx-target="#results"
      hx-swap="innerHTML">

<!-- Self-replacement -->
<span hx-get="/api/badge"
      hx-target="this"
      hx-swap="outerHTML">
```

## Custom Event Naming

Use these conventions for custom HTMX events:

| Pattern | Description | Example |
|---------|-------------|---------|
| `{model}-status-changed` | Status updates | `park-status-changed`, `ride-status-changed` |
| `{model}-created` | New item created | `park-created`, `review-created` |
| `{model}-updated` | Item updated | `ride-updated`, `photo-updated` |
| `{model}-deleted` | Item deleted | `comment-deleted` |
| `auth-changed` | Auth state change | User login/logout |

### Triggering Events

From Django views:
```python
response['HX-Trigger'] = 'park-status-changed'
# or with data
response['HX-Trigger'] = json.dumps({
    'showToast': {'type': 'success', 'message': 'Status updated!'}
})
```

Listening for events:
```html
<div hx-get="/api/park/header"
     hx-trigger="park-status-changed from:body">
```

## Loading Indicators

Use the standardized loading indicator component:

```html
<!-- Inline (in buttons) -->
<button hx-post="/api/action" hx-indicator="#btn-loading">
    Save
    {% include 'htmx/components/loading_indicator.html' with id='btn-loading' inline=True size='sm' %}
</button>

<!-- Block (below content) -->
<div hx-get="/api/data" hx-indicator="#loading">
    Content
</div>
{% include 'htmx/components/loading_indicator.html' with id='loading' message='Loading...' %}

<!-- Overlay (covers container) -->
<div class="relative" hx-get="/api/data" hx-indicator="#overlay">
    Content
    {% include 'htmx/components/loading_indicator.html' with id='overlay' mode='overlay' %}
</div>
```

## Error Handling

HTMX errors are handled globally in `base.html`. The system:

1. Shows toast notifications for different HTTP status codes
2. Handles timeouts (30 second default)
3. Handles network errors
4. Supports retry logic

### Custom Error Responses

Return error templates for 4xx/5xx responses:
```html
{% include 'htmx/components/error_message.html' with title='Error' message='Something went wrong.' %}
```

## Toast Notifications via HTMX

Trigger toast notifications from server responses:

```python
from django.http import JsonResponse

def my_view(request):
    response = render(request, 'partial.html')
    response['HX-Trigger'] = json.dumps({
        'showToast': {
            'type': 'success',  # success, error, warning, info
            'message': 'Action completed!',
            'duration': 5000  # optional, in milliseconds
        }
    })
    return response
```

## Form Validation

Use inline HTMX validation for forms:

```html
<input type="text"
       name="username"
       hx-post="/api/validate/username"
       hx-trigger="blur changed delay:500ms"
       hx-target="#username-feedback"
       hx-swap="innerHTML">
<div id="username-feedback"></div>
```

Validation endpoint returns:
```html
<!-- Success -->
{% include 'forms/partials/field_success.html' with message='Username available' %}

<!-- Error -->
{% include 'forms/partials/field_error.html' with errors=errors %}
```

## Common Patterns

### Search with Debounce

```html
<input type="search"
       hx-get="/api/search"
       hx-trigger="keyup changed delay:300ms"
       hx-target="#results"
       hx-indicator="#search-loading">
```

### Modal Content Loading

```html
<button hx-get="/api/park/123/edit"
        hx-target="#modal-container"
        hx-swap="innerHTML"
        @click="$store.modal.open()">
    Edit
</button>
```

### Infinite Scroll

```html
<div id="items">
    {% for item in items %}
        {% include 'item.html' %}
    {% endfor %}

    {% if page_obj.has_next %}
    <div hx-get="?page={{ page_obj.next_page_number }}"
         hx-trigger="revealed"
         hx-swap="outerHTML"
         hx-select="#items > *">
        {% include 'htmx/components/loading_indicator.html' %}
    </div>
    {% endif %}
</div>
```

### Status Badge Refresh

```html
<span id="park-header-badge"
      hx-get="{% url 'parks:park_header_badge' park.slug %}"
      hx-trigger="park-status-changed from:body"
      hx-swap="outerHTML">
    {% include 'components/status_badge.html' with status=park.status %}
</span>
```

## Best Practices

1. **Always specify `hx-swap`** even for default behavior (clarity)
2. **Use meaningful target IDs** following naming conventions
3. **Include loading indicators** for all async operations
4. **Handle errors gracefully** with user-friendly messages
5. **Debounce search/filter inputs** to reduce server load
6. **Use `hx-push-url`** for URL changes that should be bookmarkable
7. **Provide fallback** for JavaScript-disabled browsers where possible

## Security Considerations

- CSRF tokens are automatically included via `hx-headers` in base.html
- All HTMX endpoints should validate permissions
- Use Django's `@require_http_methods` decorator
- Sanitize any user input before rendering

## Debugging

Enable HTMX debugging in development:
```javascript
htmx.logAll();
```

Check browser DevTools Network tab for HTMX requests (look for `HX-Request: true` header).
