Files
myep/docs/LOADING_INDICATORS_IMPLEMENTATION.md
T

8.8 KiB

Loading Indicators Implementation Plan

Overview

Add loading indicators to Step 3 and Step 4 of the booking flow to provide visual feedback during slow API calls.

Date: 2025-10-06 Status: 📋 Planned (not yet implemented)

Problem Statement

Current User Experience:

  • Step 3: User clicks "Weiter" → 2-3 second wait (inquiry API) → No visual feedback
  • Step 4: User clicks "Verbindlich buchen" → 1-2 second wait (booking API) → No visual feedback
  • Users may click multiple times thinking the form didn't submit
  • No indication that processing is happening

Solution

Use HTMX for form submissions with built-in loading indicators.

Why HTMX?

  1. Already extensively used in the project (Step 2 form refreshes)
  2. Built-in loading state management via hx-indicator
  3. Better error handling (no page reload on validation errors)
  4. Progressive enhancement (graceful degradation)
  5. Consistent with existing architecture

Implementation Details

Step 1: Add HTMX Indicator Styles

File: assets/styles/app.css

Add global styles for HTMX loading indicators:

/* HTMX Loading Indicator */
.htmx-indicator {
    display: none;
}

.htmx-request .htmx-indicator {
    display: flex;
}

.htmx-request.htmx-indicator {
    display: flex;
}

Step 2: Update Step 3 Form

File: templates/booking/create_step_3.html.twig

