Files
thrillwiki_django_no_react/docs/ux/migration-guide.md

9.5 KiB
Raw Blame History

Migration Guide

This guide helps migrate existing templates to use the new standardized UX patterns.

Overview

The ThrillWiki UX standardization introduces:

  1. Breadcrumb system with Schema.org support
  2. Skeleton screens for loading states
  3. Standardized form validation with HTMX
  4. Toast notifications with action support
  5. Page headers with consistent layout
  6. Action bars for button placement
  7. Enhanced modals with focus trapping
  8. HTMX response utilities for views

Step-by-Step Migration

Step 1: Update Base Template Usage

Ensure your templates extend the base template correctly:

{% extends 'base/base.html' %}

{% block title %}Your Page Title{% endblock %}

{% block content %}
    <!-- Your content -->
{% endblock %}

Step 2: Add Breadcrumbs

In Your View

from apps.core.utils import BreadcrumbBuilder

def your_view(request):
    request.breadcrumbs = (
        BreadcrumbBuilder()
        .add_home()
        .add('Section', '/section/')
        .add_current('Current Page')
        .build()
    )
    return render(request, 'your_template.html')

In Your Template

The breadcrumbs are automatically rendered if you're using the base template. If you need to place them elsewhere:

{% include 'components/navigation/breadcrumbs.html' %}

Step 3: Replace Page Headers

Before

<div class="page-header">
    <h1>Parks</h1>
    <a href="/parks/create/" class="btn">Add Park</a>
</div>

After

{% include 'components/layout/page_header.html' with
    title='Parks'
    subtitle='Browse theme parks worldwide'
    primary_action_url='/parks/create/'
    primary_action_text='Add Park'
    primary_action_icon='fas fa-plus'
%}

Step 4: Update Form Fields

Before

<div class="form-group">
    <label for="name">Name</label>
    {{ form.name }}
    {% if form.name.errors %}
        <span class="error">{{ form.name.errors.0 }}</span>
    {% endif %}
</div>

After

{% include 'forms/partials/form_field.html' with field=form.name %}

For multiple fields with consistent styling:

{% for field in form %}
    {% include 'forms/partials/form_field.html' with field=field %}
{% endfor %}

Step 5: Update Form Actions

Before

<div class="form-buttons">
    <a href="/cancel/">Cancel</a>
    <button type="submit">Save</button>
</div>

After

{% include 'forms/partials/form_actions.html' with
    submit_text='Save Park'
    cancel_url='/parks/'
%}

Or use the action bar for more options:

{% include 'components/ui/action_bar.html' with
    align='between'
    primary_action_text='Save'
    tertiary_action_text='Cancel'
    tertiary_action_url='/parks/'
%}

Step 6: Add Loading States

For Page Sections

<div id="parks-list"
     hx-get="{% url 'parks:list' %}"
     hx-trigger="load"
     hx-swap="innerHTML">
    {% include 'components/skeletons/card_grid_skeleton.html' with cards=6 %}
</div>

For Forms

<form hx-post="{% url 'parks:create' %}"
      hx-target="#form-container"
      hx-indicator="#form-loading">
    {% csrf_token %}
    <!-- Form fields -->

    <div id="form-loading" class="htmx-indicator">
        {% include 'htmx/components/loading_indicator.html' with text='Saving...' %}
    </div>

    <button type="submit">Save</button>
</form>

Step 7: Update Toast Messages

Before (Django Messages)

from django.contrib import messages

def your_view(request):
    messages.success(request, 'Park saved!')
    return redirect('parks:list')

After (HTMX Response)

from apps.core.htmx_utils import htmx_success

def your_view(request):
    # For HTMX requests
    if request.headers.get('HX-Request'):
        return htmx_success('Park saved!')

    # For regular requests, still use messages
    messages.success(request, 'Park saved!')
    return redirect('parks:list')

Step 8: Update Modals

Before

<div class="modal" id="edit-modal">
    <div class="modal-header">
        <h2>Edit Park</h2>
        <button onclick="closeModal()">×</button>
    </div>
    <div class="modal-body">
        <!-- Content -->
    </div>
</div>

After

<div x-data="{ showEditModal: false }">
    <button @click="showEditModal = true">Edit</button>

    {% include 'components/modals/modal_base.html' with
        modal_id='edit-modal'
        show_var='showEditModal'
        title='Edit Park'
        size='lg'
    %}
</div>

For confirmation dialogs:

