From 584901f41349d412b2cfe564d53198887b76ce20 Mon Sep 17 00:00:00 2001 From: Constantin Simonis Date: Thu, 6 Feb 2025 18:48:21 +0100 Subject: [PATCH 01/12] add admin panel --- assets/controllers.json | 26 +- composer.json | 1 + composer.lock | 1050 ++++++++++++++++- config/bundles.php | 2 + config/packages/security.yaml | 39 + config/packages/translation.yaml | 7 + config/routes/security.yaml | 3 + .../Admin/CustomerCrudController.php | 42 + .../Admin/PaymentCrudController.php | 36 + src/Controller/Admin/TicketCrudController.php | 45 + src/Controller/Admin/ViewController.php | 40 + src/Entity/Payment.php | 8 + src/Enum/FoodData.php | 12 + src/Enum/TicketData.php | 7 + symfony.lock | 44 + translations/.gitignore | 0 16 files changed, 1348 insertions(+), 14 deletions(-) create mode 100644 config/packages/security.yaml create mode 100644 config/packages/translation.yaml create mode 100644 config/routes/security.yaml create mode 100644 src/Controller/Admin/CustomerCrudController.php create mode 100644 src/Controller/Admin/PaymentCrudController.php create mode 100644 src/Controller/Admin/TicketCrudController.php create mode 100644 src/Controller/Admin/ViewController.php create mode 100644 src/Enum/FoodData.php create mode 100644 translations/.gitignore diff --git a/assets/controllers.json b/assets/controllers.json index 103b202..29ea244 100644 --- a/assets/controllers.json +++ b/assets/controllers.json @@ -1,15 +1,15 @@ { - "controllers": { - "@symfony/ux-turbo": { - "turbo-core": { - "enabled": true, - "fetch": "eager" - }, - "mercure-turbo-stream": { - "enabled": false, - "fetch": "eager" - } - } - }, - "entrypoints": [] + "controllers": { + "@symfony/ux-turbo": { + "turbo-core": { + "enabled": true, + "fetch": "eager" + }, + "mercure-turbo-stream": { + "enabled": false, + "fetch": "eager" + } + } + }, + "entrypoints": [] } diff --git a/composer.json b/composer.json index d1506a7..5c6700a 100644 --- a/composer.json +++ b/composer.json @@ -12,6 +12,7 @@ "doctrine/doctrine-bundle": "^2.13", "doctrine/doctrine-migrations-bundle": "^3.4", "doctrine/orm": "^3.3", + "easycorp/easyadmin-bundle": "^4.24", "php-flasher/flasher-noty-symfony": "^2.1", "phpdocumentor/reflection-docblock": "^5.6", "stripe/stripe-php": "^16.4", diff --git a/composer.lock b/composer.lock index 61a72ee..e377fcd 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": "8dc2a9d9e624cf61b3c68d2678e9d53c", + "content-hash": "6e7803d436b49242a4a3816fdc776d3a", "packages": [ { "name": "composer/semver", @@ -1378,6 +1378,107 @@ }, "time": "2024-10-21T18:21:57+00:00" }, + { + "name": "easycorp/easyadmin-bundle", + "version": "v4.24.0", + "source": { + "type": "git", + "url": "https://github.com/EasyCorp/EasyAdminBundle.git", + "reference": "f641b359bfe23406469453f274dde167ecafd3a4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/EasyCorp/EasyAdminBundle/zipball/f641b359bfe23406469453f274dde167ecafd3a4", + "reference": "f641b359bfe23406469453f274dde167ecafd3a4", + "shasum": "" + }, + "require": { + "doctrine/doctrine-bundle": "^2.5", + "doctrine/orm": "^2.12|^3.0", + "ext-json": "*", + "php": ">=8.1", + "symfony/asset": "^5.4|^6.0|^7.0", + "symfony/cache": "^5.4|^6.0|^7.0", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/deprecation-contracts": "^3.0", + "symfony/doctrine-bridge": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/filesystem": "^5.4|^6.0|^7.0", + "symfony/form": "^5.4|^6.0|^7.0", + "symfony/framework-bundle": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/intl": "^5.4|^6.0|^7.0", + "symfony/property-access": "^5.4|^6.0|^7.0", + "symfony/security-bundle": "^5.4|^6.0|^7.0", + "symfony/string": "^5.4|^6.0|^7.0", + "symfony/translation": "^5.4|^6.0|^7.0", + "symfony/twig-bundle": "^5.4|^6.0|^7.0", + "symfony/uid": "^5.4|^6.0|^7.0", + "symfony/ux-twig-component": "^2.21", + "symfony/validator": "^5.4|^6.0|^7.0", + "twig/extra-bundle": "^3.17", + "twig/html-extra": "^3.17", + "twig/twig": "^3.15" + }, + "require-dev": { + "doctrine/doctrine-fixtures-bundle": "^3.4|3.5.x-dev", + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-phpunit": "^1.2", + "phpstan/phpstan-strict-rules": "^1.4", + "phpstan/phpstan-symfony": "^1.2", + "psr/log": "^1.0", + "symfony/browser-kit": "^5.4|^6.0|^7.0", + "symfony/css-selector": "^5.4|^6.0|^7.0", + "symfony/debug-bundle": "^5.4|^6.0|^7.0", + "symfony/dom-crawler": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/phpunit-bridge": "^6.1|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/web-link": "^5.4|^6.0|^7.0" + }, + "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.24.0" + }, + "funding": [ + { + "url": "https://github.com/javiereguiluz", + "type": "github" + } + ], + "time": "2025-01-31T19:11:07+00:00" + }, { "name": "egulias/email-validator", "version": "4.0.3", @@ -1992,6 +2093,54 @@ }, "time": "2021-02-03T23:26:27+00:00" }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, { "name": "psr/container", "version": "2.0.2", @@ -2526,6 +2675,80 @@ ], "time": "2024-09-25T14:20:29+00:00" }, + { + "name": "symfony/clock", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/clock.git", + "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/clock/zipball/b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", + "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/clock": "^1.0", + "symfony/polyfill-php83": "^1.28" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/now.php" + ], + "psr-4": { + "Symfony\\Component\\Clock\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Decouples applications from the system clock", + "homepage": "https://symfony.com", + "keywords": [ + "clock", + "psr20", + "time" + ], + "support": { + "source": "https://github.com/symfony/clock/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, { "name": "symfony/config", "version": "v7.2.0", @@ -4065,6 +4288,92 @@ ], "time": "2024-12-31T14:59:40+00:00" }, + { + "name": "symfony/intl", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/intl.git", + "reference": "76bb3462c6c308f8bd97d3c178c2626ae44d4dea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/intl/zipball/76bb3462c6c308f8bd97d3c178c2626ae44d4dea", + "reference": "76bb3462c6c308f8bd97d3c178c2626ae44d4dea", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/string": "<7.1" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0", + "symfony/var-exporter": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Intl\\": "" + }, + "exclude-from-classmap": [ + "/Tests/", + "/Resources/data/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Eriksen Costa", + "email": "eriksen.costa@infranology.com.br" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides access to the localization data of the ICU library", + "homepage": "https://symfony.com", + "keywords": [ + "i18n", + "icu", + "internationalization", + "intl", + "l10n", + "localization" + ], + "support": { + "source": "https://github.com/symfony/intl/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-11-25T14:26:33+00:00" + }, { "name": "symfony/mailer", "version": "v7.2.0", @@ -4296,6 +4605,78 @@ ], "time": "2024-11-20T11:17:29+00:00" }, + { + "name": "symfony/password-hasher", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/password-hasher.git", + "reference": "d8bd3d66d074c0acba1214a0d42f5941a8e1e94d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/password-hasher/zipball/d8bd3d66d074c0acba1214a0d42f5941a8e1e94d", + "reference": "d8bd3d66d074c0acba1214a0d42f5941a8e1e94d", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "conflict": { + "symfony/security-core": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0", + "symfony/security-core": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\PasswordHasher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Robin Chalas", + "email": "robin.chalas@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides password hashing utilities", + "homepage": "https://symfony.com", + "keywords": [ + "hashing", + "password" + ], + "support": { + "source": "https://github.com/symfony/password-hasher/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, { "name": "symfony/polyfill-intl-grapheme", "version": "v1.31.0", @@ -4778,6 +5159,85 @@ ], "time": "2024-09-09T11:45:10+00:00" }, + { + "name": "symfony/polyfill-uuid", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-uuid.git", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-uuid": "*" + }, + "suggest": { + "ext-uuid": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Uuid\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for uuid functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, { "name": "symfony/process", "version": "v7.2.0", @@ -5160,6 +5620,357 @@ ], "time": "2024-11-06T11:43:25+00:00" }, + { + "name": "symfony/security-bundle", + "version": "v7.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/security-bundle.git", + "reference": "721de227035c6e4c322fb7dd4839586d58bc0cf5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/security-bundle/zipball/721de227035c6e4c322fb7dd4839586d58bc0cf5", + "reference": "721de227035c6e4c322fb7dd4839586d58bc0cf5", + "shasum": "" + }, + "require": { + "composer-runtime-api": ">=2.1", + "ext-xml": "*", + "php": ">=8.2", + "symfony/clock": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4.11|^7.1.4", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/password-hasher": "^6.4|^7.0", + "symfony/security-core": "^7.2", + "symfony/security-csrf": "^6.4|^7.0", + "symfony/security-http": "^7.2", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/browser-kit": "<6.4", + "symfony/console": "<6.4", + "symfony/framework-bundle": "<6.4", + "symfony/http-client": "<6.4", + "symfony/ldap": "<6.4", + "symfony/serializer": "<6.4", + "symfony/twig-bundle": "<6.4", + "symfony/validator": "<6.4" + }, + "require-dev": { + "symfony/asset": "^6.4|^7.0", + "symfony/browser-kit": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/css-selector": "^6.4|^7.0", + "symfony/dom-crawler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/form": "^6.4|^7.0", + "symfony/framework-bundle": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/ldap": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/rate-limiter": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0", + "symfony/translation": "^6.4|^7.0", + "symfony/twig-bridge": "^6.4|^7.0", + "symfony/twig-bundle": "^6.4|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0", + "twig/twig": "^3.12", + "web-token/jwt-library": "^3.3.2|^4.0" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Symfony\\Bundle\\SecurityBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a tight integration of the Security component into the Symfony full-stack framework", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/security-bundle/tree/v7.2.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-01-07T09:39:55+00:00" + }, + { + "name": "symfony/security-core", + "version": "v7.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/security-core.git", + "reference": "466784ffcd0b5a16e05394335897f790b17d07e4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/security-core/zipball/466784ffcd0b5a16e05394335897f790b17d07e4", + "reference": "466784ffcd0b5a16e05394335897f790b17d07e4", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/event-dispatcher-contracts": "^2.5|^3", + "symfony/password-hasher": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/http-foundation": "<6.4", + "symfony/ldap": "<6.4", + "symfony/translation": "<6.4.3|>=7.0,<7.0.3", + "symfony/validator": "<6.4" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "psr/container": "^1.1|^2.0", + "psr/log": "^1|^2|^3", + "symfony/cache": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/ldap": "^6.4|^7.0", + "symfony/string": "^6.4|^7.0", + "symfony/translation": "^6.4.3|^7.0.3", + "symfony/validator": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Security\\Core\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Security Component - Core Library", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/security-core/tree/v7.2.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-01-27T11:08:17+00:00" + }, + { + "name": "symfony/security-csrf", + "version": "v7.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/security-csrf.git", + "reference": "2b4b0c46c901729e4e90719eacd980381f53e0a3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/security-csrf/zipball/2b4b0c46c901729e4e90719eacd980381f53e0a3", + "reference": "2b4b0c46c901729e4e90719eacd980381f53e0a3", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/security-core": "^6.4|^7.0" + }, + "conflict": { + "symfony/http-foundation": "<6.4" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Security\\Csrf\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Security Component - CSRF Library", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/security-csrf/tree/v7.2.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-01-02T18:42:10+00:00" + }, + { + "name": "symfony/security-http", + "version": "v7.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/security-http.git", + "reference": "d185c4126ef2ca8b89b6e81d67bf14a52532657f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/security-http/zipball/d185c4126ef2ca8b89b6e81d67bf14a52532657f", + "reference": "d185c4126ef2ca8b89b6e81d67bf14a52532657f", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/property-access": "^6.4|^7.0", + "symfony/security-core": "^7.2", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/clock": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/http-client-contracts": "<3.0", + "symfony/security-bundle": "<6.4", + "symfony/security-csrf": "<6.4" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/cache": "^6.4|^7.0", + "symfony/clock": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/http-client-contracts": "^3.0", + "symfony/rate-limiter": "^6.4|^7.0", + "symfony/routing": "^6.4|^7.0", + "symfony/security-csrf": "^6.4|^7.0", + "symfony/translation": "^6.4|^7.0", + "web-token/jwt-library": "^3.3.2|^4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Security\\Http\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Security Component - HTTP Integration", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/security-http/tree/v7.2.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-01-28T15:51:35+00:00" + }, { "name": "symfony/serializer", "version": "v7.2.3", @@ -5559,6 +6370,101 @@ ], "time": "2024-11-13T13:31:26+00:00" }, + { + "name": "symfony/translation", + "version": "v7.2.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "e2674a30132b7cc4d74540d6c2573aa363f05923" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/e2674a30132b7cc4d74540d6c2573aa363f05923", + "reference": "e2674a30132b7cc4d74540d6c2573aa363f05923", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.5|^3.0" + }, + "conflict": { + "symfony/config": "<6.4", + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4", + "symfony/service-contracts": "<2.5", + "symfony/twig-bundle": "<6.4", + "symfony/yaml": "<6.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "nikic/php-parser": "^4.18|^5.0", + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v7.2.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-07T08:18:10+00:00" + }, { "name": "symfony/translation-contracts", "version": "v3.5.1", @@ -5906,6 +6812,80 @@ ], "time": "2024-12-20T13:38:37+00:00" }, + { + "name": "symfony/uid", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/uid.git", + "reference": "2d294d0c48df244c71c105a169d0190bfb080426" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/uid/zipball/2d294d0c48df244c71c105a169d0190bfb080426", + "reference": "2d294d0c48df244c71c105a169d0190bfb080426", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-uuid": "^1.15" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Uid\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to generate and represent UIDs", + "homepage": "https://symfony.com", + "keywords": [ + "UID", + "ulid", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/uid/tree/v7.2.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, { "name": "symfony/ux-icons", "version": "v2.22.1", @@ -6633,6 +7613,74 @@ ], "time": "2024-09-26T19:22:23+00:00" }, + { + "name": "twig/html-extra", + "version": "v3.19.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/html-extra.git", + "reference": "c63b28e192c1b7c15bb60f81d2e48b140846239a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/html-extra/zipball/c63b28e192c1b7c15bb60f81d2e48b140846239a", + "reference": "c63b28e192c1b7c15bb60f81d2e48b140846239a", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/mime": "^5.4|^6.4|^7.0", + "twig/twig": "^3.13|^4.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Twig\\Extra\\Html\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "description": "A Twig extension for HTML", + "homepage": "https://twig.symfony.com", + "keywords": [ + "html", + "twig" + ], + "support": { + "source": "https://github.com/twigphp/html-extra/tree/v3.19.0" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2024-12-29T10:29:59+00:00" + }, { "name": "twig/twig", "version": "v3.18.0", diff --git a/config/bundles.php b/config/bundles.php index 6bf095b..30a3620 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -15,4 +15,6 @@ return [ Symfony\UX\Turbo\TurboBundle::class => ['all' => true], Symfony\UX\TwigComponent\TwigComponentBundle::class => ['all' => true], Symfony\UX\Icons\UXIconsBundle::class => ['all' => true], + Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], + EasyCorp\Bundle\EasyAdminBundle\EasyAdminBundle::class => ['all' => true], ]; diff --git a/config/packages/security.yaml b/config/packages/security.yaml new file mode 100644 index 0000000..367af25 --- /dev/null +++ b/config/packages/security.yaml @@ -0,0 +1,39 @@ +security: + # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords + password_hashers: + Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' + # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider + providers: + users_in_memory: { memory: null } + firewalls: + dev: + pattern: ^/(_(profiler|wdt)|css|images|js)/ + security: false + main: + lazy: true + provider: users_in_memory + + # activate different ways to authenticate + # https://symfony.com/doc/current/security.html#the-firewall + + # https://symfony.com/doc/current/security/impersonating_user.html + # switch_user: true + + # Easy way to control access for large sections of your site + # Note: Only the *first* access control that matches will be used + access_control: + # - { path: ^/admin, roles: ROLE_ADMIN } + # - { path: ^/profile, roles: ROLE_USER } + +when@test: + security: + password_hashers: + # By default, password hashers are resource intensive and take time. This is + # important to generate secure password hashes. In tests however, secure hashes + # are not important, waste resources and increase test times. The following + # reduces the work factor to the lowest possible values. + Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: + algorithm: auto + cost: 4 # Lowest possible value for bcrypt + time_cost: 3 # Lowest possible value for argon + memory_cost: 10 # Lowest possible value for argon diff --git a/config/packages/translation.yaml b/config/packages/translation.yaml new file mode 100644 index 0000000..b3f8f9c --- /dev/null +++ b/config/packages/translation.yaml @@ -0,0 +1,7 @@ +framework: + default_locale: en + translator: + default_path: '%kernel.project_dir%/translations' + fallbacks: + - en + providers: diff --git a/config/routes/security.yaml b/config/routes/security.yaml new file mode 100644 index 0000000..f853be1 --- /dev/null +++ b/config/routes/security.yaml @@ -0,0 +1,3 @@ +_security_logout: + resource: security.route_loader.logout + type: service diff --git a/src/Controller/Admin/CustomerCrudController.php b/src/Controller/Admin/CustomerCrudController.php new file mode 100644 index 0000000..9c5c557 --- /dev/null +++ b/src/Controller/Admin/CustomerCrudController.php @@ -0,0 +1,42 @@ +setCrudController(PaymentCrudController::class) + ->formatValue(fn(?Payment $payment) => ($payment?->getTotal() ?? 'N/A') . ' €'); + yield + } + + + public function configureActions(Actions $actions): Actions + { + return $actions + ->add(Crud::PAGE_INDEX, Action::DETAIL) + ->disable(Action::DELETE) + ->disable(Action::NEW) + ->disable(Action::EDIT); + } +} diff --git a/src/Controller/Admin/PaymentCrudController.php b/src/Controller/Admin/PaymentCrudController.php new file mode 100644 index 0000000..08356bc --- /dev/null +++ b/src/Controller/Admin/PaymentCrudController.php @@ -0,0 +1,36 @@ +setCrudController(CustomerCrudController::class) + ->formatValue(fn($value, $payment) => $payment->getCustomer()->getEmail()); + yield BooleanField::new('completed', 'Completed')->renderAsSwitch(false); + } + + public function configureActions(Actions $actions): Actions + { + return $actions + ->add(Crud::PAGE_INDEX, Action::DETAIL) + ->disable(Action::DELETE) + ->disable(Action::NEW) + ->disable(Action::EDIT); + } +} diff --git a/src/Controller/Admin/TicketCrudController.php b/src/Controller/Admin/TicketCrudController.php new file mode 100644 index 0000000..718208c --- /dev/null +++ b/src/Controller/Admin/TicketCrudController.php @@ -0,0 +1,45 @@ +setChoices(TicketData::TYPES); + + yield ChoiceField::new('foodType', 'Ernährung') + ->setChoices(FoodData::TYPES); + + yield AssociationField::new('customer', 'Kunde') + ->setCrudController(CustomerCrudController::class) + ->formatValue(fn(Customer $customer) => $customer->getEmail()); + } + + + public function configureActions(Actions $actions): Actions + { + return $actions + ->add(Crud::PAGE_INDEX, Action::DETAIL) + ->disable(Action::DELETE) + ->disable(Action::NEW) + ->disable(Action::EDIT); + } +} diff --git a/src/Controller/Admin/ViewController.php b/src/Controller/Admin/ViewController.php new file mode 100644 index 0000000..f03d94f --- /dev/null +++ b/src/Controller/Admin/ViewController.php @@ -0,0 +1,40 @@ +redirect($this->adminUrlGenerator->setController(CustomerCrudController::class)->generateUrl()); + } + + public function configureDashboard(): Dashboard + { + return Dashboard::new()->setTitle('Abiball Admin Interface'); + } + + public function configureMenuItems(): iterable + { + yield MenuItem::linkToCrud('Customers', 'fa fa-users', Customer::class); + yield MenuItem::linkToCrud('Tickets', 'fa fa-ticket', Ticket::class); + yield MenuItem::linkToCrud('Payments', 'fa fa-money', Payment::class); + } +} diff --git a/src/Entity/Payment.php b/src/Entity/Payment.php index 0e788af..80b6306 100644 --- a/src/Entity/Payment.php +++ b/src/Entity/Payment.php @@ -2,6 +2,7 @@ namespace App\Entity; +use App\Enum\TicketData; use App\Repository\PaymentRepository; use Doctrine\ORM\Mapping as ORM; @@ -63,4 +64,11 @@ class Payment return $this; } + + public function getTotal(): float + { + return $this->customer->getTickets()->reduce(function (float $total, Ticket $ticket) { + return $total + TicketData::TICKET_DATA[$ticket->getType()]['price']; + }, 0); + } } diff --git a/src/Enum/FoodData.php b/src/Enum/FoodData.php new file mode 100644 index 0000000..362961c --- /dev/null +++ b/src/Enum/FoodData.php @@ -0,0 +1,12 @@ + 1, + 'Vegetarisch' => 2, + 'Vegan' => 3, + ]; +} \ No newline at end of file diff --git a/src/Enum/TicketData.php b/src/Enum/TicketData.php index 0ef2355..d91b407 100644 --- a/src/Enum/TicketData.php +++ b/src/Enum/TicketData.php @@ -22,4 +22,11 @@ class TicketData 'price' => 0, ], ]; + + public const TYPES = [ + 'Ticket' => 1, + 'After-Show Ticket' => 2, + 'Kind (6-12 Jahre)' => 3, + 'Kind (0-6 Jahre)' => 4, + ]; } diff --git a/symfony.lock b/symfony.lock index 8377651..e488dc6 100644 --- a/symfony.lock +++ b/symfony.lock @@ -35,6 +35,15 @@ "migrations/.gitignore" ] }, + "easycorp/easyadmin-bundle": { + "version": "4.24", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "3.0", + "ref": "b131e6cbfe1b898a508987851963fff485986285" + } + }, "php-flasher/flasher-noty-symfony": { "version": "v2.1.2" }, @@ -146,6 +155,19 @@ "config/routes.yaml" ] }, + "symfony/security-bundle": { + "version": "7.2", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "6.4", + "ref": "2ae08430db28c8eb4476605894296c82a642028f" + }, + "files": [ + "config/packages/security.yaml", + "config/routes/security.yaml" + ] + }, "symfony/stimulus-bundle": { "version": "2.22", "recipe": { @@ -161,6 +183,19 @@ "assets/controllers/hello_controller.js" ] }, + "symfony/translation": { + "version": "7.2", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "6.3", + "ref": "e28e27f53663cc34f0be2837aba18e3a1bef8e7b" + }, + "files": [ + "config/packages/translation.yaml", + "translations/.gitignore" + ] + }, "symfony/twig-bundle": { "version": "7.2", "recipe": { @@ -174,6 +209,15 @@ "templates/base.html.twig" ] }, + "symfony/uid": { + "version": "7.2", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "7.0", + "ref": "0df5844274d871b37fc3816c57a768ffc60a43a5" + } + }, "symfony/ux-icons": { "version": "2.22", "recipe": { diff --git a/translations/.gitignore b/translations/.gitignore new file mode 100644 index 0000000..e69de29 -- 2.47.2 From 4820bbec3826128ba4ed4fd33939ec1e66eb1e35 Mon Sep 17 00:00:00 2001 From: Constantin Simonis Date: Thu, 6 Feb 2025 19:46:21 +0100 Subject: [PATCH 02/12] refactor --- .../Admin/CustomerCrudController.php | 25 ++++++++++++--- .../Admin/PaymentCrudController.php | 11 +++++-- src/Controller/Admin/TicketCrudController.php | 7 +++++ src/Enum/FoodData.php | 6 ++++ src/Twig/Ticket.php | 31 +++++++++++++++++++ templates/admin/customer_tickets.html.twig | 29 +++++++++++++++++ 6 files changed, 102 insertions(+), 7 deletions(-) create mode 100644 src/Twig/Ticket.php create mode 100644 templates/admin/customer_tickets.html.twig diff --git a/src/Controller/Admin/CustomerCrudController.php b/src/Controller/Admin/CustomerCrudController.php index 9c5c557..ec8a4f2 100644 --- a/src/Controller/Admin/CustomerCrudController.php +++ b/src/Controller/Admin/CustomerCrudController.php @@ -4,11 +4,15 @@ namespace App\Controller\Admin; use App\Entity\Customer; use App\Entity\Payment; +use Doctrine\Common\Collections\Collection; use EasyCorp\Bundle\EasyAdminBundle\Config\Action; use EasyCorp\Bundle\EasyAdminBundle\Config\Actions; use EasyCorp\Bundle\EasyAdminBundle\Config\Crud; use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController; use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField; +use EasyCorp\Bundle\EasyAdminBundle\Field\CollectionField; +use EasyCorp\Bundle\EasyAdminBundle\Field\EmailField; +use EasyCorp\Bundle\EasyAdminBundle\Field\TelephoneField; use EasyCorp\Bundle\EasyAdminBundle\Field\TextField; class CustomerCrudController extends AbstractCrudController @@ -22,12 +26,15 @@ class CustomerCrudController extends AbstractCrudController { yield TextField::new('firstname', 'Vorname'); yield TextField::new('lastname', 'Nachname'); - yield TextField::new('email', 'E-Mail'); - yield TextField::new('phone', 'Telefon'); - yield AssociationField::new('payment', 'Zahlung') + yield EmailField::new('email', 'E-Mail'); + yield TelephoneField::new('phone', 'Telefon'); + yield AssociationField::new('payment', 'Total') ->setCrudController(PaymentCrudController::class) - ->formatValue(fn(?Payment $payment) => ($payment?->getTotal() ?? 'N/A') . ' €'); - yield + ->formatValue(fn(?Payment $payment) => ($payment?->getTotal() ?? 0.0) . ' €') + ->hideOnIndex(); + yield CollectionField::new('tickets', 'Tickets') + ->setTemplatePath('admin/customer_tickets.html.twig') + ->hideOnIndex(); } @@ -39,4 +46,12 @@ class CustomerCrudController extends AbstractCrudController ->disable(Action::NEW) ->disable(Action::EDIT); } + + public function configureCrud(Crud $crud): Crud + { + return $crud + ->setPageTitle(Crud::PAGE_INDEX, 'Kunden') + ->setPageTitle(Crud::PAGE_DETAIL, 'Kunden') + ->showEntityActionsInlined(); + } } diff --git a/src/Controller/Admin/PaymentCrudController.php b/src/Controller/Admin/PaymentCrudController.php index 08356bc..5e146ea 100644 --- a/src/Controller/Admin/PaymentCrudController.php +++ b/src/Controller/Admin/PaymentCrudController.php @@ -19,10 +19,10 @@ class PaymentCrudController extends AbstractCrudController public function configureFields(string $pageName): iterable { - yield AssociationField::new('customer', 'Customer') + yield AssociationField::new('customer', 'Kunde') ->setCrudController(CustomerCrudController::class) ->formatValue(fn($value, $payment) => $payment->getCustomer()->getEmail()); - yield BooleanField::new('completed', 'Completed')->renderAsSwitch(false); + yield BooleanField::new('completed', 'Bezahlt')->renderAsSwitch(false); } public function configureActions(Actions $actions): Actions @@ -33,4 +33,11 @@ class PaymentCrudController extends AbstractCrudController ->disable(Action::NEW) ->disable(Action::EDIT); } + + public function configureCrud(Crud $crud): Crud + { + return $crud + ->setPageTitle(Crud::PAGE_INDEX, 'Zahlungen') + ->setPageTitle(Crud::PAGE_DETAIL, 'Zahlung'); + } } diff --git a/src/Controller/Admin/TicketCrudController.php b/src/Controller/Admin/TicketCrudController.php index 718208c..b7f462f 100644 --- a/src/Controller/Admin/TicketCrudController.php +++ b/src/Controller/Admin/TicketCrudController.php @@ -42,4 +42,11 @@ class TicketCrudController extends AbstractCrudController ->disable(Action::NEW) ->disable(Action::EDIT); } + + public function configureCrud(Crud $crud): Crud + { + return $crud + ->setPageTitle(Crud::PAGE_INDEX, 'Tickets') + ->setPageTitle(Crud::PAGE_DETAIL, 'Ticket'); + } } diff --git a/src/Enum/FoodData.php b/src/Enum/FoodData.php index 362961c..dba7975 100644 --- a/src/Enum/FoodData.php +++ b/src/Enum/FoodData.php @@ -4,6 +4,12 @@ namespace App\Enum; class FoodData { + public const FOOD_DATA = [ + 1 => 'Mit Fleisch', + 2 => 'Vegetarisch', + 3 => 'Vegan' + ]; + public const TYPES = [ 'Mit Fleisch' => 1, 'Vegetarisch' => 2, diff --git a/src/Twig/Ticket.php b/src/Twig/Ticket.php new file mode 100644 index 0000000..1515c91 --- /dev/null +++ b/src/Twig/Ticket.php @@ -0,0 +1,31 @@ +getFoodName(...)), + new TwigFilter('ticket', $this->getTicket(...)), + + ]; + } + + public function getFoodName(int $id): string + { + return FoodData::FOOD_DATA[$id] ?? 'N/A'; + } + + public function getTicket(int $id): array + { + return TicketData::TICKET_DATA[$id] ?? ['name' => 'N/A', 'price' => 'N/A']; + } +} \ No newline at end of file diff --git a/templates/admin/customer_tickets.html.twig b/templates/admin/customer_tickets.html.twig new file mode 100644 index 0000000..283c731 --- /dev/null +++ b/templates/admin/customer_tickets.html.twig @@ -0,0 +1,29 @@ + + + + + + + + + + + {% for ticket in field.value %} + + + + + + + {% else %} + + + + {% endfor %} + +
NameErnährungPreis
{{ (ticket.type | ticket)['name'] }}{{ ticket.foodType | food }}{{ (ticket.type | ticket)['price'] }}€ + + View + +
No Orders Found
-- 2.47.2 From ea7b2e6aea39cd40f48b9b45cd1f8e7ad82cdcab Mon Sep 17 00:00:00 2001 From: Constantin Simonis Date: Thu, 6 Feb 2025 20:10:16 +0100 Subject: [PATCH 03/12] add wip dashboard --- src/Controller/Admin/ViewController.php | 11 ++++++++--- src/DataObjects/FoodData.php | 13 +++++++++++++ src/Repository/PaymentRepository.php | 11 +++++++++++ src/Repository/TicketRepository.php | 19 +++++++++++++++++++ templates/admin/dashboard.html.twig | 8 ++++++++ 5 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 src/DataObjects/FoodData.php create mode 100644 templates/admin/dashboard.html.twig diff --git a/src/Controller/Admin/ViewController.php b/src/Controller/Admin/ViewController.php index f03d94f..4f364ab 100644 --- a/src/Controller/Admin/ViewController.php +++ b/src/Controller/Admin/ViewController.php @@ -5,25 +5,29 @@ namespace App\Controller\Admin; use App\Entity\Customer; use App\Entity\Payment; use App\Entity\Ticket; +use App\Repository\PaymentRepository; +use App\Repository\TicketRepository; use EasyCorp\Bundle\EasyAdminBundle\Attribute\AdminDashboard; use EasyCorp\Bundle\EasyAdminBundle\Config\Dashboard; use EasyCorp\Bundle\EasyAdminBundle\Config\MenuItem; use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractDashboardController; -use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; #[AdminDashboard(routePath: '/admin', routeName: 'admin')] class ViewController extends AbstractDashboardController { - public function __construct(private AdminUrlGenerator $adminUrlGenerator) + public function __construct(private readonly PaymentRepository $paymentRepository, private readonly TicketRepository $ticketRepository) { } #[Route('/admin', name: 'admin')] public function index(): Response { - return $this->redirect($this->adminUrlGenerator->setController(CustomerCrudController::class)->generateUrl()); + return $this->render('admin/dashboard.html.twig', [ + 'total' => $this->paymentRepository->getTotalMoneyMade(), + 'foodData' => $this->ticketRepository->getFoodData(), + ]); } public function configureDashboard(): Dashboard @@ -33,6 +37,7 @@ class ViewController extends AbstractDashboardController public function configureMenuItems(): iterable { + yield MenuItem::linktoDashboard('Dashboard', 'fa fa-home'); yield MenuItem::linkToCrud('Customers', 'fa fa-users', Customer::class); yield MenuItem::linkToCrud('Tickets', 'fa fa-ticket', Ticket::class); yield MenuItem::linkToCrud('Payments', 'fa fa-money', Payment::class); diff --git a/src/DataObjects/FoodData.php b/src/DataObjects/FoodData.php new file mode 100644 index 0000000..1cbd2cc --- /dev/null +++ b/src/DataObjects/FoodData.php @@ -0,0 +1,13 @@ +findAll(); + $total = 0.0; + foreach ($all as $payment) { + $total += $payment->getTotal(); + } + + return $total; + } } diff --git a/src/Repository/TicketRepository.php b/src/Repository/TicketRepository.php index fce3bff..cd6e338 100644 --- a/src/Repository/TicketRepository.php +++ b/src/Repository/TicketRepository.php @@ -2,6 +2,7 @@ namespace App\Repository; +use App\DataObjects\FoodData; use App\Entity\Ticket; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Persistence\ManagerRegistry; @@ -15,4 +16,22 @@ class TicketRepository extends ServiceEntityRepository { parent::__construct($registry, Ticket::class); } + + public function getFoodData(): FoodData + { + $qb = $this->createQueryBuilder('t') + ->select( + 'SUM(CASE WHEN t.foodType = 1 THEN 1 ELSE 0 END) as totalMeat', + 'SUM(CASE WHEN t.foodType = 2 THEN 1 ELSE 0 END) as totalVegetarian', + 'SUM(CASE WHEN t.foodType = 3 THEN 1 ELSE 0 END) as totalVegan' + ); + + $result = $qb->getQuery()->getSingleResult(); + + return new FoodData( + (int) $result['totalMeat'], + (int) $result['totalVegetarian'], + (int) $result['totalVegan'] + ); + } } diff --git a/templates/admin/dashboard.html.twig b/templates/admin/dashboard.html.twig new file mode 100644 index 0000000..0f39655 --- /dev/null +++ b/templates/admin/dashboard.html.twig @@ -0,0 +1,8 @@ +{% extends '@EasyAdmin/page/content.html.twig' %} + +{% block content %} + {{ total }} € + meat: {{ foodData.totalMeat }} + vegetarian: {{ foodData.totalVegetarian }} + vegan: {{ foodData.totalVegan }} +{% endblock %} \ No newline at end of file -- 2.47.2 From c74c32d3164086e80710a8613171337af37a0d9d Mon Sep 17 00:00:00 2001 From: Constantin Simonis Date: Thu, 6 Feb 2025 21:04:47 +0100 Subject: [PATCH 04/12] some improvements --- src/Controller/Admin/ViewController.php | 7 +++-- templates/admin/customer_tickets.html.twig | 2 ++ templates/admin/dashboard.html.twig | 34 +++++++++++++++++++--- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/Controller/Admin/ViewController.php b/src/Controller/Admin/ViewController.php index 4f364ab..a5c1d12 100644 --- a/src/Controller/Admin/ViewController.php +++ b/src/Controller/Admin/ViewController.php @@ -25,8 +25,9 @@ class ViewController extends AbstractDashboardController public function index(): Response { return $this->render('admin/dashboard.html.twig', [ - 'total' => $this->paymentRepository->getTotalMoneyMade(), + 'totalMoney' => $this->paymentRepository->getTotalMoneyMade(), 'foodData' => $this->ticketRepository->getFoodData(), + 'totalTickets' => $this->ticketRepository->count(), ]); } @@ -38,8 +39,8 @@ class ViewController extends AbstractDashboardController public function configureMenuItems(): iterable { yield MenuItem::linktoDashboard('Dashboard', 'fa fa-home'); - yield MenuItem::linkToCrud('Customers', 'fa fa-users', Customer::class); + yield MenuItem::linkToCrud('Kunden', 'fa fa-users', Customer::class); yield MenuItem::linkToCrud('Tickets', 'fa fa-ticket', Ticket::class); - yield MenuItem::linkToCrud('Payments', 'fa fa-money', Payment::class); + yield MenuItem::linkToCrud('Zahlungen', 'fa fa-money', Payment::class); } } diff --git a/templates/admin/customer_tickets.html.twig b/templates/admin/customer_tickets.html.twig index 283c731..14f9b55 100644 --- a/templates/admin/customer_tickets.html.twig +++ b/templates/admin/customer_tickets.html.twig @@ -4,6 +4,7 @@ Name Ernährung Preis + Anmerkung @@ -13,6 +14,7 @@ {{ (ticket.type | ticket)['name'] }} {{ ticket.foodType | food }} {{ (ticket.type | ticket)['price'] }}€ + {{ ticket.note }} diff --git a/templates/admin/dashboard.html.twig b/templates/admin/dashboard.html.twig index 0f39655..0d4416f 100644 --- a/templates/admin/dashboard.html.twig +++ b/templates/admin/dashboard.html.twig @@ -1,8 +1,34 @@ {% extends '@EasyAdmin/page/content.html.twig' %} {% block content %} - {{ total }} € - meat: {{ foodData.totalMeat }} - vegetarian: {{ foodData.totalVegetarian }} - vegan: {{ foodData.totalVegan }} +
+
+
+
+
+
Ticketwert insgesamt
+

