From 99190b0ef1865e41afe54aeab9210021d71e3e42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Sat, 10 Jan 2026 11:35:16 +0100 Subject: [PATCH] wip: integrate with easy admin bundle --- composer.json | 1 + composer.lock | 196 +++++++++++++++++- config/bundles.php | 2 + config/packages/security.yaml | 3 +- config/packages/twig_component.yaml | 5 + config/routes/easyadmin.yaml | 3 + src/Admin/Field/JsonDataField.php | 19 ++ .../Admin/BookingEditDraftCrudController.php | 55 +++++ src/Controller/Admin/DashboardController.php | 38 ++++ .../Admin/LogEntryCrudController.php | 52 +++++ src/Controller/Admin/UserCrudController.php | 46 ++++ src/Entity/LogEntry.php | 10 + symfony.lock | 24 +++ templates/admin/dashboard/index.html.twig | 5 + templates/admin/field/json.html.twig | 22 ++ 15 files changed, 478 insertions(+), 3 deletions(-) create mode 100644 config/packages/twig_component.yaml create mode 100644 config/routes/easyadmin.yaml create mode 100644 src/Admin/Field/JsonDataField.php create mode 100644 src/Controller/Admin/BookingEditDraftCrudController.php create mode 100644 src/Controller/Admin/DashboardController.php create mode 100644 src/Controller/Admin/LogEntryCrudController.php create mode 100644 src/Controller/Admin/UserCrudController.php create mode 100644 templates/admin/dashboard/index.html.twig create mode 100644 templates/admin/field/json.html.twig diff --git a/composer.json b/composer.json index 27ef814..cff7deb 100644 --- a/composer.json +++ b/composer.json @@ -14,6 +14,7 @@ "doctrine/doctrine-bundle": "^2.13", "doctrine/doctrine-migrations-bundle": "^3.3", "doctrine/orm": "^3.3", + "easycorp/easyadmin-bundle": "^4.27", "league/flysystem-bundle": "^3.4", "league/flysystem-sftp-v3": "^3.29", "league/oauth2-server-bundle": "^1.0", diff --git a/composer.lock b/composer.lock index e492d9a..e09f7e3 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "fa2a91c6c7c6bc1761ab54f0020f1c8b", + "content-hash": "a8cba38524e7e05db6de57adc2077b59", "packages": [ { "name": "carbonphp/carbon-doctrine-types", @@ -1327,6 +1327,111 @@ ], "time": "2025-10-31T18:51:33+00:00" }, + { + "name": "easycorp/easyadmin-bundle", + "version": "v4.27.6", + "source": { + "type": "git", + "url": "https://github.com/EasyCorp/EasyAdminBundle.git", + "reference": "ece70f2111f006262cd30af16dc10b1720704e10" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/EasyCorp/EasyAdminBundle/zipball/ece70f2111f006262cd30af16dc10b1720704e10", + "reference": "ece70f2111f006262cd30af16dc10b1720704e10", + "shasum": "" + }, + "require": { + "doctrine/doctrine-bundle": "^2.5|^3.0", + "doctrine/orm": "^2.12|^3.0", + "php": ">=8.1", + "symfony/asset": "^5.4|^6.0|^7.0|^8.0", + "symfony/cache": "^5.4|^6.0|^7.0|^8.0", + "symfony/config": "^5.4|^6.0|^7.0|^8.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0|^8.0", + "symfony/deprecation-contracts": "^3.0", + "symfony/doctrine-bridge": "^5.4|^6.0|^7.0|^8.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0|^8.0", + "symfony/filesystem": "^5.4|^6.0|^7.0|^8.0", + "symfony/form": "^5.4|^6.0|^7.0|^8.0", + "symfony/framework-bundle": "^5.4|^6.0|^7.0|^8.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0|^8.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0|^8.0", + "symfony/intl": "^5.4|^6.0|^7.0|^8.0", + "symfony/property-access": "^5.4|^6.0|^7.0|^8.0", + "symfony/security-bundle": "^5.4|^6.0|^7.0|^8.0", + "symfony/string": "^5.4|^6.0|^7.0|^8.0", + "symfony/translation": "^5.4|^6.0|^7.0|^8.0", + "symfony/twig-bridge": "^5.4.48|^6.4.16|^7.1.9|^8.0", + "symfony/twig-bundle": "^5.4|^6.0|^7.0|^8.0", + "symfony/uid": "^5.4|^6.0|^7.0|^8.0", + "symfony/ux-twig-component": "^2.21", + "symfony/validator": "^5.4|^6.0|^7.0|^8.0", + "twig/extra-bundle": "^3.17", + "twig/html-extra": "^3.17", + "twig/twig": "^3.20" + }, + "conflict": { + "symfony/error-handler": "<5.4.35" + }, + "require-dev": { + "doctrine/doctrine-fixtures-bundle": "^3.4|3.5.x-dev|^4.0", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpstan/phpstan-symfony": "^2.0", + "psr/log": "^1.0|^2.0|^3.0", + "symfony/browser-kit": "^5.4|^6.0|^7.0|^8.0", + "symfony/css-selector": "^5.4|^6.0|^7.0|^8.0", + "symfony/debug-bundle": "^5.4|^6.0|^7.0|^8.0", + "symfony/dom-crawler": "^5.4|^6.0|^7.0|^8.0", + "symfony/expression-language": "^5.4|^6.0|^7.0|^8.0", + "symfony/phpunit-bridge": "^6.1|^7.0|^8.0", + "symfony/process": "^5.4|^6.0|^7.0|^8.0", + "symfony/web-link": "^5.4|^6.0|^7.0|^8.0", + "vincentlanglet/twig-cs-fixer": "^3.10" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "EasyCorp\\Bundle\\EasyAdminBundle\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Project Contributors", + "homepage": "https://github.com/EasyCorp/EasyAdminBundle/graphs/contributors" + } + ], + "description": "Admin generator for Symfony applications", + "homepage": "https://github.com/EasyCorp/EasyAdminBundle", + "keywords": [ + "admin", + "backend", + "generator" + ], + "support": { + "issues": "https://github.com/EasyCorp/EasyAdminBundle/issues", + "source": "https://github.com/EasyCorp/EasyAdminBundle/tree/v4.27.6" + }, + "funding": [ + { + "url": "https://github.com/javiereguiluz", + "type": "github" + } + ], + "time": "2026-01-04T09:25:06+00:00" + }, { "name": "egulias/email-validator", "version": "4.0.4", @@ -9201,6 +9306,93 @@ ], "time": "2025-07-10T08:14:14+00:00" }, + { + "name": "symfony/ux-twig-component", + "version": "v2.32.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/ux-twig-component.git", + "reference": "0a300088327d1b766733fdcd81ae4a77852d6177" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/ux-twig-component/zipball/0a300088327d1b766733fdcd81ae4a77852d6177", + "reference": "0a300088327d1b766733fdcd81ae4a77852d6177", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/dependency-injection": "^5.4|^6.0|^7.0|^8.0", + "symfony/deprecation-contracts": "^2.2|^3.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0|^8.0", + "symfony/property-access": "^5.4|^6.0|^7.0|^8.0", + "twig/twig": "^3.10.3" + }, + "conflict": { + "symfony/config": "<5.4.0" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0|^7.0|^8.0", + "symfony/css-selector": "^5.4|^6.0|^7.0|^8.0", + "symfony/dom-crawler": "^5.4|^6.0|^7.0|^8.0", + "symfony/framework-bundle": "^5.4|^6.0|^7.0|^8.0", + "symfony/phpunit-bridge": "^6.0|^7.0|^8.0", + "symfony/stimulus-bundle": "^2.9.1", + "symfony/twig-bundle": "^5.4|^6.0|^7.0|^8.0", + "symfony/webpack-encore-bundle": "^1.15|^2.3.0" + }, + "type": "symfony-bundle", + "extra": { + "thanks": { + "url": "https://github.com/symfony/ux", + "name": "symfony/ux" + } + }, + "autoload": { + "psr-4": { + "Symfony\\UX\\TwigComponent\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Twig components for Symfony", + "homepage": "https://symfony.com", + "keywords": [ + "components", + "symfony-ux", + "twig" + ], + "support": { + "source": "https://github.com/symfony/ux-twig-component/tree/v2.32.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-12-25T09:25:01+00:00" + }, { "name": "symfony/validator", "version": "v6.4.31", @@ -14065,5 +14257,5 @@ "platform-overrides": { "php": "8.2.28" }, - "plugin-api-version": "2.9.0" + "plugin-api-version": "2.6.0" } diff --git a/config/bundles.php b/config/bundles.php index 82998e3..325728f 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -16,4 +16,6 @@ return [ League\FlysystemBundle\FlysystemBundle::class => ['all' => true], League\Bundle\OAuth2ServerBundle\LeagueOAuth2ServerBundle::class => ['all' => true], Zenstruck\ScheduleBundle\ZenstruckScheduleBundle::class => ['all' => true], + Symfony\UX\TwigComponent\TwigComponentBundle::class => ['all' => true], + EasyCorp\Bundle\EasyAdminBundle\EasyAdminBundle::class => ['all' => true], ]; diff --git a/config/packages/security.yaml b/config/packages/security.yaml index ac54dcf..91fbd4d 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -33,7 +33,8 @@ security: # Note: Only the *first* access control that matches will be used access_control: - { path: ^/authorize, roles: IS_AUTHENTICATED_REMEMBERED, requires_channel: https } - - { path: '^/', roles: PUBLIC_ACCESS, requires_channel: https } + - { path: ^/admin, roles: ROLE_ADMIN, requires_channel: https } + - { path: ^/, roles: PUBLIC_ACCESS, requires_channel: https } when@test: security: diff --git a/config/packages/twig_component.yaml b/config/packages/twig_component.yaml new file mode 100644 index 0000000..fd17ac6 --- /dev/null +++ b/config/packages/twig_component.yaml @@ -0,0 +1,5 @@ +twig_component: + anonymous_template_directory: 'components/' + defaults: + # Namespace & directory for components + App\Twig\Components\: 'components/' diff --git a/config/routes/easyadmin.yaml b/config/routes/easyadmin.yaml new file mode 100644 index 0000000..083ca15 --- /dev/null +++ b/config/routes/easyadmin.yaml @@ -0,0 +1,3 @@ +easyadmin: + resource: . + type: easyadmin.routes diff --git a/src/Admin/Field/JsonDataField.php b/src/Admin/Field/JsonDataField.php new file mode 100644 index 0000000..1df8e10 --- /dev/null +++ b/src/Admin/Field/JsonDataField.php @@ -0,0 +1,19 @@ +setProperty($propertyName) + ->setLabel($label) + ->setTemplatePath('admin/field/json.html.twig'); + } +} diff --git a/src/Controller/Admin/BookingEditDraftCrudController.php b/src/Controller/Admin/BookingEditDraftCrudController.php new file mode 100644 index 0000000..441f66c --- /dev/null +++ b/src/Controller/Admin/BookingEditDraftCrudController.php @@ -0,0 +1,55 @@ +add(Crud::PAGE_INDEX, Action::DETAIL) + ->remove(Crud::PAGE_INDEX, Action::NEW) + ->remove(Crud::PAGE_INDEX, Action::EDIT) + ->remove(Crud::PAGE_DETAIL, Action::EDIT) + ; + } + + public function configureCrud(Crud $crud): Crud + { + return $crud + ->setEntityLabelInSingular('Buchungsentwurf') + ->setEntityLabelInPlural('Buchungsentwürfe') + ->setDefaultSort(['createdAt' => 'DESC']); + } + + public function configureFields(string $pageName): iterable + { + return [ + IntegerField::new('bookingId', 'Buchungs ID'), + AssociationField::new('user', 'Kundenaccount')->formatValue(fn (User $user) => $user->getEmail()), + DateTimeField::new('createdAt', 'erstellt am'), + DateTimeField::new('updatedAt', 'aktualisiert am'), + JsonDataField::new('formData', 'Daten')->onlyOnDetail() + ]; + } +} diff --git a/src/Controller/Admin/DashboardController.php b/src/Controller/Admin/DashboardController.php new file mode 100644 index 0000000..a075c8a --- /dev/null +++ b/src/Controller/Admin/DashboardController.php @@ -0,0 +1,38 @@ +render('admin/dashboard/index.html.twig'); + } + + public function configureDashboard(): Dashboard + { + return Dashboard::new() + ->setTitle('MyE&P') + ->setLocales(['de']) + ->setFaviconPath('build/favicon/icon.svg') + ; + } + + public function configureMenuItems(): iterable + { + yield MenuItem::linkToDashboard('Dashboard', 'fa fa-home'); + yield MenuItem::linkToCrud('Log', 'fa fa-list', LogEntry::class); + yield MenuItem::linkToCrud('Buchungsentwürfe', 'fa fa-pen-to-square', BookingEditDraft::class); + yield MenuItem::linkToCrud('Kundenaccounts', 'fa fa-users', User::class); + } +} diff --git a/src/Controller/Admin/LogEntryCrudController.php b/src/Controller/Admin/LogEntryCrudController.php new file mode 100644 index 0000000..105a350 --- /dev/null +++ b/src/Controller/Admin/LogEntryCrudController.php @@ -0,0 +1,52 @@ +add(Crud::PAGE_INDEX, Action::DETAIL) + ->remove(Crud::PAGE_INDEX, Action::NEW) + ->remove(Crud::PAGE_INDEX, Action::EDIT) + ->remove(Crud::PAGE_INDEX, Action::DELETE) + ->remove(Crud::PAGE_DETAIL, Action::EDIT) + ->remove(Crud::PAGE_DETAIL, Action::DELETE) + ; + } + + public function configureCrud(Crud $crud): Crud + { + return $crud + ->setEntityLabelInSingular('Logeintrag') + ->setEntityLabelInPlural('Logeinträge') + ->setDefaultSort(['createdAt' => 'DESC']); + } + + public function configureFields(string $pageName): iterable + { + return [ + DateTimeField::new('createdAt', 'Zeitstempel'), + TextField::new('username', 'Benutzer'), + TextField::new('requestId', 'Request ID'), + TextField::new('message', 'Aktion'), + ]; + } +} diff --git a/src/Controller/Admin/UserCrudController.php b/src/Controller/Admin/UserCrudController.php new file mode 100644 index 0000000..fe01565 --- /dev/null +++ b/src/Controller/Admin/UserCrudController.php @@ -0,0 +1,46 @@ +remove(Crud::PAGE_INDEX, Action::NEW) + ->remove(Crud::PAGE_INDEX, Action::EDIT) + ->remove(Crud::PAGE_INDEX, Action::DELETE) + ; + } + + public function configureCrud(Crud $crud): Crud + { + return $crud + ->setEntityLabelInSingular('Kundenaccount') + ->setEntityLabelInPlural('Kundenaccounts') + ->setDefaultSort(['lastLoginAt' => 'DESC']); + } + + public function configureFields(string $pageName): iterable + { + return [ + TextField::new('email', 'E-Mail'), + DateTimeField::new('lastLoginAt', 'letzter Login'), + ]; + } +} diff --git a/src/Entity/LogEntry.php b/src/Entity/LogEntry.php index c9abbc2..6aeed53 100644 --- a/src/Entity/LogEntry.php +++ b/src/Entity/LogEntry.php @@ -101,4 +101,14 @@ class LogEntry return $this; } + + public function getUsername(): string + { + return $this->getExtra()['user']['username'] ?? ''; + } + + public function getRequestId(): string + { + return $this->getExtra()['request_id'] ?? '-'; + } } diff --git a/symfony.lock b/symfony.lock index f57dc98..ec4e884 100644 --- a/symfony.lock +++ b/symfony.lock @@ -35,6 +35,18 @@ "migrations/.gitignore" ] }, + "easycorp/easyadmin-bundle": { + "version": "4.27", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "4.14", + "ref": "13b3e524d1038a6861ad6c7fc64e31b644ba0c54" + }, + "files": [ + "config/routes/easyadmin.yaml" + ] + }, "friendsofphp/php-cs-fixer": { "version": "3.75", "recipe": { @@ -328,6 +340,18 @@ "config/packages/uid.yaml" ] }, + "symfony/ux-twig-component": { + "version": "2.32", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "2.13", + "ref": "f367ae2a1faf01c503de2171f1ec22567febeead" + }, + "files": [ + "config/packages/twig_component.yaml" + ] + }, "symfony/validator": { "version": "6.4", "recipe": { diff --git a/templates/admin/dashboard/index.html.twig b/templates/admin/dashboard/index.html.twig new file mode 100644 index 0000000..baf39ab --- /dev/null +++ b/templates/admin/dashboard/index.html.twig @@ -0,0 +1,5 @@ +{% extends '@EasyAdmin/layout.html.twig' %} + +{% block main %} + +{% endblock main %} diff --git a/templates/admin/field/json.html.twig b/templates/admin/field/json.html.twig new file mode 100644 index 0000000..78e3ef8 --- /dev/null +++ b/templates/admin/field/json.html.twig @@ -0,0 +1,22 @@ +{% macro render_value(value) %} + {% import _self as self %} + {% if value is iterable %} + + {% else %} + {{ value }} + {% endif %} +{% endmacro %} + + {% import _self as self %} + {{ self.render_value(field.value) }}