{% include 'components/modals/modal_confirm.html' with
    modal_id='delete-confirm'
    show_var='showDeleteModal'
    title='Delete Park'
    message='Are you sure? This cannot be undone.'
    confirm_text='Delete'
    confirm_variant='destructive'
    confirm_hx_delete='/parks/123/'
%}

Step 9: Update HTMX Views

Before

from django.http import HttpResponse
import json

def create_park(request):
    park = Park.objects.create(**form.cleaned_data)

    response = HttpResponse('')
    response['HX-Trigger'] = json.dumps({
        'showMessage': {'message': 'Park created!'}
    })
    return response

After

from apps.core.htmx_utils import htmx_success, htmx_error, htmx_modal_close

def create_park(request):
    form = ParkForm(request.POST)

    if not form.is_valid():
        return htmx_error('Validation failed', status=422)

    park = form.save()

    # Simple success
    return htmx_success(f'{park.name} created!')

    # Or close modal and refresh list
    return htmx_modal_close(
        message=f'{park.name} created!',
        refresh_target='#parks-list'
    )

Step 10: Add Page Meta

In Your View

from apps.core.utils import build_meta_context

def park_detail(request, slug):
    park = get_object_or_404(Park, slug=slug)

    request.page_meta = build_meta_context(
        title=park.name,
        description=park.description,
        instance=park,
        request=request,
    )

    return render(request, 'parks/detail.html', {'park': park})

The meta tags are automatically rendered in the base template.

Component Replacements

Old Pattern New Component
Custom page header components/layout/page_header.html
Form field div forms/partials/form_field.html
Form buttons forms/partials/form_actions.html or components/ui/action_bar.html
Loading spinner htmx/components/loading_indicator.html
Placeholder content components/skeletons/*.html
Custom modal components/modals/modal_base.html
Confirm dialog components/modals/modal_confirm.html
Custom breadcrumbs components/navigation/breadcrumbs.html
Status labels components/status_badge.html

View Helper Replacements

Old Pattern New Helper
HttpResponse + HX-Trigger htmx_success() / htmx_error()
HttpResponse + HX-Redirect htmx_redirect()
Custom modal close htmx_modal_close()
Inline validation htmx_validation_response()
Check HX-Request header is_htmx_request()

Common Issues

Issue: Breadcrumbs Not Showing

  1. Ensure context processor is added to settings:
TEMPLATES = [{
    'OPTIONS': {
        'context_processors': [
            # ...
            'apps.core.context_processors.breadcrumbs',
        ],
    },
}]
  1. Ensure you set request.breadcrumbs in your view.

Issue: Toast Not Appearing

  1. Ensure toast container is in base template:
{% include 'components/ui/toast-container.html' %}
  1. Ensure Alpine.js is loaded and toast store is initialized.

  2. For HTMX responses, use the helper functions:

return htmx_success('Message here')

Issue: Modal Not Closing

  1. Ensure you're using the correct show_var:
<div x-data="{ showModal: false }">
    {% include 'components/modals/modal_base.html' with show_var='showModal' %}
</div>
  1. For HTMX responses, use:
return htmx_modal_close(message='Done!')

Issue: Form Validation Not Working

  1. Ensure HTMX attributes are correct:
<form hx-post="/create/"
      hx-target="#form-container"
      hx-swap="outerHTML">
  1. Return proper status code on errors:
if not form.is_valid():
    return render(request, 'form.html', {'form': form}, status=422)

Issue: Skeleton Not Matching Content

Adjust skeleton parameters to match your content:

{# For a 3-column grid with 9 cards #}
{% include 'components/skeletons/card_grid_skeleton.html' with cards=9 cols=3 %}

{# For a table with checkboxes #}
{% include 'components/skeletons/table_skeleton.html' with rows=10 columns=5 show_checkbox=True %}

Testing Migrations

After migrating a template:

  1. Visual Check: Ensure layout matches design
  2. Functionality: Test all interactive elements
  3. Accessibility: Test keyboard navigation and screen readers
  4. Responsive: Check on mobile, tablet, and desktop
  5. Loading States: Verify skeletons and indicators work
  6. Error States: Test form validation and error messages
  7. Success States: Verify toast notifications appear

Rollback

If issues arise, components are designed to be backwards compatible. You can temporarily revert to old patterns while debugging:

{% comment %}
{% include 'components/layout/page_header.html' with title='Parks' %}
{% endcomment %}

<div class="page-header">
    <h1>Parks</h1>
</div>