{{ totalMoney }} €

+
+
+
+
+
+
+
Essens Daten
+

Mit Fleisch: {{ foodData.totalMeat }}

+

Vegetarisch: {{ foodData.totalVegetarian }}

+

Vegan: {{ foodData.totalVegan }}

+
+
+
+
+
+
+
Ticket Anzahl
+

{{ totalTickets }}

+
+
+
+
+
{% endblock %} \ No newline at end of file -- 2.47.2 From 94127740d54fb127e5c02046234af9be9c024024 Mon Sep 17 00:00:00 2001 From: Constantin Simonis Date: Fri, 7 Feb 2025 11:50:38 +0100 Subject: [PATCH 05/12] wip --- config/packages/security.yaml | 13 +++++- src/Controller/Admin/SecurityController.php | 18 +++++++++ src/Security/AdminPanelAuthenticator.php | 45 +++++++++++++++++++++ templates/admin/login.html.twig | 18 +++++++++ 4 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 src/Controller/Admin/SecurityController.php create mode 100644 src/Security/AdminPanelAuthenticator.php create mode 100644 templates/admin/login.html.twig diff --git a/config/packages/security.yaml b/config/packages/security.yaml index 367af25..9c1b866 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -4,7 +4,12 @@ security: Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider providers: - users_in_memory: { memory: null } + users_in_memory: + memory: + users: + user: { password: '123', roles: ['ROLE_ADMIN'] } + admin: { password: '123', roles: ['ROLE_SUPER_ADMIN'] } + firewalls: dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ @@ -12,6 +17,9 @@ security: main: lazy: true provider: users_in_memory + custom_authenticator: App\Security\AdminPanelAuthenticator + form_login: + login_path: /admin/login # activate different ways to authenticate # https://symfony.com/doc/current/security.html#the-firewall @@ -22,7 +30,8 @@ security: # Easy way to control access for large sections of your site # Note: Only the *first* access control that matches will be used access_control: - # - { path: ^/admin, roles: ROLE_ADMIN } + - { path: ^/admin/login, roles: PUBLIC_ACCESS } + - { path: ^/admin, roles: ROLE_ADMIN } # - { path: ^/profile, roles: ROLE_USER } when@test: diff --git a/src/Controller/Admin/SecurityController.php b/src/Controller/Admin/SecurityController.php new file mode 100644 index 0000000..f00bed0 --- /dev/null +++ b/src/Controller/Admin/SecurityController.php @@ -0,0 +1,18 @@ +render('admin/login.html.twig'); + } +} \ No newline at end of file diff --git a/src/Security/AdminPanelAuthenticator.php b/src/Security/AdminPanelAuthenticator.php new file mode 100644 index 0000000..348843d --- /dev/null +++ b/src/Security/AdminPanelAuthenticator.php @@ -0,0 +1,45 @@ +getRequestUri(), '/admin'); + } + + public function authenticate(Request $request): Passport + { + throw new CustomUserMessageAuthenticationException(); + } + + public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response + { + return null; + } + + public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response + { + return null; + } + + // public function start(Request $request, ?AuthenticationException $authException = null): Response + // { + // /* + // * If you would like this class to control what happens when an anonymous user accesses a + // * protected page (e.g. redirect to /login), uncomment this method and make this class + // * implement Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface. + // * + // * For more details, see https://symfony.com/doc/current/security/experimental_authenticators.html#configuring-the-authentication-entry-point + // */ + // } +} diff --git a/templates/admin/login.html.twig b/templates/admin/login.html.twig new file mode 100644 index 0000000..9384089 --- /dev/null +++ b/templates/admin/login.html.twig @@ -0,0 +1,18 @@ +{% extends 'base.html.twig' %} + +{% block body %} +
+

