feat: improved layout, removed htmx-boost for simplicity
This commit is contained in:
@@ -0,0 +1,11 @@
|
|||||||
|
import { Controller} from '@hotwired/stimulus'
|
||||||
|
|
||||||
|
export default class extends Controller {
|
||||||
|
|
||||||
|
static targets = [ 'indicator' ]
|
||||||
|
static classes = [ 'hidden' ]
|
||||||
|
|
||||||
|
toggle() {
|
||||||
|
this.indicatorTarget.classList.remove(this.hiddenClass)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -82,8 +82,6 @@ class EditController extends AbstractController
|
|||||||
|
|
||||||
$form = $this->createForm(BookingType::class, $formData, [
|
$form = $this->createForm(BookingType::class, $formData, [
|
||||||
'attr' => ['novalidate' => 'novalidate'],
|
'attr' => ['novalidate' => 'novalidate'],
|
||||||
'hx_post' => $this->generateUrl('app_booking_edit', ['id' => $id]),
|
|
||||||
'hx_target' => '#myep',
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Controller;
|
|
||||||
|
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
use Symfony\Component\Routing\Attribute\Route;
|
|
||||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
|
||||||
|
|
||||||
class IndexController extends AbstractController
|
|
||||||
{
|
|
||||||
#[Route('/', name: 'app_index')]
|
|
||||||
#[IsGranted('PUBLIC_ACCESS')]
|
|
||||||
public function index(): Response
|
|
||||||
{
|
|
||||||
return $this->render('index/index.html.twig');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -34,8 +34,6 @@ class PersonalDataController extends AbstractController
|
|||||||
|
|
||||||
$personalDataForm = $this->createForm(PersonalDataType::class, $personalData, [
|
$personalDataForm = $this->createForm(PersonalDataType::class, $personalData, [
|
||||||
'attr' => ['novalidate' => 'novalidate'],
|
'attr' => ['novalidate' => 'novalidate'],
|
||||||
'hx_post' => $this->generateUrl('app_personal_data'),
|
|
||||||
'hx_target' => '#myep',
|
|
||||||
]);
|
]);
|
||||||
$personalDataForm->handleRequest($request);
|
$personalDataForm->handleRequest($request);
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
|||||||
|
|
||||||
class SecurityController extends AbstractController
|
class SecurityController extends AbstractController
|
||||||
{
|
{
|
||||||
#[Route('/login', name: 'app_login')]
|
#[Route('/', name: 'app_login')]
|
||||||
#[IsGranted('PUBLIC_ACCESS')]
|
#[IsGranted('PUBLIC_ACCESS')]
|
||||||
public function login(AuthenticationUtils $authenticationUtils): Response
|
public function login(AuthenticationUtils $authenticationUtils): Response
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\EventListener;
|
|
||||||
|
|
||||||
use App\Htmx\HxRedirectResponse;
|
|
||||||
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
|
|
||||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
|
||||||
use Symfony\Component\Security\Http\Event\LogoutEvent;
|
|
||||||
|
|
||||||
#[AsEventListener(event: LogoutEvent::class, method: 'onLogout')]
|
|
||||||
class LogoutListener
|
|
||||||
{
|
|
||||||
public function __construct(private readonly UrlGeneratorInterface $urlGenerator)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function onLogout(LogoutEvent $event): void
|
|
||||||
{
|
|
||||||
$event->setResponse(new HxRedirectResponse($this->urlGenerator->generate('app_index')));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Extension;
|
|
||||||
|
|
||||||
use Symfony\Component\Form\AbstractTypeExtension;
|
|
||||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
|
||||||
use Symfony\Component\Form\FormInterface;
|
|
||||||
use Symfony\Component\Form\FormView;
|
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
|
||||||
|
|
||||||
class HtmxSubmitExtension extends AbstractTypeExtension
|
|
||||||
{
|
|
||||||
public function configureOptions(OptionsResolver $resolver): void
|
|
||||||
{
|
|
||||||
$resolver->setDefaults([
|
|
||||||
'hx_post' => null,
|
|
||||||
'hx_target' => '#htmx-modal',
|
|
||||||
'hx_swap' => 'outerHTML',
|
|
||||||
'hx_indicator' => '#loading-indicator',
|
|
||||||
'hx_trigger' => null,
|
|
||||||
'hx_select' => null,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function buildView(FormView $view, FormInterface $form, array $options): void
|
|
||||||
{
|
|
||||||
if (null !== $options['hx_post']) {
|
|
||||||
$attr = [
|
|
||||||
'hx-post' => $options['hx_post'],
|
|
||||||
'hx-target' => $options['hx_target'],
|
|
||||||
'hx-swap' => $options['hx_swap'],
|
|
||||||
];
|
|
||||||
if (null !== $options['hx_indicator']) {
|
|
||||||
$attr['hx-indicator'] = $options['hx_indicator'];
|
|
||||||
}
|
|
||||||
if (null !== $options['hx_trigger']) {
|
|
||||||
$attr['hx-trigger'] = $options['hx_trigger'];
|
|
||||||
}
|
|
||||||
if (null !== $options['hx_select']) {
|
|
||||||
$attr['hx-select'] = $options['hx_select'];
|
|
||||||
}
|
|
||||||
$view->vars['attr'] = array_merge($view->vars['attr'], $attr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getExtendedTypes(): iterable
|
|
||||||
{
|
|
||||||
return [FormType::class];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Htmx;
|
|
||||||
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
|
|
||||||
class HxRedirectResponse extends Response
|
|
||||||
{
|
|
||||||
public function __construct(string $url)
|
|
||||||
{
|
|
||||||
return parent::__construct(null, Response::HTTP_OK, ['HX-Redirect' => $url]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Htmx;
|
|
||||||
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
|
|
||||||
class HxTriggerResponse extends Response
|
|
||||||
{
|
|
||||||
public function __construct(string $content, string $trigger)
|
|
||||||
{
|
|
||||||
return parent::__construct($content, Response::HTTP_OK, ['HX-Trigger' => $trigger]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -24,12 +24,14 @@ class MenuBuilder
|
|||||||
'route' => 'app_personal_data',
|
'route' => 'app_personal_data',
|
||||||
'linkAttributes' => [
|
'linkAttributes' => [
|
||||||
'title' => 'Persönliche Daten',
|
'title' => 'Persönliche Daten',
|
||||||
|
'data-action' => 'loading#toggle',
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
$menu->addChild('Meine Buchungen', [
|
$menu->addChild('Meine Buchungen', [
|
||||||
'route' => 'app_bookings',
|
'route' => 'app_bookings',
|
||||||
'linkAttributes' => [
|
'linkAttributes' => [
|
||||||
'title' => 'Meine Buchungen',
|
'title' => 'Meine Buchungen',
|
||||||
|
'data-action' => 'loading#toggle',
|
||||||
],
|
],
|
||||||
'extras' => [
|
'extras' => [
|
||||||
'routes' => [
|
'routes' => [
|
||||||
@@ -41,6 +43,7 @@ class MenuBuilder
|
|||||||
'route' => 'app_logout',
|
'route' => 'app_logout',
|
||||||
'linkAttributes' => [
|
'linkAttributes' => [
|
||||||
'title' => 'Logout',
|
'title' => 'Logout',
|
||||||
|
'data-action' => 'loading#toggle',
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<div class="invisible fixed top-0 left-0 inset-0 z-10 bg-white/90 flex items-center justify-center"
|
||||||
|
{{ stimulus_target('loading', 'indicator') }}>
|
||||||
|
{% include '_partials/_spinner.html.twig' with { 'class': 'w-8 h-8'} %}
|
||||||
|
</div>
|
||||||
@@ -232,11 +232,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<a href="{{ path('app_bookings') }}"
|
<a href="{{ path('app_bookings') }}"
|
||||||
class="button bg-button">
|
class="button bg-button" {{ stimulus_action('loading', 'toggle') }}>
|
||||||
zurück
|
zurück
|
||||||
</a>
|
</a>
|
||||||
<button type="submit"
|
<button type="submit"
|
||||||
class="button bg-button bg-button--secondary">
|
class="button bg-button bg-button--secondary" {{ stimulus_action('loading', 'toggle') }}>
|
||||||
Aktualisieren
|
Aktualisieren
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -64,16 +64,19 @@
|
|||||||
<div class="flex md:flex-col space-x-2 md:space-x-0 md:space-y-1">
|
<div class="flex md:flex-col space-x-2 md:space-x-0 md:space-y-1">
|
||||||
{% if booking.editable %}
|
{% if booking.editable %}
|
||||||
<a href="{{ path('app_booking_edit', { 'id': booking.id }) }}"
|
<a href="{{ path('app_booking_edit', { 'id': booking.id }) }}"
|
||||||
|
{{ stimulus_action('loading', 'toggle') }}
|
||||||
class="button bg-button bg-button--secondary button--small">
|
class="button bg-button bg-button--secondary button--small">
|
||||||
bearbeiten
|
bearbeiten
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="{{ path('app_booking_confirmation', { 'id': booking.id }) }}"
|
<a href="{{ path('app_booking_confirmation', { 'id': booking.id }) }}"
|
||||||
class="button bg-button button--small">
|
class="button bg-button button--small"
|
||||||
|
target="_blank" {{ stimulus_action('loading', 'toggle') }}>
|
||||||
Bestätigung
|
Bestätigung
|
||||||
</a>
|
</a>
|
||||||
<a href="{{ path('app_booking_documents', { 'id': booking.id }) }}"
|
<a href="{{ path('app_booking_documents', { 'id': booking.id }) }}"
|
||||||
class="button bg-button button--small">
|
class="button bg-button button--small"
|
||||||
|
target="_blank" {{ stimulus_action('loading', 'toggle') }}>
|
||||||
Reisedokumente
|
Reisedokumente
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
{% extends 'base.html.twig' %}
|
|
||||||
|
|
||||||
{% block body %}
|
|
||||||
<div hx-get="{{ path('app_login') }}"
|
|
||||||
hx-trigger="load"
|
|
||||||
hx-swap="outerHTML"></div>
|
|
||||||
{% endblock %}
|
|
||||||
+17
-21
@@ -1,23 +1,19 @@
|
|||||||
<div class="my-16"
|
{% extends 'base.html.twig' %}
|
||||||
id="myep"
|
|
||||||
hx-boost="true"
|
{% block body %}
|
||||||
hx-indicator="#loading-indicator">
|
<div class="{{ html_classes('pb-8', { 'pt-16': is_granted('ROLE_USER') }) }}" {{ stimulus_controller('loading', [], { 'hidden': 'invisible' }) }}>
|
||||||
<div class="fixed inset-x-0 top-0 z-20 flex items-center justify-between pb-4">
|
<div class="fixed inset-x-0 top-0 z-20 flex items-center justify-between pb-4">
|
||||||
{% if is_granted('ROLE_USER') %}
|
{% if is_granted('ROLE_USER') %}
|
||||||
<nav class="w-full bg-zinc-200 flex items-center mb-8 relative">
|
<nav class="w-full bg-zinc-200 flex items-center mb-8 relative">
|
||||||
<div class="flex-1 block text-2xl px-4">My E&P</div>
|
<div class="flex-1 block text-2xl px-4">My E&P</div>
|
||||||
<div class="bg-zinc-200">
|
<div class="bg-zinc-200">
|
||||||
{{ knp_menu_render(knp_menu_get('main')) }}
|
{{ knp_menu_render(knp_menu_get('main')) }}
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div id="content">
|
|
||||||
{% block content %}{% endblock %}
|
{% block content %}{% endblock %}
|
||||||
|
{% include '_partials/_flashes.html.twig' %}
|
||||||
|
{% include '_partials/_loading_indicator.html.twig' %}
|
||||||
</div>
|
</div>
|
||||||
<div class="htmx-indicator fixed top-0 left-0 inset-0 z-10 bg-white/90 flex items-center justify-center"
|
{% endblock %}
|
||||||
id="loading-indicator">
|
|
||||||
{% include '_partials/_spinner.html.twig' with { 'class': 'w-8 h-8'} %}
|
|
||||||
</div>
|
|
||||||
{% include '_partials/_flashes.html.twig' %}
|
|
||||||
</div>
|
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
<h2 class="text-2xl font-semibold pb-4">
|
<h2 class="text-2xl font-semibold pb-4">
|
||||||
Persönliche Daten
|
Persönliche Daten
|
||||||
</h2>
|
</h2>
|
||||||
{{ form_start(personalDataForm) }}
|
{{ form_start(personalDataForm, { 'attr': { 'data-action': 'loading#toggle' } }) }}
|
||||||
<div class="grid grid-cols-2 gap-x-8 gap-y-4 pb-4">
|
<div class="grid grid-cols-2 gap-x-8 gap-y-4 pb-4">
|
||||||
{{ form_row(personalDataForm.firstName) }}
|
{{ form_row(personalDataForm.firstName) }}
|
||||||
{{ form_row(personalDataForm.name) }}
|
{{ form_row(personalDataForm.name) }}
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
{{ form_end(personalDataForm) }}
|
{{ form_end(personalDataForm) }}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div id="newsletter">
|
||||||
<h2 class="text-2xl font-semibold pb-4">
|
<h2 class="text-2xl font-semibold pb-4">
|
||||||
Newsletter
|
Newsletter
|
||||||
</h2>
|
</h2>
|
||||||
@@ -50,15 +50,14 @@
|
|||||||
angemeldet.
|
angemeldet.
|
||||||
</p>
|
</p>
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="button bg-button bg-button--secondary"
|
class="relative button bg-button bg-button--secondary"
|
||||||
hx-post="{{ path('app_personal_data_newsletter') }}"
|
hx-post="{{ path('app_personal_data_newsletter') }}"
|
||||||
hx-target="#myep"
|
hx-target="#newsletter"
|
||||||
hx-swap="outerHTML">
|
hx-select="#newsletter"
|
||||||
{% if personalData.communication.newsletter %}
|
hx-indicator="#button-indicator"
|
||||||
jetzt abmelden
|
hx-swap="innerHTML">
|
||||||
{% else %}
|
{% if personalData.communication.newsletter %}jetzt abmelden{% else %}jetzt anmelden{% endif %}
|
||||||
jetzt anmelden
|
<span class="htmx-indicator absolute top-0 left-0 inset-0 bg-secondary/80 flex items-center justify-center" id="button-indicator">{% include '_partials/_spinner.html.twig' with { 'class': 'w-6 h-6 text-white' } %}</span>
|
||||||
{% endif %}
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,24 +4,23 @@
|
|||||||
<h1 class="mb-4">
|
<h1 class="mb-4">
|
||||||
Registrierung MyE&P
|
Registrierung MyE&P
|
||||||
</h1>
|
</h1>
|
||||||
{{ form_start(form) }}
|
{{ form_start(form, { 'attr': { 'data-action': 'loading#toggle' } }) }}
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-4 mb-4">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-4 mb-4">
|
||||||
{{ form_row(form.firstName) }}
|
<div>
|
||||||
{{ form_row(form.name) }}
|
{{ form_row(form.firstName) }}
|
||||||
{{ form_row(form.email) }}
|
{{ form_row(form.name) }}
|
||||||
{{ form_row(form.gender) }}
|
{{ form_row(form.email) }}
|
||||||
|
{{ form_row(form.gender) }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col lg:flex-row space-y-2 lg:space-y-0 lg:space-x-2">
|
<div class="flex flex-col lg:flex-row space-y-2 lg:space-y-0 lg:space-x-2">
|
||||||
<button type="submit" class="button bg-button">
|
<button type="submit" class="button bg-button">
|
||||||
registrieren
|
registrieren
|
||||||
</button>
|
</button>
|
||||||
<button type="button"
|
<a href="{{ path('app_login') }}"
|
||||||
hx-get="{{ path('app_login') }}"
|
class="button bg-button bg-button--secondary">
|
||||||
hx-target="#myep"
|
|
||||||
hx-swap="outerHTML"
|
|
||||||
class="button bg-button bg-button--secondary">
|
|
||||||
zum Login
|
zum Login
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{{ form_rest(form) }}
|
{{ form_rest(form) }}
|
||||||
{{ form_end(form) }}
|
{{ form_end(form) }}
|
||||||
|
|||||||
@@ -1,24 +1,26 @@
|
|||||||
{{ form_start(form) }}
|
{% extends 'layout.html.twig' %}
|
||||||
{{ form_row(form.email) }}
|
|
||||||
<div class="flex flex-col lg:flex-row space-y-2 lg:space-y-0 lg:space-x-2 mt-4">
|
{% block content %}
|
||||||
<button type="submit" class="button bg-button bg-button--secondary">
|
<h1 class="mb-4">
|
||||||
Passwort erneuern
|
Passwort vergessen
|
||||||
</button>
|
</h1>
|
||||||
<button type="button"
|
{{ form_start(form, { 'attr': { 'data-action': 'loading#toggle' } }) }}
|
||||||
hx-get="{{ path('app_login') }}"
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-4 mb-4">
|
||||||
hx-target="#login-form"
|
{{ form_row(form.email) }}
|
||||||
hx-select="#login-form"
|
</div>
|
||||||
hx-swap="outerHTML"
|
<div class="flex flex-col lg:flex-row space-y-2 lg:space-y-0 lg:space-x-2 mt-4">
|
||||||
class="button bg-button">
|
<button type="submit" class="button bg-button">
|
||||||
zum Login
|
Passwort erneuern
|
||||||
</button>
|
</button>
|
||||||
<button type="button"
|
<a href="{{ path('app_login') }}"
|
||||||
hx-get="{{ path('app_registration') }}"
|
class="button bg-button bg-button--secondary">
|
||||||
hx-target="#myep"
|
zum Login
|
||||||
hx-swap="outerHTML"
|
</a>
|
||||||
class="button bg-button">
|
<a href="{{ path('app_registration') }}"
|
||||||
registrieren
|
class="button bg-button bg-button--secondary">
|
||||||
</button>
|
registrieren
|
||||||
</div>
|
</a>
|
||||||
{{ form_rest(form) }}
|
</div>
|
||||||
{{ form_end(form) }}
|
{{ form_rest(form) }}
|
||||||
|
{{ form_end(form) }}
|
||||||
|
{% endblock %}
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
<span class="flex-1 text-sm">{{ error.messageKey|trans(error.messageData, 'security') }}</span>
|
<span class="flex-1 text-sm">{{ error.messageKey|trans(error.messageData, 'security') }}</span>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<form action="{{ path('app_login') }}" method="post">
|
<form action="{{ path('app_login') }}" method="post" {{ stimulus_action('loading', 'toggle') }}>
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="username" class="mb-1 font-semibold">
|
<label for="username" class="mb-1 font-semibold">
|
||||||
E-Mail:
|
E-Mail:
|
||||||
@@ -52,19 +52,14 @@
|
|||||||
<button type="submit" class="button bg-button">
|
<button type="submit" class="button bg-button">
|
||||||
Login
|
Login
|
||||||
</button>
|
</button>
|
||||||
<button type="button"
|
<a href="{{ path('app_reset_password') }}"
|
||||||
hx-get="{{ path('app_reset_password') }}"
|
class="button bg-button bg-button--secondary">
|
||||||
hx-target="#login-form"
|
|
||||||
class="button bg-button bg-button--secondary">
|
|
||||||
Passwort vergessen?
|
Passwort vergessen?
|
||||||
</button>
|
</a>
|
||||||
<button type="button"
|
<a href="{{ path('app_registration') }}"
|
||||||
hx-get="{{ path('app_registration') }}"
|
class="button bg-button bg-button--secondary">
|
||||||
hx-target="#myep"
|
|
||||||
hx-swap="outerHTML"
|
|
||||||
class="button bg-button bg-button--secondary">
|
|
||||||
registrieren
|
registrieren
|
||||||
</button>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">
|
<input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
Reference in New Issue
Block a user