Changes:

  1. Add HTMX attributes to form:
{{ form_start(form, {
    'attr': {
        'novalidate': 'novalidate',
        'hx-post': path('app_booking_create_step_3'),
        'hx-swap': 'none',
        'hx-indicator': '#step3-loading'
    }
}) }}
  1. Add loading overlay before form close:
{# Loading indicator #}
<div id="step3-loading" class="htmx-indicator fixed inset-0 bg-gray-900 bg-opacity-50 flex items-center justify-center z-50">
    <div class="bg-white rounded-lg p-8 shadow-xl">
        <div class="flex items-center space-x-4">
            <svg class="animate-spin h-8 w-8 text-primary-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
                <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
                <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
            </svg>
            <span class="text-lg font-medium">Buchung wird validiert...</span>
        </div>
    </div>
</div>

{{ form_end(form) }}

File: src/Controller/Booking/CreateStep3Controller.php

Changes:

Add HTMX detection and response handling:

public function step3(Request $request): Response
{
    // ... existing validation logic ...

    if ($form->isSubmitted() && $form->isValid()) {
        try {
            // ... existing inquiry + price validation logic ...

            // Validation successful - proceed to confirmation step
            $bookingCreateDto->currentStep = 4;
            $this->bookingService->saveBookingCreateDto($request, $bookingCreateDto);

            // Handle HTMX requests
            if ($request->headers->get('HX-Request')) {
                return new Response('', 200, [
                    'HX-Redirect' => $this->generateUrl('app_booking_create_step_4')
                ]);
            }

            return $this->redirectToRoute('app_booking_create_step_4');
        } catch (\Exception $e) {
            // ... existing error handling ...
        }
    }

    return $this->render('booking/create_step_3.html.twig', [
        'bookingCreateDto' => $bookingCreateDto,
        'form' => $form->createView(),
        ...$this->getSummaryVariables($bookingCreateDto),
    ]);
}

Step 3: Update Step 4 Form

File: templates/booking/create_step_4.html.twig

Changes:

  1. Add HTMX attributes to form (find form_start):
{{ form_start(form, {
    'attr': {
        'novalidate': 'novalidate',
        'hx-post': path('app_booking_create_step_4'),
        'hx-swap': 'none',
        'hx-indicator': '#step4-loading'
    }
}) }}
  1. Add loading overlay before submit button:
{# Loading indicator #}
<div id="step4-loading" class="htmx-indicator fixed inset-0 bg-gray-900 bg-opacity-50 flex items-center justify-center z-50">
    <div class="bg-white rounded-lg p-8 shadow-xl">
        <div class="flex items-center space-x-4">
            <svg class="animate-spin h-8 w-8 text-primary-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
                <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
                <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
            </svg>
            <span class="text-lg font-medium">Buchung wird durchgeführt...</span>
        </div>
    </div>
</div>

{{ form_end(form) }}

File: src/Controller/Booking/CreateStep4Controller.php

Changes:

Add HTMX response handling:

public function step4(Request $request): Response
{
    // ... existing code ...

    if ($form->isSubmitted() && $form->isValid()) {
        try {
            // Submit final booking (already validated in Step 3)
            $bookingResponse = $this->apiClient->createBooking($bookingCreateDto);

            // ... existing error handling ...

            // Success: Store booking number in flash and clear session
            $this->addFlash('booking_number', $bookingResponse->transactionNumber);
            $this->bookingService->clearBookingCreateDto($request);

            // Handle HTMX requests
            if ($request->headers->get('HX-Request')) {
                return new Response('', 200, [
                    'HX-Redirect' => $this->generateUrl('app_booking_success')
                ]);
            }

            return $this->redirectToRoute('app_booking_success');
        } catch (\Exception $e) {
            // ... existing error handling ...
        }
    }

    return $this->render('booking/create_step_4.html.twig', [
        'bookingCreateDto' => $bookingCreateDto,
        'form' => $form->createView(),
        ...$this->getSummaryVariables($bookingCreateDto),
    ]);
}

Alternative: Stimulus-Only Approach

If HTMX is not desired, use the existing loading_controller.js:

Template:

<div data-controller="loading" data-loading-hidden-class="hidden">
    {{ form_start(form, {'attr': {'data-action': 'submit->loading#toggle'}}) }}

    <div data-loading-target="indicator" class="hidden fixed inset-0 bg-gray-900 bg-opacity-50 flex items-center justify-center z-50">
        <div class="bg-white rounded-lg p-8 shadow-xl">
            <!-- Loading spinner -->
        </div>
    </div>

    <!-- Form fields -->

    <button type="submit">Weiter</button>
    {{ form_end(form) }}
</div>

Pros: Simpler, no controller changes Cons:

  • Loading indicator persists if server returns error
  • Page reload happens anyway
  • No error handling improvement

Files to Modify

Templates:

  1. templates/booking/create_step_3.html.twig - Add HTMX attributes + loading indicator
  2. templates/booking/create_step_4.html.twig - Add HTMX attributes + loading indicator

Controllers:

  1. src/Controller/Booking/CreateStep3Controller.php - Add HTMX response handling
  2. src/Controller/Booking/CreateStep4Controller.php - Add HTMX response handling

Styles:

  1. assets/styles/app.css - Add .htmx-indicator styles (if not already present)

Benefits

User Experience:

  • Clear visual feedback during API calls
  • Prevents duplicate submissions (button disabled during request)
  • Professional loading experience
  • Reduced user confusion and frustration

Technical:

  • Better error handling (no page reload on validation errors)
  • Consistent with existing HTMX usage in Step 2
  • Progressive enhancement (works without JavaScript)
  • Flash messages still work via HX-Redirect

Testing Checklist

  • Step 3: Loading indicator shows during inquiry API call
  • Step 3: Form disabled during submission
  • Step 3: Success redirects to Step 4
  • Step 3: Validation errors show inline without reload
  • Step 4: Loading indicator shows during booking API call
  • Step 4: Form disabled during submission
  • Step 4: Success redirects to success page with flash message
  • Step 4: Errors show inline without reload
  • Works without JavaScript (graceful degradation)
  • No duplicate submissions possible

Implementation Priority

High Priority - Significantly improves UX during slow API operations

Notes

  • HTMX already included in project dependencies
  • Loading indicators match existing design system
  • Compatible with all existing validation logic
  • No changes to backend business logic required