Login

+
+
+ + +
+
+ + +
+ +
+
+{% endblock %} \ No newline at end of file -- 2.47.2 From 76ee85ab1fcac0d1425628e542bdae75473b397e Mon Sep 17 00:00:00 2001 From: Jan-Marlon Leibl Date: Fri, 7 Feb 2025 12:00:35 +0100 Subject: [PATCH 06/12] feat(admin): enhance login form design and functionality --- templates/admin/login.html.twig | 62 ++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/templates/admin/login.html.twig b/templates/admin/login.html.twig index 9384089..51dbb45 100644 --- a/templates/admin/login.html.twig +++ b/templates/admin/login.html.twig @@ -1,18 +1,62 @@ {% extends 'base.html.twig' %} {% block body %} -
-

Login

-
+
+
+
+

Administration

+
+ + + +
- - + +
+
+ +
+ +
-
- - + +
+ +
+
+ +
+ +
+
+ +
+ + +
+ +
+
-
+
{% endblock %} \ No newline at end of file -- 2.47.2 From 40186c61495607a777e933343e2e72368fb11662 Mon Sep 17 00:00:00 2001 From: Constantin Simonis Date: Fri, 7 Feb 2025 12:31:19 +0100 Subject: [PATCH 07/12] add security --- .env | 5 +++++ .env.dev | 4 +++- config/packages/security.yaml | 13 +++++++++--- src/Controller/Admin/SecurityController.php | 10 ++++++--- src/DataObjects/LoginData.php | 23 +++++++++++++++++++++ src/Security/AdminPanelAuthenticator.php | 18 +++++++++++++--- templates/admin/login.html.twig | 20 +++++++----------- 7 files changed, 71 insertions(+), 22 deletions(-) create mode 100644 src/DataObjects/LoginData.php diff --git a/.env b/.env index 5a204fb..4d69617 100644 --- a/.env +++ b/.env @@ -16,4 +16,9 @@ DATABASE_URL="postgresql://${DB_USER:-db}:${DB_PW:-db}@${DB_HOST:-db}:${DB_PORT: ### STRIPE STRIPE_PUBLIC_KEY=${STRIPE_PUBLIC_KEY} STRIPE_SECRET_KEY=${STRIPE_PUBLIC_KEY} +### + +### ADMIN PANEL +USER_PASSWORD=${USER_PASSWORD} +ADMIN_PASSWORD=${ADMIN_PASSWORD} ### \ No newline at end of file diff --git a/.env.dev b/.env.dev index f248cd7..9350a9f 100644 --- a/.env.dev +++ b/.env.dev @@ -1,4 +1,6 @@ - ###> symfony/framework-bundle ### APP_SECRET=5a866a6ab3ce4ef99240ba643868b123 ###< symfony/framework-bundle ### + +USER_PASSWORD=\$2y\$13\$z/XlUykvakLzDR8TeFrQk.jmGuOKOcULlMY/m17aWmkY4f4NrIaam +ADMIN_PASSWORD=\$2y\$13\$z/XlUykvakLzDR8TeFrQk.jmGuOKOcULlMY/m17aWmkY4f4NrIaam \ No newline at end of file diff --git a/config/packages/security.yaml b/config/packages/security.yaml index 9c1b866..d9edc6b 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -7,8 +7,8 @@ security: users_in_memory: memory: users: - user: { password: '123', roles: ['ROLE_ADMIN'] } - admin: { password: '123', roles: ['ROLE_SUPER_ADMIN'] } + user: { password: '%env(USER_PASSWORD)%', roles: ['ROLE_ADMIN'] } + admin: { password: '%env(ADMIN_PASSWORD)%', roles: ['ROLE_SUPER_ADMIN'] } firewalls: dev: @@ -20,7 +20,10 @@ security: custom_authenticator: App\Security\AdminPanelAuthenticator form_login: login_path: /admin/login - + check_path: /admin/login + logout: + path: /admin/logout + target: /admin/login # activate different ways to authenticate # https://symfony.com/doc/current/security.html#the-firewall @@ -29,6 +32,10 @@ security: # Easy way to control access for large sections of your site # Note: Only the *first* access control that matches will be used + + role_hierarchy: + ROLE_SUPER_ADMIN: ROLE_ADMIN + access_control: - { path: ^/admin/login, roles: PUBLIC_ACCESS } - { path: ^/admin, roles: ROLE_ADMIN } diff --git a/src/Controller/Admin/SecurityController.php b/src/Controller/Admin/SecurityController.php index f00bed0..75e0239 100644 --- a/src/Controller/Admin/SecurityController.php +++ b/src/Controller/Admin/SecurityController.php @@ -7,12 +7,16 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Security\Http\Authentication\AuthenticationUtils; class SecurityController extends AbstractController { - #[Route(path: '/admin/login', name: 'admin_login', methods: Request::METHOD_GET)] - public function login(): Response + #[Route(path: '/admin/login', name: 'admin_login', methods: [Request::METHOD_GET, Request::METHOD_POST])] + public function login(AuthenticationUtils $authenticationUtils): Response { - return $this->render('admin/login.html.twig'); + return $this->render('admin/login.html.twig', [ + 'last_username' => $authenticationUtils->getLastUsername(), + 'error' => $authenticationUtils->getLastAuthenticationError(), + ]); } } \ No newline at end of file diff --git a/src/DataObjects/LoginData.php b/src/DataObjects/LoginData.php new file mode 100644 index 0000000..63f3031 --- /dev/null +++ b/src/DataObjects/LoginData.php @@ -0,0 +1,23 @@ +get('_username'), + $request->get('_password'), + ); + } +} \ No newline at end of file diff --git a/src/Security/AdminPanelAuthenticator.php b/src/Security/AdminPanelAuthenticator.php index 348843d..cceb699 100644 --- a/src/Security/AdminPanelAuthenticator.php +++ b/src/Security/AdminPanelAuthenticator.php @@ -2,29 +2,41 @@ namespace App\Security; +use App\DataObjects\LoginData; +use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException; use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator; +use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge; +use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; +use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials; use Symfony\Component\Security\Http\Authenticator\Passport\Passport; class AdminPanelAuthenticator extends AbstractAuthenticator { public function supports(Request $request): ?bool { - return str_starts_with($request->getRequestUri(), '/admin'); + return str_starts_with($request->getRequestUri(), '/admin') && $request->isMethod(Request::METHOD_POST); } public function authenticate(Request $request): Passport { - throw new CustomUserMessageAuthenticationException(); + $data = LoginData::fromRequest($request); + + if ($request->isMethod(Request::METHOD_POST) && (!$data->password || !$data->username)) { + dd($data); + throw new CustomUserMessageAuthenticationException(); + } + + return new Passport(new UserBadge($data->username), new PasswordCredentials($data->password), [new RememberMeBadge()]); } public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response { - return null; + return new RedirectResponse('/admin'); } public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response diff --git a/templates/admin/login.html.twig b/templates/admin/login.html.twig index 51dbb45..36c515b 100644 --- a/templates/admin/login.html.twig +++ b/templates/admin/login.html.twig @@ -4,10 +4,14 @@
-

Administration

+

Abiball Admin Panel

-
+ {% if error %} + {{ error.message }} + {% endif %} + +
@@ -21,7 +25,7 @@ name="_username" required class="block w-full pl-9 sm:pl-10 py-2 sm:py-2.5 text-sm sm:text-base bg-[#2a2a2a] border border-[#333333] text-gray-200 rounded-md focus:ring-2 focus:ring-orange-500/20 focus:border-orange-500 transition-colors" - placeholder="admin@example.com" /> + placeholder="username" />
@@ -40,15 +44,7 @@
-
- - -
+