From 0aa25d107bb5c1c8ceb148deaedf8ff07270d761 Mon Sep 17 00:00:00 2001 From: lubiana Date: Sun, 26 Jan 2025 11:48:16 +0100 Subject: [PATCH 01/41] improve coverage, remove infection --- .forgejo/workflows/pull_request.yml | 14 +- .forgejo/workflows/push.yml | 2 +- composer.json | 10 +- composer.lock | 1211 +---------------- src/Form/MenuItemType.php | 40 +- src/Repository/MenuItemRepository.php | 49 +- templates/menu_item/show.html.twig | 12 + tests/Controller/FoodVendorControllerTest.php | 8 +- tests/Controller/MenuItemControllerTest.php | 106 +- tests/Entity/MenuItemTest.php | 25 + 10 files changed, 160 insertions(+), 1317 deletions(-) diff --git a/.forgejo/workflows/pull_request.yml b/.forgejo/workflows/pull_request.yml index dd18b5b..6bbc920 100644 --- a/.forgejo/workflows/pull_request.yml +++ b/.forgejo/workflows/pull_request.yml @@ -23,19 +23,7 @@ jobs: - name: lint run: composer lint - name: test - run: composer mutation - - name: Add comment to pull request - run: | - echo '```' >> /tmp/pull-request-comment - cat var/log/infection.txt >> /tmp/pull-request-comment - cat var/log/summary.log >> /tmp/pull-request-comment - echo '```' >> /tmp/pull-request-comment - jq -n --arg msg "$(cat /tmp/pull-request-comment)" '{"body": $msg}' > /tmp/git-msg - curl -X POST \ - -H "Authorization: token ${GITHUB_TOKEN}" \ - -H "Content-Type: application/json" \ - -d @/tmp/git-msg \ - "${{ env.GITHUB_SERVER_URL }}/api/v1/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" + run: composer test - name: GIT commit and push all changed files env: CI_COMMIT_MESSAGE: Continuous Integration Fixes diff --git a/.forgejo/workflows/push.yml b/.forgejo/workflows/push.yml index 42aad3b..b4a5837 100644 --- a/.forgejo/workflows/push.yml +++ b/.forgejo/workflows/push.yml @@ -27,7 +27,7 @@ jobs: - name: lint run: composer lint - name: test - run: composer mutation + run: composer test - name: GIT commit and push all changed files env: CI_COMMIT_MESSAGE: Continuous Integration Fixes diff --git a/composer.json b/composer.json index 0485899..003b2b5 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,6 @@ }, "require-dev": { "doctrine/doctrine-fixtures-bundle": "^4.0", - "infection/infection": "^0.29.6", "lubiana/code-quality": "^1.7.2", "phpunit/phpunit": "^9.6.20", "symfony/browser-kit": "7.2.*", @@ -42,8 +41,7 @@ "php-http/discovery": true, "symfony/flex": true, "symfony/runtime": true, - "dealerdirect/phpcodesniffer-composer-installer": true, - "infection/extension-installer": true + "dealerdirect/phpcodesniffer-composer-installer": true }, "sort-packages": true, "platform": { @@ -68,7 +66,8 @@ "symfony/polyfill-php74": "*", "symfony/polyfill-php80": "*", "symfony/polyfill-php81": "*", - "symfony/polyfill-php82": "*" + "symfony/polyfill-php82": "*", + "symfony/polyfill-php83": "*" }, "scripts": { "auto-scripts": { @@ -86,8 +85,7 @@ "rector", "ecs --fix || ecs --fix" ], - "test": "bin/phpunit", - "mutation": "infection --threads=8 --show-mutations" + "test": "bin/phpunit" }, "conflict": { "symfony/symfony": "*" diff --git a/composer.lock b/composer.lock index 487eb49..2d25c27 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": "3d1e7e69822d02e0e2d6e84a5ff14087", + "content-hash": "9914f982d0228ab4735cb683fe2cb7e4", "packages": [ { "name": "doctrine/cache", @@ -3465,82 +3465,6 @@ ], "time": "2024-09-09T11:45:10+00:00" }, - { - "name": "symfony/polyfill-php83", - "version": "v1.31.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/2fb86d65e2d424369ad2905e83b236a8805ba491", - "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php83\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "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": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php83/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/polyfill-uuid", "version": "v1.31.0", @@ -5156,239 +5080,6 @@ } ], "packages-dev": [ - { - "name": "colinodell/json5", - "version": "v3.0.0", - "source": { - "type": "git", - "url": "https://github.com/colinodell/json5.git", - "reference": "5724d21bc5c910c2560af1b8915f0cc0163579c8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/colinodell/json5/zipball/5724d21bc5c910c2560af1b8915f0cc0163579c8", - "reference": "5724d21bc5c910c2560af1b8915f0cc0163579c8", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-mbstring": "*", - "php": "^8.0" - }, - "require-dev": { - "mikehaertl/php-shellcommand": "^1.7.0", - "phpstan/phpstan": "^1.10.57", - "scrutinizer/ocular": "^1.9", - "squizlabs/php_codesniffer": "^3.8.1", - "symfony/finder": "^6.0|^7.0", - "symfony/phpunit-bridge": "^7.0.3" - }, - "bin": [ - "bin/json5" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, - "autoload": { - "files": [ - "src/global.php" - ], - "psr-4": { - "ColinODell\\Json5\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Colin O'Dell", - "email": "colinodell@gmail.com", - "homepage": "https://www.colinodell.com", - "role": "Developer" - } - ], - "description": "UTF-8 compatible JSON5 parser for PHP", - "homepage": "https://github.com/colinodell/json5", - "keywords": [ - "JSON5", - "json", - "json5_decode", - "json_decode" - ], - "support": { - "issues": "https://github.com/colinodell/json5/issues", - "source": "https://github.com/colinodell/json5/tree/v3.0.0" - }, - "funding": [ - { - "url": "https://www.colinodell.com/sponsor", - "type": "custom" - }, - { - "url": "https://www.paypal.me/colinpodell/10.00", - "type": "custom" - }, - { - "url": "https://github.com/colinodell", - "type": "github" - }, - { - "url": "https://www.patreon.com/colinodell", - "type": "patreon" - } - ], - "time": "2024-02-09T13:06:12+00:00" - }, - { - "name": "composer/pcre", - "version": "3.3.2", - "source": { - "type": "git", - "url": "https://github.com/composer/pcre.git", - "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", - "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", - "shasum": "" - }, - "require": { - "php": "^7.4 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<1.11.10" - }, - "require-dev": { - "phpstan/phpstan": "^1.12 || ^2", - "phpstan/phpstan-strict-rules": "^1 || ^2", - "phpunit/phpunit": "^8 || ^9" - }, - "type": "library", - "extra": { - "phpstan": { - "includes": [ - "extension.neon" - ] - }, - "branch-alias": { - "dev-main": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Pcre\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "PCRE wrapping library that offers type-safe preg_* replacements.", - "keywords": [ - "PCRE", - "preg", - "regex", - "regular expression" - ], - "support": { - "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.3.2" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2024-11-12T16:29:46+00:00" - }, - { - "name": "composer/xdebug-handler", - "version": "3.0.5", - "source": { - "type": "git", - "url": "https://github.com/composer/xdebug-handler.git", - "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", - "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", - "shasum": "" - }, - "require": { - "composer/pcre": "^1 || ^2 || ^3", - "php": "^7.2.5 || ^8.0", - "psr/log": "^1 || ^2 || ^3" - }, - "require-dev": { - "phpstan/phpstan": "^1.0", - "phpstan/phpstan-strict-rules": "^1.1", - "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Composer\\XdebugHandler\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "John Stevenson", - "email": "john-stevenson@blueyonder.co.uk" - } - ], - "description": "Restarts a process without Xdebug.", - "keywords": [ - "Xdebug", - "performance" - ], - "support": { - "irc": "ircs://irc.libera.chat:6697/composer", - "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2024-05-06T16:37:16+00:00" - }, { "name": "dealerdirect/phpcodesniffer-composer-installer", "version": "v1.0.0", @@ -5636,490 +5327,6 @@ ], "time": "2024-12-05T18:35:55+00:00" }, - { - "name": "fidry/cpu-core-counter", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "8520451a140d3f46ac33042715115e290cf5785f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", - "reference": "8520451a140d3f46ac33042715115e290cf5785f", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "fidry/makefile": "^0.2.0", - "fidry/php-cs-fixer-config": "^1.1.2", - "phpstan/extension-installer": "^1.2.0", - "phpstan/phpstan": "^1.9.2", - "phpstan/phpstan-deprecation-rules": "^1.0.0", - "phpstan/phpstan-phpunit": "^1.2.2", - "phpstan/phpstan-strict-rules": "^1.4.4", - "phpunit/phpunit": "^8.5.31 || ^9.5.26", - "webmozarts/strict-phpunit": "^7.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Fidry\\CpuCoreCounter\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Théo FIDRY", - "email": "theo.fidry@gmail.com" - } - ], - "description": "Tiny utility to get the number of CPU cores.", - "keywords": [ - "CPU", - "core" - ], - "support": { - "issues": "https://github.com/theofidry/cpu-core-counter/issues", - "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0" - }, - "funding": [ - { - "url": "https://github.com/theofidry", - "type": "github" - } - ], - "time": "2024-08-06T10:04:20+00:00" - }, - { - "name": "infection/abstract-testframework-adapter", - "version": "0.5.0", - "source": { - "type": "git", - "url": "https://github.com/infection/abstract-testframework-adapter.git", - "reference": "18925e20d15d1a5995bb85c9dc09e8751e1e069b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/infection/abstract-testframework-adapter/zipball/18925e20d15d1a5995bb85c9dc09e8751e1e069b", - "reference": "18925e20d15d1a5995bb85c9dc09e8751e1e069b", - "shasum": "" - }, - "require": { - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "ergebnis/composer-normalize": "^2.8", - "friendsofphp/php-cs-fixer": "^2.17", - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Infection\\AbstractTestFramework\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Maks Rafalko", - "email": "maks.rafalko@gmail.com" - } - ], - "description": "Abstract Test Framework Adapter for Infection", - "support": { - "issues": "https://github.com/infection/abstract-testframework-adapter/issues", - "source": "https://github.com/infection/abstract-testframework-adapter/tree/0.5.0" - }, - "funding": [ - { - "url": "https://github.com/infection", - "type": "github" - }, - { - "url": "https://opencollective.com/infection", - "type": "open_collective" - } - ], - "time": "2021-08-17T18:49:12+00:00" - }, - { - "name": "infection/extension-installer", - "version": "0.1.2", - "source": { - "type": "git", - "url": "https://github.com/infection/extension-installer.git", - "reference": "9b351d2910b9a23ab4815542e93d541e0ca0cdcf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/infection/extension-installer/zipball/9b351d2910b9a23ab4815542e93d541e0ca0cdcf", - "reference": "9b351d2910b9a23ab4815542e93d541e0ca0cdcf", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.1 || ^2.0" - }, - "require-dev": { - "composer/composer": "^1.9 || ^2.0", - "friendsofphp/php-cs-fixer": "^2.18, <2.19", - "infection/infection": "^0.15.2", - "php-coveralls/php-coveralls": "^2.4", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^0.12.10", - "phpstan/phpstan-phpunit": "^0.12.6", - "phpstan/phpstan-strict-rules": "^0.12.2", - "phpstan/phpstan-webmozart-assert": "^0.12.2", - "phpunit/phpunit": "^9.5", - "vimeo/psalm": "^4.8" - }, - "type": "composer-plugin", - "extra": { - "class": "Infection\\ExtensionInstaller\\Plugin" - }, - "autoload": { - "psr-4": { - "Infection\\ExtensionInstaller\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Maks Rafalko", - "email": "maks.rafalko@gmail.com" - } - ], - "description": "Infection Extension Installer", - "support": { - "issues": "https://github.com/infection/extension-installer/issues", - "source": "https://github.com/infection/extension-installer/tree/0.1.2" - }, - "funding": [ - { - "url": "https://github.com/infection", - "type": "github" - }, - { - "url": "https://opencollective.com/infection", - "type": "open_collective" - } - ], - "time": "2021-10-20T22:08:34+00:00" - }, - { - "name": "infection/include-interceptor", - "version": "0.2.5", - "source": { - "type": "git", - "url": "https://github.com/infection/include-interceptor.git", - "reference": "0cc76d95a79d9832d74e74492b0a30139904bdf7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/infection/include-interceptor/zipball/0cc76d95a79d9832d74e74492b0a30139904bdf7", - "reference": "0cc76d95a79d9832d74e74492b0a30139904bdf7", - "shasum": "" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.16", - "infection/infection": "^0.15.0", - "phan/phan": "^2.4 || ^3", - "php-coveralls/php-coveralls": "^2.2", - "phpstan/phpstan": "^0.12.8", - "phpunit/phpunit": "^8.5", - "vimeo/psalm": "^3.8" - }, - "type": "library", - "autoload": { - "psr-4": { - "Infection\\StreamWrapper\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Maks Rafalko", - "email": "maks.rafalko@gmail.com" - } - ], - "description": "Stream Wrapper: Include Interceptor. Allows to replace included (autoloaded) file with another one.", - "support": { - "issues": "https://github.com/infection/include-interceptor/issues", - "source": "https://github.com/infection/include-interceptor/tree/0.2.5" - }, - "funding": [ - { - "url": "https://github.com/infection", - "type": "github" - }, - { - "url": "https://opencollective.com/infection", - "type": "open_collective" - } - ], - "time": "2021-08-09T10:03:57+00:00" - }, - { - "name": "infection/infection", - "version": "0.29.10", - "source": { - "type": "git", - "url": "https://github.com/infection/infection.git", - "reference": "cac7d20e5d286a37488527e477f5a695a9d7a44c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/infection/infection/zipball/cac7d20e5d286a37488527e477f5a695a9d7a44c", - "reference": "cac7d20e5d286a37488527e477f5a695a9d7a44c", - "shasum": "" - }, - "require": { - "colinodell/json5": "^2.2 || ^3.0", - "composer-runtime-api": "^2.0", - "composer/xdebug-handler": "^2.0 || ^3.0", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "fidry/cpu-core-counter": "^0.4.0 || ^0.5.0 || ^1.0", - "infection/abstract-testframework-adapter": "^0.5.0", - "infection/extension-installer": "^0.1.0", - "infection/include-interceptor": "^0.2.5", - "infection/mutator": "^0.4", - "justinrainbow/json-schema": "^5.3", - "nikic/php-parser": "^5.3", - "ondram/ci-detector": "^4.1.0", - "php": "^8.2", - "sanmai/later": "^0.1.1", - "sanmai/pipeline": "^5.1 || ^6", - "sebastian/diff": "^3.0.2 || ^4.0 || ^5.0 || ^6.0", - "shish/safe": "^2.6", - "symfony/console": "^5.4 || ^6.0 || ^7.0", - "symfony/filesystem": "^5.4 || ^6.0 || ^7.0", - "symfony/finder": "^5.4 || ^6.0 || ^7.0", - "symfony/process": "^5.4 || ^6.0 || ^7.0", - "webmozart/assert": "^1.11" - }, - "conflict": { - "antecedent/patchwork": "<2.1.25", - "dg/bypass-finals": "<1.4.1", - "phpunit/php-code-coverage": ">9,<9.1.4 || >9.2.17,<9.2.21" - }, - "require-dev": { - "ext-simplexml": "*", - "fidry/makefile": "^1.0", - "helmich/phpunit-json-assert": "^3.0", - "phpstan/extension-installer": "^1.1.0", - "phpstan/phpstan": "^1.10.15", - "phpstan/phpstan-phpunit": "^1.0.0", - "phpstan/phpstan-strict-rules": "^1.1.0", - "phpstan/phpstan-webmozart-assert": "^1.0.2", - "phpunit/phpunit": "^10.5", - "rector/rector": "^1.0", - "sidz/phpstan-rules": "^0.4", - "symfony/yaml": "^5.4 || ^6.0 || ^7.0" - }, - "bin": [ - "bin/infection" - ], - "type": "library", - "autoload": { - "psr-4": { - "Infection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Maks Rafalko", - "email": "maks.rafalko@gmail.com", - "homepage": "https://twitter.com/maks_rafalko" - }, - { - "name": "Oleg Zhulnev", - "homepage": "https://github.com/sidz" - }, - { - "name": "Gert de Pagter", - "homepage": "https://github.com/BackEndTea" - }, - { - "name": "Théo FIDRY", - "email": "theo.fidry@gmail.com", - "homepage": "https://twitter.com/tfidry" - }, - { - "name": "Alexey Kopytko", - "email": "alexey@kopytko.com", - "homepage": "https://www.alexeykopytko.com" - }, - { - "name": "Andreas Möller", - "email": "am@localheinz.com", - "homepage": "https://localheinz.com" - } - ], - "description": "Infection is a Mutation Testing framework for PHP. The mutation adequacy score can be used to measure the effectiveness of a test set in terms of its ability to detect faults.", - "keywords": [ - "coverage", - "mutant", - "mutation framework", - "mutation testing", - "testing", - "unit testing" - ], - "support": { - "issues": "https://github.com/infection/infection/issues", - "source": "https://github.com/infection/infection/tree/0.29.10" - }, - "funding": [ - { - "url": "https://github.com/infection", - "type": "github" - }, - { - "url": "https://opencollective.com/infection", - "type": "open_collective" - } - ], - "time": "2024-12-17T19:11:10+00:00" - }, - { - "name": "infection/mutator", - "version": "0.4.0", - "source": { - "type": "git", - "url": "https://github.com/infection/mutator.git", - "reference": "51d6d01a2357102030aee9d603063c4bad86b144" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/infection/mutator/zipball/51d6d01a2357102030aee9d603063c4bad86b144", - "reference": "51d6d01a2357102030aee9d603063c4bad86b144", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^5.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.6 || ^10" - }, - "type": "library", - "autoload": { - "psr-4": { - "Infection\\Mutator\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Maks Rafalko", - "email": "maks.rafalko@gmail.com" - } - ], - "description": "Mutator interface to implement custom mutators (mutation operators) for Infection", - "support": { - "issues": "https://github.com/infection/mutator/issues", - "source": "https://github.com/infection/mutator/tree/0.4.0" - }, - "funding": [ - { - "url": "https://github.com/infection", - "type": "github" - }, - { - "url": "https://opencollective.com/infection", - "type": "open_collective" - } - ], - "time": "2024-05-14T22:39:59+00:00" - }, - { - "name": "justinrainbow/json-schema", - "version": "5.3.0", - "source": { - "type": "git", - "url": "https://github.com/jsonrainbow/json-schema.git", - "reference": "feb2ca6dd1cebdaf1ed60a4c8de2e53ce11c4fd8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/feb2ca6dd1cebdaf1ed60a4c8de2e53ce11c4fd8", - "reference": "feb2ca6dd1cebdaf1ed60a4c8de2e53ce11c4fd8", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", - "json-schema/json-schema-test-suite": "1.2.0", - "phpunit/phpunit": "^4.8.35" - }, - "bin": [ - "bin/validate-json" - ], - "type": "library", - "autoload": { - "psr-4": { - "JsonSchema\\": "src/JsonSchema/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bruno Prieto Reis", - "email": "bruno.p.reis@gmail.com" - }, - { - "name": "Justin Rainbow", - "email": "justin.rainbow@gmail.com" - }, - { - "name": "Igor Wiedler", - "email": "igor@wiedler.ch" - }, - { - "name": "Robert Schönthal", - "email": "seroscho@googlemail.com" - } - ], - "description": "A library to validate a json schema.", - "homepage": "https://github.com/justinrainbow/json-schema", - "keywords": [ - "json", - "schema" - ], - "support": { - "issues": "https://github.com/jsonrainbow/json-schema/issues", - "source": "https://github.com/jsonrainbow/json-schema/tree/5.3.0" - }, - "time": "2024-07-06T21:00:26+00:00" - }, { "name": "lubiana/code-quality", "version": "1.7.2", @@ -6334,84 +5541,6 @@ }, "time": "2024-12-30T11:07:19+00:00" }, - { - "name": "ondram/ci-detector", - "version": "4.2.0", - "source": { - "type": "git", - "url": "https://github.com/OndraM/ci-detector.git", - "reference": "8b0223b5ed235fd377c75fdd1bfcad05c0f168b8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/OndraM/ci-detector/zipball/8b0223b5ed235fd377c75fdd1bfcad05c0f168b8", - "reference": "8b0223b5ed235fd377c75fdd1bfcad05c0f168b8", - "shasum": "" - }, - "require": { - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "ergebnis/composer-normalize": "^2.13.2", - "lmc/coding-standard": "^3.0.0", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.1.0", - "phpstan/phpstan": "^1.2.0", - "phpstan/phpstan-phpunit": "^1.0.0", - "phpunit/phpunit": "^9.6.13" - }, - "type": "library", - "autoload": { - "psr-4": { - "OndraM\\CiDetector\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ondřej Machulda", - "email": "ondrej.machulda@gmail.com" - } - ], - "description": "Detect continuous integration environment and provide unified access to properties of current build", - "keywords": [ - "CircleCI", - "Codeship", - "Wercker", - "adapter", - "appveyor", - "aws", - "aws codebuild", - "azure", - "azure devops", - "azure pipelines", - "bamboo", - "bitbucket", - "buddy", - "ci-info", - "codebuild", - "continuous integration", - "continuousphp", - "devops", - "drone", - "github", - "gitlab", - "interface", - "jenkins", - "pipelines", - "sourcehut", - "teamcity", - "travis" - ], - "support": { - "issues": "https://github.com/OndraM/ci-detector/issues", - "source": "https://github.com/OndraM/ci-detector/tree/4.2.0" - }, - "time": "2024-03-12T13:22:30+00:00" - }, { "name": "phar-io/manifest", "version": "2.0.4", @@ -7116,135 +6245,6 @@ ], "time": "2024-11-08T13:59:10+00:00" }, - { - "name": "sanmai/later", - "version": "0.1.4", - "source": { - "type": "git", - "url": "https://github.com/sanmai/later.git", - "reference": "e24c4304a4b1349c2a83151a692cec0c10579f60" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sanmai/later/zipball/e24c4304a4b1349c2a83151a692cec0c10579f60", - "reference": "e24c4304a4b1349c2a83151a692cec0c10579f60", - "shasum": "" - }, - "require": { - "php": ">=7.4" - }, - "require-dev": { - "ergebnis/composer-normalize": "^2.8", - "friendsofphp/php-cs-fixer": "^3.35.1", - "infection/infection": ">=0.27.6", - "phan/phan": ">=2", - "php-coveralls/php-coveralls": "^2.0", - "phpstan/phpstan": ">=1.4.5", - "phpunit/phpunit": ">=9.5 <10", - "vimeo/psalm": ">=2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "0.1.x-dev" - } - }, - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "Later\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Alexey Kopytko", - "email": "alexey@kopytko.com" - } - ], - "description": "Later: deferred wrapper object", - "support": { - "issues": "https://github.com/sanmai/later/issues", - "source": "https://github.com/sanmai/later/tree/0.1.4" - }, - "funding": [ - { - "url": "https://github.com/sanmai", - "type": "github" - } - ], - "time": "2023-10-24T00:25:28+00:00" - }, - { - "name": "sanmai/pipeline", - "version": "6.12", - "source": { - "type": "git", - "url": "https://github.com/sanmai/pipeline.git", - "reference": "ad7dbc3f773eeafb90d5459522fbd8f188532e25" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sanmai/pipeline/zipball/ad7dbc3f773eeafb90d5459522fbd8f188532e25", - "reference": "ad7dbc3f773eeafb90d5459522fbd8f188532e25", - "shasum": "" - }, - "require": { - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "ergebnis/composer-normalize": "^2.8", - "friendsofphp/php-cs-fixer": "^3.17", - "infection/infection": ">=0.10.5", - "league/pipeline": "^0.3 || ^1.0", - "phan/phan": ">=1.1", - "php-coveralls/php-coveralls": "^2.4.1", - "phpstan/phpstan": ">=0.10", - "phpunit/phpunit": ">=9.4", - "vimeo/psalm": ">=2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "v6.x-dev" - } - }, - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "Pipeline\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Alexey Kopytko", - "email": "alexey@kopytko.com" - } - ], - "description": "General-purpose collections pipeline", - "support": { - "issues": "https://github.com/sanmai/pipeline/issues", - "source": "https://github.com/sanmai/pipeline/tree/6.12" - }, - "funding": [ - { - "url": "https://github.com/sanmai", - "type": "github" - } - ], - "time": "2024-10-17T02:22:57+00:00" - }, { "name": "sebastian/cli-parser", "version": "1.0.2", @@ -8208,157 +7208,6 @@ ], "time": "2020-09-28T06:39:44+00:00" }, - { - "name": "shish/safe", - "version": "v2.6.4", - "source": { - "type": "git", - "url": "https://github.com/shish/safe.git", - "reference": "482e6227330a70b21c1c9e9301cc99b5658ccb89" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/shish/safe/zipball/482e6227330a70b21c1c9e9301cc99b5658ccb89", - "reference": "482e6227330a70b21c1c9e9301cc99b5658ccb89", - "shasum": "" - }, - "require": { - "php": ">= 8.2" - }, - "replace": { - "thecodingmachine/safe": "2.5.0" - }, - "require-dev": { - "phpstan/phpstan": "^1", - "phpunit/phpunit": "^10.0 || ^11.0", - "squizlabs/php_codesniffer": "^3", - "thecodingmachine/phpstan-strict-rules": "^1.0" - }, - "type": "library", - "autoload": { - "files": [ - "deprecated/apc.php", - "deprecated/array.php", - "deprecated/datetime.php", - "deprecated/libevent.php", - "deprecated/misc.php", - "deprecated/password.php", - "deprecated/mssql.php", - "deprecated/stats.php", - "deprecated/strings.php", - "lib/special_cases.php", - "deprecated/mysqli.php", - "generated/apache.php", - "generated/apcu.php", - "generated/array.php", - "generated/bzip2.php", - "generated/calendar.php", - "generated/classobj.php", - "generated/com.php", - "generated/cubrid.php", - "generated/curl.php", - "generated/datetime.php", - "generated/dir.php", - "generated/eio.php", - "generated/errorfunc.php", - "generated/exec.php", - "generated/fileinfo.php", - "generated/filesystem.php", - "generated/filter.php", - "generated/fpm.php", - "generated/ftp.php", - "generated/funchand.php", - "generated/gettext.php", - "generated/gnupg.php", - "generated/hash.php", - "generated/ibase.php", - "generated/ibmDb2.php", - "generated/iconv.php", - "generated/image.php", - "generated/imap.php", - "generated/info.php", - "generated/inotify.php", - "generated/json.php", - "generated/ldap.php", - "generated/libxml.php", - "generated/lzf.php", - "generated/mailparse.php", - "generated/mbstring.php", - "generated/misc.php", - "generated/mysql.php", - "generated/network.php", - "generated/oci8.php", - "generated/opcache.php", - "generated/openssl.php", - "generated/outcontrol.php", - "generated/pcntl.php", - "generated/pcre.php", - "generated/pgsql.php", - "generated/posix.php", - "generated/ps.php", - "generated/pspell.php", - "generated/readline.php", - "generated/rnp.php", - "generated/rpminfo.php", - "generated/rrd.php", - "generated/sem.php", - "generated/session.php", - "generated/shmop.php", - "generated/sockets.php", - "generated/sodium.php", - "generated/solr.php", - "generated/spl.php", - "generated/sqlsrv.php", - "generated/ssdeep.php", - "generated/ssh2.php", - "generated/stream.php", - "generated/strings.php", - "generated/swoole.php", - "generated/uodbc.php", - "generated/uopz.php", - "generated/url.php", - "generated/var.php", - "generated/xdiff.php", - "generated/xml.php", - "generated/xmlrpc.php", - "generated/yaml.php", - "generated/yaz.php", - "generated/zip.php", - "generated/zlib.php" - ], - "classmap": [ - "lib/DateTime.php", - "lib/DateTimeImmutable.php", - "lib/Exceptions/", - "deprecated/Exceptions/", - "generated/Exceptions/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHP core functions that throw exceptions instead of returning FALSE on error (a less-abandoned fork of thecodingmachine/safe)", - "support": { - "issues": "https://github.com/shish/safe/issues", - "source": "https://github.com/shish/safe/tree/v2.6.4" - }, - "funding": [ - { - "url": "https://github.com/OskarStark", - "type": "github" - }, - { - "url": "https://github.com/shish", - "type": "github" - }, - { - "url": "https://github.com/staabm", - "type": "github" - } - ], - "time": "2024-12-18T13:36:07+00:00" - }, { "name": "slevomat/coding-standard", "version": "8.15.0", @@ -9177,64 +8026,6 @@ } ], "time": "2024-03-03T12:36:25+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" } ], "aliases": [], diff --git a/src/Form/MenuItemType.php b/src/Form/MenuItemType.php index 641cf1b..e3c07f8 100644 --- a/src/Form/MenuItemType.php +++ b/src/Form/MenuItemType.php @@ -4,6 +4,7 @@ namespace App\Form; use App\Entity\MenuItem; use App\Repository\MenuItemRepository; +use Doctrine\DBAL\Types\TextType; use Doctrine\ORM\QueryBuilder; use Override; use Symfony\Bridge\Doctrine\Form\Type\EntityType; @@ -13,6 +14,7 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Uid\Ulid; +use Symfony\Component\Validator\Constraints\NotBlank; use function array_map; use function assert; @@ -22,41 +24,21 @@ final class MenuItemType extends AbstractType public function buildForm(FormBuilderInterface $builder, array $options): void { $item = $options['data']; - assert($item instanceof MenuItem); // Ensure it's of the correct type - $vendorId = $item->getFoodVendor()?->getId(); // Use safe navigation operator in case FoodVendor is null + assert($item instanceof MenuItem); - $builder->add('name'); // Basic field + $builder->add('name', \Symfony\Component\Form\Extension\Core\Type\TextType::class, [ + 'constraints' => [ + new NotBlank(), + new \Symfony\Component\Validator\Constraints\Length(['min' => 3]), + ] + ]); $builder->add('aliases', EntityType::class, [ 'class' => MenuItem::class, 'choice_label' => 'name', 'multiple' => true, 'expanded' => true, - 'query_builder' => static function (MenuItemRepository $repository) use ($item, $vendorId): QueryBuilder { - - $ids = $repository->createQueryBuilder('m') - ->select('DISTINCT IDENTITY(m.aliasOf)') - ->where('m.deletedAt IS NULL') - ->andWhere('m.aliasOf IS NOT NULL') - ->getquery(); - $ids = $ids->getScalarResult(); - $ids = array_map(static fn(array $id): Ulid => Ulid::fromBinary($id[1]), $ids); - - // Build the main query with a NOT EXISTS constraint - $qb = $repository->createQueryBuilder('m'); - $qb - ->where('m.foodVendor = :vendorId') - ->andWhere('m.deletedAt IS NULL') - ->andWhere('m.id != :id'); - foreach ($ids as $key => $id) { - $qb->andWhere("m.id != :idBy{$key}"); - $qb->setParameter("idBy{$key}", $id, UlidType::NAME); - } - $qb - ->orderBy('m.name', 'ASC') - ->setParameter('vendorId', $vendorId, UlidType::NAME) // ULID or appropriate type - ->setParameter('id', $item->getId()); // ULID or appropriate type - return $qb; - }, + 'query_builder' => static fn (MenuItemRepository $repository): QueryBuilder + => $repository->getSuitableAliasQueryBuilder($item), ]); } diff --git a/src/Repository/MenuItemRepository.php b/src/Repository/MenuItemRepository.php index dbcb4f2..51ca1a3 100644 --- a/src/Repository/MenuItemRepository.php +++ b/src/Repository/MenuItemRepository.php @@ -5,6 +5,8 @@ namespace App\Repository; use App\Entity\MenuItem; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Persistence\ManagerRegistry; +use Symfony\Bridge\Doctrine\Types\UlidType; +use Symfony\Component\Uid\Ulid; /** * @extends ServiceEntityRepository @@ -16,28 +18,29 @@ final class MenuItemRepository extends ServiceEntityRepository parent::__construct($registry, MenuItem::class); } - // /** - // * @return MenuItem[] Returns an array of MenuItem objects - // */ - // public function findByExampleField($value): array - // { - // return $this->createQueryBuilder('m') - // ->andWhere('m.exampleField = :val') - // ->setParameter('val', $value) - // ->orderBy('m.id', 'ASC') - // ->setMaxResults(10) - // ->getQuery() - // ->getResult() - // ; - // } + public function getSuitableAliasQueryBuilder(MenuItem $menuItem): \Doctrine\ORM\QueryBuilder + { + $ids = $this->createQueryBuilder('m') + ->select('DISTINCT IDENTITY(m.aliasOf)') + ->where('m.deletedAt IS NULL') + ->andWhere('m.aliasOf IS NOT NULL') + ->getquery(); + $ids = $ids->getScalarResult(); + $ids = array_map(static fn(array $id): Ulid => Ulid::fromBinary($id[1]), $ids); - // public function findOneBySomeField($value): ?MenuItem - // { - // return $this->createQueryBuilder('m') - // ->andWhere('m.exampleField = :val') - // ->setParameter('val', $value) - // ->getQuery() - // ->getOneOrNullResult() - // ; - // } + $qb = $this->createQueryBuilder('m'); + $qb + ->where('m.foodVendor = :vendorId') + ->andWhere('m.deletedAt IS NULL') + ->andWhere('m.id != :id'); + foreach ($ids as $key => $id) { + $qb->andWhere("m.id != :idBy{$key}") + ->setParameter("idBy{$key}", $id, UlidType::NAME); + } + $qb + ->orderBy('m.name', 'ASC') + ->setParameter('vendorId', $menuItem->getFoodVendor()->getId(), UlidType::NAME) + ->setParameter('id', $menuItem->getId(), UlidType::NAME); + return $qb; + } } diff --git a/templates/menu_item/show.html.twig b/templates/menu_item/show.html.twig index 80731e6..a07b969 100644 --- a/templates/menu_item/show.html.twig +++ b/templates/menu_item/show.html.twig @@ -21,6 +21,18 @@ {{ menu_item.aliasOf.name }} {% endif %} + {% if(menu_item.aliases|length > 0) %} + + Aliases + + + + + {% endif %} diff --git a/tests/Controller/FoodVendorControllerTest.php b/tests/Controller/FoodVendorControllerTest.php index 5e50526..d000d79 100644 --- a/tests/Controller/FoodVendorControllerTest.php +++ b/tests/Controller/FoodVendorControllerTest.php @@ -9,6 +9,7 @@ use Override; use function sprintf; + final class FoodVendorControllerTest extends DbWebTest { private string $path = '/food/vendor/'; @@ -23,15 +24,20 @@ final class FoodVendorControllerTest extends DbWebTest public function testNew(): void { + self::assertSame(0, $this->repository->count([])); $this->client->request('GET', sprintf('%snew', $this->path)); self::assertResponseStatusCodeSame(200); $this->client->submitForm('Save', [ - 'food_vendor[name]' => 'Testing', + 'food_vendor[name]' => 'TestingNew', ]); + $newVendor = $this->repository->findOneBy(['name' => 'TestingNew']); + $this->assertInstanceof(FoodVendor::class, $newVendor); self::assertSame(1, $this->repository->count([])); + + } public function testShow(): void diff --git a/tests/Controller/MenuItemControllerTest.php b/tests/Controller/MenuItemControllerTest.php index 4d6dd02..05b2a1e 100644 --- a/tests/Controller/MenuItemControllerTest.php +++ b/tests/Controller/MenuItemControllerTest.php @@ -14,6 +14,9 @@ final class MenuItemControllerTest extends DbWebTest { private string $path = '/menu/item/'; private FoodVendor $vendor; + private MenuItem $menuItem; + private MenuItem $aliasOne; + private MenuItem $aliasTwo; #[Override] public function setUp(): void @@ -23,7 +26,30 @@ final class MenuItemControllerTest extends DbWebTest $this->vendor->setName('Food Vendor'); $this->manager->persist($this->vendor); + + $this->menuItem = new MenuItem; + $this->menuItem->setName('Testing 1 2'); + + $this->vendor->addMenuItem($this->menuItem); + $this->manager->persist($this->vendor); + $this->manager->persist($this->menuItem); + + $this->aliasOne = new MenuItem; + $this->aliasOne->setName('AliasOne'); + $this->aliasOne->setFoodVendor($this->vendor); + $this->menuItem->addAlias($this->aliasOne); + + $this->aliasTwo = new MenuItem; + $this->aliasTwo->setName('AliasTwo'); + $this->aliasTwo->setFoodVendor($this->vendor); + $this->aliasTwo->setAliasOf($this->menuItem); + $this->menuItem->addAlias($this->aliasTwo); + + $this->manager->persist($this->aliasOne); + $this->manager->persist($this->aliasTwo); + $this->manager->persist($this->menuItem); $this->manager->flush(); + } #[Override] @@ -34,70 +60,82 @@ final class MenuItemControllerTest extends DbWebTest public function testShow(): void { - $menuItem = new MenuItem; - $menuItem->setName('Testing 1 2'); - $this->vendor->addMenuItem($menuItem); - $this->manager->persist($this->vendor); - $this->manager->persist($menuItem); - $this->manager->flush(); - - $crawler = $this->client->request('GET', "{$this->path}{$menuItem->getId()}"); + $crawler = $this->client->request('GET', "{$this->path}{$this->menuItem->getId()}"); $idValue = $crawler->filter( '.table > tbody:nth-child(1) > tr:nth-child(1) > td:nth-child(2)' )->text(); $nameValue = $crawler->filter( '.table > tbody:nth-child(1) > tr:nth-child(2) > td:nth-child(2)' )->text(); + + $aliasTwoNameValue = $crawler->filter( + '.table > tbody:nth-child(1) > tr:nth-child(3) > td:nth-child(2) > ul:nth-child(1) > li:nth-child(1)' + )->text(); + $aliasOneNameValue = $crawler->filter( + '.table > tbody:nth-child(1) > tr:nth-child(3) > td:nth-child(2) > ul:nth-child(1) > li:nth-child(2)' + )->text(); self::assertResponseStatusCodeSame(200); - $this->assertEquals($idValue, $menuItem->getId()); - $this->assertEquals($nameValue, $menuItem->getName()); + $this->assertEquals($idValue, $this->menuItem->getId()); + $this->assertEquals($nameValue, $this->menuItem->getName()); + $this->assertEquals($aliasTwoNameValue, $this->aliasOne->getName()); + $this->assertEquals($aliasOneNameValue, $this->aliasTwo->getName()); } public function testEdit(): void { - $menuItem = new MenuItem; - $menuItem->setName('Testing 1 2'); - - $this->vendor->addMenuItem($menuItem); - $this->manager->persist($this->vendor); - $this->manager->persist($menuItem); - $this->manager->flush(); - - $crawler = $this->client->request('GET', sprintf('%s%s/edit', $this->path, $menuItem->getId())); + $crawler = $this->client->request('GET', sprintf('%s%s/edit', $this->path, $this->menuItem->getId())); $nameElem = $crawler->filter('#menu_item_name'); $this->assertEquals( 'Testing 1 2', $nameElem->attr('value') ); - $this->client->submitForm('Update', [ - 'menu_item[name]' => 'Testing-1', - ]); + $form = $crawler->selectButton('Update')->form(); + $form['menu_item[name]'] = 'Testing-1'; + $form['menu_item[aliases]'][0]->untick(); - self::assertResponseRedirects(sprintf('/menu/item/%s', $menuItem->getId())); + $this->client->submit($form); + + self::assertResponseRedirects(sprintf('/menu/item/%s', $this->menuItem->getId())); + $menuItem = $this->repository->find($this->menuItem->getId()); + $this->assertEquals('Testing-1', $menuItem->getName()); + $this->assertEquals(1, $menuItem->getAliases()->count()); + $aliasOne = $this->repository->find($this->aliasOne->getId()); + $this->assertNull($aliasOne->getAliasOf()); } + public function testEditInvalid(): void + { + $crawler = $this->client->request('GET', sprintf('%s%s/edit', $this->path, $this->menuItem->getId())); + $nameElem = $crawler->filter('#menu_item_name'); + $this->assertEquals( + 'Testing 1 2', + $nameElem->attr('value') + ); + + $form = $crawler->selectButton('Update')->form(); + $form['menu_item[name]'] = 'a'; + + $this->client->submit($form); + + self::assertResponseStatusCodeSame(422); + } + + public function testDelete(): void { - $menuItem = new MenuItem; - $menuItem->setName('Testing 1 2'); - - $this->vendor->addMenuItem($menuItem); - $this->manager->persist($this->vendor); - $this->manager->persist($menuItem); - $order = new FoodOrder; $order->setFoodVendor($this->vendor); $this->manager->persist($order); $this->manager->flush(); - $this->assertFalse($menuItem->isDeleted()); + $this->assertFalse($this->menuItem->isDeleted()); - $this->client->request('GET', "{$this->path}{$menuItem->getId()}"); + $this->client->request('GET', "{$this->path}{$this->menuItem->getId()}"); $this->client->submitForm('Delete', []); - $menuItem = $this->repository->find($menuItem->getId()); + $menuItem = $this->repository->find($this->menuItem->getId()); $this->assertTrue($menuItem->isDeleted()); @@ -105,7 +143,7 @@ final class MenuItemControllerTest extends DbWebTest $count = $crawler->filter('body > main:nth-child(2) > div:nth-child(5)') ->children() ->count(); - $this->assertSame(0, $count); + $this->assertSame(2, $count); $this->assertResponseIsSuccessful(); diff --git a/tests/Entity/MenuItemTest.php b/tests/Entity/MenuItemTest.php index ccc34d6..a30de40 100644 --- a/tests/Entity/MenuItemTest.php +++ b/tests/Entity/MenuItemTest.php @@ -27,4 +27,29 @@ final class MenuItemTest extends TestCase $this->assertTrue($item->isDeleted()); $this->assertInstanceOf(DateTimeImmutable::class, $item->getDeletedAt()); } + + public function testMenuItemAlias(): void + { + $item = new MenuItem; + $item->setName('Test'); + $this->assertEquals('Test', $item->getName()); + + $vendor = new FoodVendor; + $vendor->setName('Test'); + $item->setFoodVendor($vendor); + + $item2 = new MenuItem; + $item2->setName('Test2'); + $item2->setFoodVendor($vendor); + + $item->addAlias($item2); + + $this->assertCount(1, $item->getAliases()); + $this->assertSame($item, $item2->getAliasOf()); + + $item->removeAlias($item2); + $this->assertCount(0, $item->getAliases()); + $this->assertNull($item2->getAliasOf()); + + } } From 7fb0614db41dff7f82c86a7cc58354cc0544a734 Mon Sep 17 00:00:00 2001 From: Continuous Integration Date: Sun, 26 Jan 2025 10:50:50 +0000 Subject: [PATCH 02/41] Continuous Integration Fixes --- src/Form/MenuItemType.php | 17 ++++++++--------- src/Repository/MenuItemRepository.php | 5 ++++- tests/Controller/FoodVendorControllerTest.php | 6 +++--- tests/Controller/MenuItemControllerTest.php | 9 ++++++--- tests/Entity/MenuItemTest.php | 1 + 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/Form/MenuItemType.php b/src/Form/MenuItemType.php index e3c07f8..0761e21 100644 --- a/src/Form/MenuItemType.php +++ b/src/Form/MenuItemType.php @@ -4,18 +4,15 @@ namespace App\Form; use App\Entity\MenuItem; use App\Repository\MenuItemRepository; -use Doctrine\DBAL\Types\TextType; use Doctrine\ORM\QueryBuilder; use Override; use Symfony\Bridge\Doctrine\Form\Type\EntityType; -use Symfony\Bridge\Doctrine\Types\UlidType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Uid\Ulid; - +use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\NotBlank; -use function array_map; + use function assert; final class MenuItemType extends AbstractType @@ -28,16 +25,18 @@ final class MenuItemType extends AbstractType $builder->add('name', \Symfony\Component\Form\Extension\Core\Type\TextType::class, [ 'constraints' => [ - new NotBlank(), - new \Symfony\Component\Validator\Constraints\Length(['min' => 3]), - ] + new NotBlank, + new Length([ + 'min' => 3, + ]), + ], ]); $builder->add('aliases', EntityType::class, [ 'class' => MenuItem::class, 'choice_label' => 'name', 'multiple' => true, 'expanded' => true, - 'query_builder' => static fn (MenuItemRepository $repository): QueryBuilder + 'query_builder' => static fn(MenuItemRepository $repository): QueryBuilder => $repository->getSuitableAliasQueryBuilder($item), ]); } diff --git a/src/Repository/MenuItemRepository.php b/src/Repository/MenuItemRepository.php index 51ca1a3..651a52b 100644 --- a/src/Repository/MenuItemRepository.php +++ b/src/Repository/MenuItemRepository.php @@ -7,6 +7,9 @@ use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Persistence\ManagerRegistry; use Symfony\Bridge\Doctrine\Types\UlidType; use Symfony\Component\Uid\Ulid; +use Doctrine\ORM\QueryBuilder; + +use function array_map; /** * @extends ServiceEntityRepository @@ -18,7 +21,7 @@ final class MenuItemRepository extends ServiceEntityRepository parent::__construct($registry, MenuItem::class); } - public function getSuitableAliasQueryBuilder(MenuItem $menuItem): \Doctrine\ORM\QueryBuilder + public function getSuitableAliasQueryBuilder(MenuItem $menuItem): QueryBuilder { $ids = $this->createQueryBuilder('m') ->select('DISTINCT IDENTITY(m.aliasOf)') diff --git a/tests/Controller/FoodVendorControllerTest.php b/tests/Controller/FoodVendorControllerTest.php index d000d79..d7fa1b9 100644 --- a/tests/Controller/FoodVendorControllerTest.php +++ b/tests/Controller/FoodVendorControllerTest.php @@ -9,7 +9,6 @@ use Override; use function sprintf; - final class FoodVendorControllerTest extends DbWebTest { private string $path = '/food/vendor/'; @@ -33,11 +32,12 @@ final class FoodVendorControllerTest extends DbWebTest 'food_vendor[name]' => 'TestingNew', ]); - $newVendor = $this->repository->findOneBy(['name' => 'TestingNew']); + $newVendor = $this->repository->findOneBy([ + 'name' => 'TestingNew', + ]); $this->assertInstanceof(FoodVendor::class, $newVendor); self::assertSame(1, $this->repository->count([])); - } public function testShow(): void diff --git a/tests/Controller/MenuItemControllerTest.php b/tests/Controller/MenuItemControllerTest.php index 05b2a1e..92cb1a0 100644 --- a/tests/Controller/MenuItemControllerTest.php +++ b/tests/Controller/MenuItemControllerTest.php @@ -37,12 +37,14 @@ final class MenuItemControllerTest extends DbWebTest $this->aliasOne = new MenuItem; $this->aliasOne->setName('AliasOne'); $this->aliasOne->setFoodVendor($this->vendor); + $this->menuItem->addAlias($this->aliasOne); $this->aliasTwo = new MenuItem; $this->aliasTwo->setName('AliasTwo'); $this->aliasTwo->setFoodVendor($this->vendor); $this->aliasTwo->setAliasOf($this->menuItem); + $this->menuItem->addAlias($this->aliasTwo); $this->manager->persist($this->aliasOne); @@ -91,7 +93,8 @@ final class MenuItemControllerTest extends DbWebTest $nameElem->attr('value') ); - $form = $crawler->selectButton('Update')->form(); + $form = $crawler->selectButton('Update') + ->form(); $form['menu_item[name]'] = 'Testing-1'; $form['menu_item[aliases]'][0]->untick(); @@ -114,7 +117,8 @@ final class MenuItemControllerTest extends DbWebTest $nameElem->attr('value') ); - $form = $crawler->selectButton('Update')->form(); + $form = $crawler->selectButton('Update') + ->form(); $form['menu_item[name]'] = 'a'; $this->client->submit($form); @@ -122,7 +126,6 @@ final class MenuItemControllerTest extends DbWebTest self::assertResponseStatusCodeSame(422); } - public function testDelete(): void { $order = new FoodOrder; diff --git a/tests/Entity/MenuItemTest.php b/tests/Entity/MenuItemTest.php index a30de40..a56c81d 100644 --- a/tests/Entity/MenuItemTest.php +++ b/tests/Entity/MenuItemTest.php @@ -36,6 +36,7 @@ final class MenuItemTest extends TestCase $vendor = new FoodVendor; $vendor->setName('Test'); + $item->setFoodVendor($vendor); $item2 = new MenuItem; From 82575d2da090d3b6b6277166b0f35d506037cb44 Mon Sep 17 00:00:00 2001 From: lubiana Date: Sun, 26 Jan 2025 11:54:42 +0100 Subject: [PATCH 03/41] add changes --- src/Form/MenuItemType.php | 3 ++- src/Repository/MenuItemRepository.php | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Form/MenuItemType.php b/src/Form/MenuItemType.php index 0761e21..6d94034 100644 --- a/src/Form/MenuItemType.php +++ b/src/Form/MenuItemType.php @@ -8,6 +8,7 @@ use Doctrine\ORM\QueryBuilder; use Override; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Validator\Constraints\Length; @@ -23,7 +24,7 @@ final class MenuItemType extends AbstractType $item = $options['data']; assert($item instanceof MenuItem); - $builder->add('name', \Symfony\Component\Form\Extension\Core\Type\TextType::class, [ + $builder->add('name', TextType::class, [ 'constraints' => [ new NotBlank, new Length([ diff --git a/src/Repository/MenuItemRepository.php b/src/Repository/MenuItemRepository.php index 651a52b..b207856 100644 --- a/src/Repository/MenuItemRepository.php +++ b/src/Repository/MenuItemRepository.php @@ -4,10 +4,10 @@ namespace App\Repository; use App\Entity\MenuItem; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; +use Doctrine\ORM\QueryBuilder; use Doctrine\Persistence\ManagerRegistry; use Symfony\Bridge\Doctrine\Types\UlidType; use Symfony\Component\Uid\Ulid; -use Doctrine\ORM\QueryBuilder; use function array_map; From 2273c91f2bc6905c837705663b368dbcbada8cb8 Mon Sep 17 00:00:00 2001 From: lubiana Date: Wed, 29 Jan 2025 19:50:33 +0100 Subject: [PATCH 04/41] danke --- public/static/css/fieber.css | 965 +++++++++++++++++++++++++++++++++++ templates/base.html.twig | 11 +- 2 files changed, 973 insertions(+), 3 deletions(-) create mode 100644 public/static/css/fieber.css diff --git a/public/static/css/fieber.css b/public/static/css/fieber.css new file mode 100644 index 0000000..d15789b --- /dev/null +++ b/public/static/css/fieber.css @@ -0,0 +1,965 @@ +/* SPDX-License-Identifier: MIT + SPDX-FileCopyrightText: Copyright (c) 2022-2025 zichy +*/ + +/* Custom properties +======================================== +*/ + +:root { + --f-sans: ui-sans-serif, sans-serif; + + --f-body: ui-serif; + --f-heading: var(--f-sans); + --f-form: var(--f-sans); + --f-code: ui-monospace; + + --f-size: clamp(1.6rem, 1.75vw, 2rem); + --f-size-small: 0.85em; + --f-size-large: 1.25em; + --f-line: 1.5; + + --c-gray: #666; + --c-red: #b30; + --c-yellow: #fe9; + + --i-triangle: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10"%3E%3Cpolygon fill="black" points="5 10 10 0 0 0"/%3E%3C/svg%3E'); + + --w-body: 80ch; +} + +/* Dark theme */ + +@media (prefers-color-scheme: dark) { + :root { + --c-gray: #999; + --c-red: #f99; + --c-yellow: #ff9; + + --i-triangle: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10"%3E%3Cpolygon fill="white" points="5 10 10 0 0 0"/%3E%3C/svg%3E'); + } +} + + +/* Globals +======================================== +*/ + +/* Box sizing */ + +*, +*::before, +*::after { + box-sizing: border-box; +} + +/* Text rendering */ + +* { + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; +} + +/* Interaction */ + +::selection { + background: Highlight; + color: HighlightText; + text-shadow: none; +} + +*:focus { + outline: 2px solid LinkText; + outline-offset: 0.25rem; +} + +/* Font size & Scrolling */ + +html { + font-size: 62.5%; + scroll-behavior: smooth; + scroll-padding-top: 2rem; +} + +/* Backdrop */ + +::backdrop { + background-color: rgba(255, 255, 255, 0.6); +} + +@media (prefers-color-scheme: dark) { + ::backdrop { + background-color: rgba(0, 0, 0, 0.6); + } +} + +/* Hidden elements */ + +[hidden] { + display: none; +} + +/* Print spacing */ + +@page { + margin: 15mm 20mm; +} + + +/* Body +======================================== +*/ + +/* Colors & Typography */ + +body { + background-color: Canvas; + color: CanvasText; + font-size: var(--f-size); + font-family: var(--f-body); + line-height: var(--f-line); +} + +/* Body sizing */ + +@media screen { + body { + max-width: var(--w-body, 100%); + min-width: 320px; + padding: 2rem; + margin: 0 auto; + overflow-x: hidden; + overflow-y: scroll; + } +} + +/* Print colors */ + +@media print { + body { + background-color: white; + color: black; + } +} + + +/* Links +======================================== +*/ + +a:any-link { + color: LinkText; + text-decoration: underline; + text-decoration-thickness: 0.125em; +} + +a:any-link:hover { + background-color: LinkText; + color: Canvas; + text-decoration-line: none; +} + +@media print { + a[href^="http"]::after { + content: ' ('attr(href)')'; + font-size: var(--f-size-small); + word-break: break-all; + } +} + + +/* Media +======================================== +*/ + +/* Reset */ + +:where(iframe, img, svg, canvas, audio, video) { + display: block; + max-width: 100%; +} + +@media print { + :where(audio, video) { + display: none; + } +} + +figure { + margin-inline: 0; + break-inside: avoid; +} + +/* Image */ + +img { + height: auto; + position: relative; +} + +img::before { + content: ''; + background-color: Highlight; + width: 100%; + height: 100%; + position: absolute; + top: 0; + left: 0; +} + +@media screen { + picture img { + width: 100%; + } +} + +/* Video */ + +video { + width: 100%; + height: auto; +} + +/* Iframe */ + +iframe { + border-style: none; +} + + +/* Headings +======================================== +*/ + +:where(h1, h2, h3, h4, h5, h6) { + font-family: var(--f-heading); + line-height: calc(var(--f-line) / 1.25); + hyphens: auto; +} + +:where(h3, h5) { + color: var(--c-gray); +} + +:where(h4, h5, h6) { + text-transform: uppercase; +} + +:where(h2, h3, h4, h5, h6):target { + background-color: var(--c-yellow); + color: MarkText; +} + + +/* Lists +======================================== +*/ + +:where(ul, ol) { + padding-inline-start: 1em; +} + +ul { + list-style-type: disc; +} + +li::marker { + color: var(--c-gray); +} + +li p { + margin: 0; +} + +/* Description */ + +dt { + font-style: italic; +} + +/* Navigation */ + +nav ul { + display: flex; + flex-wrap: wrap; + gap: 0.5rem 2rem; + list-style-type: none; + padding: 0; +} + +@media print { + nav { + display: none; + } +} + + +/* Inline elements +======================================== +*/ + +/* Bold text */ + +:where(b, strong) { + font-weight: bolder; +} + +/* Small text */ + +small { + font-size: var(--f-size-small); +} + +/* Mark */ + +mark { + background-color: var(--c-yellow); +} + +/* Abbreviation */ + +abbr[title] { + text-decoration-line: underline; + text-decoration-style: dotted; + cursor: help; +} + +a abbr[title] { + text-decoration: none; +} + +/* Subscript & Superscript */ + +:where(sub, sup) { + line-height: 0; +} + +/* Quote */ + +q { + font-style: italic; + quotes: none; +} + +/* Keyboard input */ + +kbd { + background: linear-gradient(0deg, Canvas 0%, ButtonFace 100%); + font-size: var(--f-size-small); + font-family: var(--f-sans); + font-weight: bold; + padding: 0.2em 0.4em; + border-radius: 0.5rem; + box-shadow: 1px 1px 1px 0px var(--c-gray); +} + + +/* Ruby annotation +======================================== +*/ + +rt { + color: var(--c-gray); + font-family: var(--f-sans); + letter-spacing: -0.05em; + padding: 0 0.25em; +} + + +/* Horizontal rule +======================================== +*/ + +hr { + height: 0; + margin: 2em 0; + border: 0; + border-top: 2px solid var(--c-gray); +} + + +/* Blockquote +======================================== +*/ + +blockquote { + font-size: var(--f-size-large); + font-style: italic; + line-height: calc(var(--f-line) / 1.25); + margin: 0; +} + +blockquote > *:first-child { + margin-block-start: 0; +} + +blockquote > *:last-child { + margin-block-end: 0; +} + + +/* Captions +======================================== +*/ + +:where(caption, figcaption) { + color: var(--c-gray); + font-family: var(--f-heading); + font-size: var(--f-size-small); + font-style: italic; + margin-block-start: 0.5rem; +} + +caption { + text-align: left; + caption-side: bottom; +} + +[dir='rtl' i] caption { + text-align: right; +} + + +/* Code +======================================== +*/ + +:where(pre, code, samp, var) { + background-color: ButtonFace; +} + +:where(code, samp, var) { + font-size: var(--f-size-small); + font-family: var(--f-code); + padding: 0.2em 0.4em; +} + +pre { + font-size: var(--f-size-small); + padding: 2rem; +} + +@media screen { + pre { + overflow-x: scroll; + } +} + +pre code { + background-color: transparent; + display: block; + white-space: pre-wrap; + padding: 0; +} + + +/* Details +======================================== +*/ + +details { + background-color: ButtonFace; + padding: 2rem; + margin: 1em 0; + border-radius: 0.5rem; +} + +details > *:nth-child(2) { + margin-block-start: 0; +} + +details > *:last-child { + margin-block-end: 0; +} + +summary { + color: LinkText; + font-family: var(--f-heading); + font-weight: bold; + cursor: pointer; +} + +summary:hover { + text-decoration: underline; +} + +details[open] summary { + margin-block-end: 2rem; +} + + +/* Aside +======================================== +*/ + +aside { + color: var(--c-gray); +} + +@media (min-width: 769px) { + aside { + font-size: var(--f-size-small); + float: right; + width: calc(var(--w-body) / 2.5); + padding-block-end: 2rem; + padding-inline-start: 4rem; + } + + aside > *:first-child { + margin-block-start: 0; + } + + aside > *:last-child { + margin-block-end: 0; + } +} + + +/* Table +======================================== +*/ + +table { + width: 100%; + margin: 1em 0; + border-collapse: collapse; + border-spacing: 0; + break-inside: avoid; +} + +@media screen and (max-width: 768px) { + table { + display: block; + overflow-x: auto; + overflow-y: hidden; + } +} + +thead { + border-bottom: 2px solid var(--c-gray); +} + +tbody tr:nth-child(odd) { + background-color: ButtonFace; +} + +tfoot { + border-top: 2px solid var(--c-gray); +} + +:where(th, td) { + padding: 0.5rem 1rem; +} + +@media (max-width: 768px) { + :where(th, td) { + min-width: 10rem; + } +} + +th { + font-family: var(--f-heading); + text-align: left; + vertical-align: bottom; +} + +[dir='rtl' i] th { + text-align: right; +} + + +/* Forms & Inputs +======================================== +*/ + +/* Reset */ + +:where(input, textarea, select, button, progress) { + -webkit-appearance: none; + background-color: transparent; + break-inside: avoid; +} + +:where(input, textarea, select, button) { + font-family: var(--f-form); + font-size: 1em; + border-radius: 0.5rem; +} + +:where(input:not([type='button' i]):not([type='submit' i]):not([type='reset' i]):not([type='checkbox' i]):not([type='radio' i]):not([type='image' i]), textarea, select) { + color: CanvasText; + font-size: var(--f-size-small); + display: block; + width: 100%; + padding: 0.75rem 1rem; + border: 2px solid LinkText; +} + +/* Placeholder */ + +::placeholder { + color: var(--c-gray); +} + +/* Fieldset */ + +fieldset { + padding: 2rem; + border: 2px solid LinkText; + border-radius: 0.5rem; + break-inside: avoid; +} + +/* Label & Legend */ + +:where(legend, label) { + font-family: var(--f-form); + font-weight: bold; + display: block; +} + +legend { + padding: 0 1rem; +} + +:where(legend, label) small { + color: var(--c-gray); + font-weight: normal; +} + +/* Textarea */ + +textarea { + resize: vertical; +} + +/* Checkbox & Radio input */ + +label:has([type='checkbox' i], [type='radio' i]) { + font-family: var(--f-form); + font-size: var(--f-size-small); + font-weight: normal; + display: grid; + grid-template-columns: 1.25em 1fr; + column-gap: 0.5em; + padding-block-end: 0; +} + +label:has([type='checkbox' i][disabled], [type='radio' i][disabled]) { + color: var(--c-gray); +} + +:where([type='checkbox' i], [type='radio' i]) { + width: 1.25em; + height: 1.25em; + position: relative; + margin: 0.2rem 0 0; + border: 2px solid LinkText; + cursor: pointer; +} + +[type='radio' i] { + border-radius: 50%; +} + +:where([type='checkbox' i], [type='radio' i]):checked { + background-color: LinkText; +} + +[type='checkbox' i]:checked::after { + content: '\2713'; + color: Canvas; + font-family: var(--f-form); + font-weight: bold; + line-height: 1; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +/* Color input */ + +[type='color' i] { + height: 4rem; + padding: 0.5rem; + cursor: pointer; +} + +::-webkit-color-swatch-wrapper { + padding: 0; +} + +::-webkit-color-swatch { + border: 0; +} + +::-moz-color-swatch { + border: 0; +} + +/* Range input */ + +[type='range' i] { + margin: 1.25rem 0 0; + padding: 0; + border: 0; +} + +[type='range' i]:focus { + outline: none; +} + +::-webkit-slider-runnable-track { + background-color: LinkText; + height: 4px; + border-radius: 0.5rem; +} + +[disabled]::-webkit-slider-runnable-track { + background-color: var(--c-gray); +} + +::-moz-range-track { + background-color: LinkText; + height: 4px; + border-radius: 0.5rem; +} + +[disabled]::-moz-range-track { + background-color: var(--c-gray); +} + +::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + background-color: Canvas; + height: 2rem; + width: 2rem; + margin-block-start: calc(-1rem + 2px); + border: 2px solid LinkText; + border-radius: 50%; + cursor: ew-resize; +} + +[disabled]::-webkit-slider-thumb { + border-color: var(--c-gray); +} + +[type='range' i]:focus::-webkit-slider-thumb { + outline: 2px solid LinkText; + outline-offset: 0.25rem; +} + +::-moz-range-thumb { + appearance: none; + background-color: Canvas; + height: 2rem; + width: 2rem; + margin-block-start: calc(-1rem + 2px); + border: 2px solid LinkText; + border-radius: 50%; + cursor: ew-resize; +} + +[disabled]::-moz-range-thumb { + border-color: var(--c-gray); +} + +[type='range' i]:focus::-moz-range-thumb { + outline: 2px solid LinkText; + outline-offset: 0.25rem; +} + +/* Select */ + +select { + background: Canvas var(--i-triangle) no-repeat calc(100% - 1rem) center / 1.5rem; + text-overflow: ellipsis; + white-space: nowrap; + padding-inline-end: 3.5rem; + overflow: hidden; + cursor: pointer; +} + +[dir='rtl' i] select { + background-position: 1rem center; + padding-inline: 3.5rem 1rem; +} + +select[multiple] { + background-image: none; + padding-inline-end: 1rem; +} + +/* Buttons */ + +:where(button, [type='button' i], [type='submit' i], [type='reset' i]) { + font-size: var(--f-size-small); + font-weight: bold; + text-align: center; + text-decoration: none; + line-height: 1; + display: inline-block; + min-width: 5rem; + padding: 0.2em 0.4em; + border: 2px solid LinkText; + -webkit-user-select: text; + user-select: text; + cursor: pointer; + touch-action: manipulation; +} + +:where(button:not([disabled]), [type='button' i]:not([disabled]), [type='submit' i]:not([disabled]), [type='reset' i]:not([disabled])):hover { + text-decoration: underline; +} + +@media screen { + :where(button, [type='button' i], [type='submit' i], [type='reset' i]) { + background-color: LinkText; + color: Canvas; + } + + :where(button[disabled], [type='button' i][disabled], [type='submit' i][disabled], [type='reset' i][disabled]) { + background-color: var(--c-gray); + color: currentColor; + } +} + +form :where(button, [type='button' i], [type='submit' i], [type='reset' i]) { + padding: 1rem 1.5rem; +} + +/* Meter & Progress */ + +:where(meter, progress) { + width: 100%; + height: 3rem; + border: 2px solid var(--c-gray); +} + +label + :where(meter, progress) { + margin-block-start: 0.5rem; +} + +meter { + background: transparent; + display: block; + margin-block-end: 1em; + border: 2px solid var(--c-gray); +} + +::-webkit-meter-bar { + background: Canvas; + height: 3rem; + border: 2px solid var(--c-gray); + border-radius: 0; +} + +::-webkit-progress-bar { + background-color: Canvas; +} + +::-moz-progress-bar { + background-color: var(--c-gray); +} + +::-webkit-progress-value { + background-color: var(--c-gray); +} + +/* Disabled state */ + +[disabled] { + border-color: var(--c-gray); + cursor: not-allowed; +} + +/* Error state */ + +[aria-invalid] { + border-color: var(--c-red) !important; +} + +[aria-invalid]:focus { + outline-color: var(--c-red); +} + +[aria-invalid] + p[id] { + color: var(--c-red); +} + +/* Form spacing */ + +form label:not(:first-of-type) { + margin-block-start: 3rem; +} + +form label + :where(input, textarea, select) { + margin-block-start: 0.5rem; +} + +form fieldset { + margin: 3rem 0; +} + +fieldset label:not(:first-of-type) { + margin-block-start: 2rem; +} + +form p[id] { + margin-block-start: 0.5rem; +} + + +/* Dialog +======================================== +*/ + +dialog[open] { + background-color: Canvas; + color: currentColor; + display: block; + max-width: var(--w-body, 100%); + min-width: calc(var(--w-body) / 2); + padding: 2rem; + border: 2px solid var(--c-gray); + border-radius: 0.5rem; +} + +body:has(dialog[open]) { + overflow: hidden; +} + +dialog:not([open]) { + display: none; +} + +dialog > *:first-child { + margin-block-start: 0; +} + +dialog > *:last-child { + margin-block-end: 0; +} + + +/* Opinionated layout +======================================== +*/ + +@media screen { + body > header { + margin-block-end: 4em; + } + + main > :where(section, article), + body > footer { + margin-block-start: 4em; + clear: both; + } + + body > footer { + margin-block-start: 4em; + } +} diff --git a/templates/base.html.twig b/templates/base.html.twig index 9bac562..458d961 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -2,16 +2,21 @@ + + + {% block title %}Welcome!{% endblock %} {% set currentDate = "now"|date("d") %} - {% if currentDate % 3 == 0 %} + {% if currentDate % 4 == 0 %} - {% elseif currentDate % 3 == 1 %} + {% elseif currentDate % 4 == 1 %} - {% else %} + {% elseif currentDate % 4 == 2 %} + {% else %} + {% endif %} ")}}function zr(){var e=re().querySelector('meta[name="htmx-config"]');if(e){return E(e.content)}else{return null}}function $r(){var e=zr();if(e){Q.config=le(Q.config,e)}}jr(function(){$r();_r();var e=re().body;zt(e);var t=re().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(e){var t=e.target;var r=ae(t);if(r&&r.xhr){r.xhr.abort()}});const r=window.onpopstate?window.onpopstate.bind(window):null;window.onpopstate=function(e){if(e.state&&e.state.htmx){ar();oe(t,function(e){ce(e,"htmx:restored",{document:re(),triggerEvent:ce})})}else{if(r){r(e)}}};setTimeout(function(){ce(e,"htmx:load",{});e=null},0)});return Q}()}); \ No newline at end of file diff --git a/symfony.lock b/symfony.lock index 676275b..cfab487 100644 --- a/symfony.lock +++ b/symfony.lock @@ -115,6 +115,21 @@ "phpcs.xml.dist" ] }, + "symfony/asset-mapper": { + "version": "7.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "6.4", + "ref": "5ad1308aa756d58f999ffbe1540d1189f5d7d14a" + }, + "files": [ + "assets/app.js", + "assets/styles/app.css", + "config/packages/asset_mapper.yaml", + "importmap.php" + ] + }, "symfony/console": { "version": "7.3", "recipe": { @@ -265,5 +280,8 @@ "config/packages/web_profiler.yaml", "config/routes/web_profiler.yaml" ] + }, + "twig/extra-bundle": { + "version": "v3.21.0" } } diff --git a/templates/base.html.twig b/templates/base.html.twig index 1036cc3..942c876 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -3,27 +3,12 @@ - - {% block title %}Welcome!{% endblock %} - {% set currentDate = "now"|date("d") %} - {% if currentDate % 4 == 0 %} - - {% elseif currentDate % 4 == 1 %} - - {% elseif currentDate % 4 == 2 %} - - {% else %} - - {% endif %} - - + {% block javascripts %} + {% block importmap %}{{ importmap('app') }}{% endblock %} + {% endblock %}
From 5cb66c5012635b1fc026e3cca8c8c9a728a229f0 Mon Sep 17 00:00:00 2001 From: lubiana Date: Wed, 18 Jun 2025 20:12:17 +0200 Subject: [PATCH 26/41] booty --- assets/app.js | 15 +- assets/javascript/emoji-footprint.js | 19 + assets/javascript/modes.js | 136 +++++ assets/javascript/numberInputs.js | 55 ++ assets/javascript/radioState.js | 35 ++ assets/javascript/theme.js | 18 + assets/styles/app.css | 182 +++++- assets/styles/emoji-footprint.css | 30 + assets/styles/modes.css | 565 ++++++++++++++++++ config/packages/twig.php | 19 +- templates/_form.html.twig | 6 +- templates/base.html.twig | 56 +- templates/food_order/edit.html.twig | 8 +- templates/food_order/index.html.twig | 25 +- templates/food_order/new.html.twig | 4 +- templates/food_order/show.html.twig | 32 +- templates/food_order/table_row.html.twig | 2 +- templates/food_vendor/_form.html.twig | 6 +- templates/food_vendor/edit.html.twig | 8 +- templates/food_vendor/index.html.twig | 14 +- templates/food_vendor/new.html.twig | 8 +- templates/food_vendor/show.html.twig | 21 +- templates/menu_item/_delete_form.html.twig | 2 +- templates/menu_item/_form.html.twig | 6 +- templates/menu_item/edit.html.twig | 10 +- templates/menu_item/index.html.twig | 14 +- templates/menu_item/new.html.twig | 8 +- templates/menu_item/show.html.twig | 18 +- templates/order_item/_form.html.twig | 6 +- templates/order_item/edit.html.twig | 8 +- templates/order_item/new.html.twig | 16 +- templates/username.html.twig | 8 +- .../Controller/FoodOrderControllerTest.php | 6 +- .../Controller/FoodVendorControllerTest.php | 2 +- 34 files changed, 1236 insertions(+), 132 deletions(-) create mode 100644 assets/javascript/emoji-footprint.js create mode 100644 assets/javascript/modes.js create mode 100644 assets/javascript/numberInputs.js create mode 100644 assets/javascript/radioState.js create mode 100644 assets/javascript/theme.js create mode 100644 assets/styles/emoji-footprint.css create mode 100644 assets/styles/modes.css diff --git a/assets/app.js b/assets/app.js index 321cea2..88672ac 100644 --- a/assets/app.js +++ b/assets/app.js @@ -4,6 +4,19 @@ * This file will be included onto the page via the importmap() Twig function, * which should already be in your base.html.twig. */ +import 'bootstrap/dist/css/bootstrap.min.css'; import './styles/app.css'; +import './styles/modes.css'; +import './styles/emoji-footprint.css'; -import './javascript/htmx.js'; \ No newline at end of file +// Import modules +import './javascript/theme.js'; +import './javascript/emoji-footprint.js'; +import './javascript/modes.js'; +import './javascript/htmx.js'; +import 'bootstrap'; +import { initRadioState } from './javascript/radioState.js'; + +document.addEventListener('DOMContentLoaded', () => { + initRadioState(); +}); \ No newline at end of file diff --git a/assets/javascript/emoji-footprint.js b/assets/javascript/emoji-footprint.js new file mode 100644 index 0000000..238377b --- /dev/null +++ b/assets/javascript/emoji-footprint.js @@ -0,0 +1,19 @@ +// Sparkle effect on mouse move +document.addEventListener('mousemove', function (e) { + const emojis = ['✨', '💖', '🌟', '💅', '🦄', '🎉', '🌈']; + const sparkle = document.createElement('div'); + sparkle.className = 'emoji-footprint'; + sparkle.textContent = emojis[Math.floor(Math.random() * emojis.length)]; + sparkle.style.left = e.pageX + 'px'; + sparkle.style.top = e.pageY + 'px'; + document.body.appendChild(sparkle); + + setTimeout(() => { + sparkle.remove(); + }, 1000); +}); + +export function initEmojiFootprint() { + // The sparkle effect is already initialized when this module is imported + // This function can be used if we need to control when the effect starts +} \ No newline at end of file diff --git a/assets/javascript/modes.js b/assets/javascript/modes.js new file mode 100644 index 0000000..a288f32 --- /dev/null +++ b/assets/javascript/modes.js @@ -0,0 +1,136 @@ +// Bonkers mode functionality +function setEmojiLevelClass(mode) { + document.body.classList.remove('emoji-normal', 'emoji-enhanced', 'emoji-bonkers'); + if (mode === 'bonkers') { + document.body.classList.add('emoji-bonkers'); + } else if (mode === 'enhanced') { + document.body.classList.add('emoji-enhanced'); + } else { + document.body.classList.add('emoji-normal'); + } +} + +function initBonkersMode() { + // Check if we're in bonkers mode + const currentMode = document.documentElement.getAttribute('data-website-mode'); + setEmojiLevelClass(currentMode); + + if (currentMode === 'bonkers') { + // Apply bonkers mode immediately + document.body.classList.add('bonkers-mode'); + + // Start the fabulous effects + createExtraSparkles(); + createSlayEffects(); + + console.log('🌈✨ Bonkers mode activated! ✨🌈'); + } else { + // Remove bonkers mode if it was active + document.body.classList.remove('bonkers-mode'); + } +} + +// Function to create extra sparkles during bonkers mode +function createExtraSparkles() { + const currentMode = document.documentElement.getAttribute('data-website-mode'); + if (currentMode !== 'bonkers') return; + + const extraEmojis = [ + '💃', '🕺', + '🍑', '💦', '😏', '😈', '👅', '💋', '🥵', '😳', '🤤', '😍', '🥴', + '💕', '💖', '💗', '💘', '💝', '💞', '💟', '💌', '💏', '💑', + '🍆', '🥒', '🍌', '💦', '👀', '😉', '😌', '😍', '🥰', '😘', + '😚', '😋', '😏', '😫', '😩', '🥺', '🥵', '🥴', + '💖', '💗', '💕', '💞', '💓', '💗', '💖', '💘', '💝', + '💋', '💏', '💑' + ]; + const sparkle = document.createElement('div'); + sparkle.className = 'emoji-footprint'; + sparkle.textContent = extraEmojis[Math.floor(Math.random() * extraEmojis.length)]; + sparkle.style.left = Math.random() * window.innerWidth + 'px'; + sparkle.style.top = Math.random() * window.innerHeight + 'px'; + document.body.appendChild(sparkle); + + setTimeout(() => { + if (sparkle.parentNode) { + sparkle.remove(); + } + }, 3000); + + // Continue creating extra sparkles while in bonkers mode + const newMode = document.documentElement.getAttribute('data-website-mode'); + if (newMode === 'bonkers') { + setTimeout(() => createExtraSparkles(), 150); + } +} + +// Function to create slay effects +function createSlayEffects() { + const currentMode = document.documentElement.getAttribute('data-website-mode'); + if (currentMode !== 'bonkers') return; + + // Create floating "SLAY" text effects + const slayWords = [ + 'SLAY', 'QUEEN', 'FABULOUS', 'ICONIC', 'LEGENDARY', 'STUNNING', 'GORGEOUS', 'FLAWLESS', + 'DAZZLING', 'RADIANT', 'BREATHTAKING', 'EXQUISITE', 'DIVINE' + ]; + const slayElement = document.createElement('div'); + slayElement.className = 'slay-text'; + slayElement.textContent = slayWords[Math.floor(Math.random() * slayWords.length)]; + slayElement.style.left = Math.random() * window.innerWidth + 'px'; + slayElement.style.top = Math.random() * window.innerHeight + 'px'; + document.body.appendChild(slayElement); + + setTimeout(() => { + if (slayElement.parentNode) { + slayElement.remove(); + } + }, 3000); + + // Continue creating slay effects while in bonkers mode + const newMode = document.documentElement.getAttribute('data-website-mode'); + if (newMode === 'bonkers') { + setTimeout(() => createSlayEffects(), 800); + } +} + +// Watch for mode changes +function watchModeChanges() { + // Create a MutationObserver to watch for changes to the data-website-mode attribute + const observer = new MutationObserver(function(mutations) { + mutations.forEach(function(mutation) { + if (mutation.type === 'attributes' && mutation.attributeName === 'data-website-mode') { + const newMode = document.documentElement.getAttribute('data-website-mode'); + + if (newMode === 'bonkers') { + document.body.classList.add('bonkers-mode'); + setEmojiLevelClass(newMode); + + // Start the fabulous effects + createExtraSparkles(); + createSlayEffects(); + + console.log('🌈✨ Switched to bonkers mode! ✨🌈'); + } else { + document.body.classList.remove('bonkers-mode'); + setEmojiLevelClass(newMode); + console.log(`😴 Switched to ${newMode} mode`); + } + } + }); + }); + + // Start observing + observer.observe(document.documentElement, { + attributes: true, + attributeFilter: ['data-website-mode'] + }); +} + +// Initialize when DOM is loaded +document.addEventListener('DOMContentLoaded', function() { + initBonkersMode(); + watchModeChanges(); +}); + +export { initBonkersMode, watchModeChanges }; diff --git a/assets/javascript/numberInputs.js b/assets/javascript/numberInputs.js new file mode 100644 index 0000000..a4a8dc1 --- /dev/null +++ b/assets/javascript/numberInputs.js @@ -0,0 +1,55 @@ +// Function to initialize number input buttons +function initNumberInputs(container = document) { + container.querySelectorAll('.number-input-wrapper').forEach(function(wrapper) { + const input = wrapper.querySelector('input[type="number"]'); + const decreaseBtn = wrapper.querySelector('[data-action="decrease"]'); + const increaseBtn = wrapper.querySelector('[data-action="increase"]'); + + if (!input || !decreaseBtn || !increaseBtn) return; + + // Skip if already initialized + if (decreaseBtn.hasAttribute('data-initialized')) return; + + const step = parseFloat(input.getAttribute('step')) || 1; + const min = 0; + const max = input.getAttribute('max') ? parseFloat(input.getAttribute('max')) : null; + + decreaseBtn.addEventListener('click', function() { + const currentValue = parseFloat(input.value) || 0; + const newValue = currentValue - step; + + if (min === null || newValue >= min) { + input.value = newValue; + input.dispatchEvent(new Event('change', { bubbles: true })); + } + }); + + increaseBtn.addEventListener('click', function() { + const currentValue = parseFloat(input.value) || 0; + const newValue = currentValue + step; + + if (max === null || newValue <= max) { + input.value = newValue; + input.dispatchEvent(new Event('change', { bubbles: true })); + } + }); + + // Validate input on change + input.addEventListener('input', function() { + const value = parseFloat(this.value); + + if (min !== null && value < min) { + this.value = min; + } + if (max !== null && value > max) { + this.value = max; + } + }); + + // Mark as initialized + decreaseBtn.setAttribute('data-initialized', 'true'); + increaseBtn.setAttribute('data-initialized', 'true'); + }); +} + +export { initNumberInputs }; \ No newline at end of file diff --git a/assets/javascript/radioState.js b/assets/javascript/radioState.js new file mode 100644 index 0000000..a30d311 --- /dev/null +++ b/assets/javascript/radioState.js @@ -0,0 +1,35 @@ +// Radio button state management with localStorage +function initRadioState() { + // Store and retrieve radio button state + const radioButtons = document.querySelectorAll('input[name="mode"]'); + + // Load saved state on page load + const savedMode = localStorage.getItem('selectedMode'); + if (savedMode) { + const radioToCheck = document.getElementById(savedMode); + if (radioToCheck) { + radioToCheck.checked = true; + // Set the data attribute to match the saved mode + document.documentElement.setAttribute('data-website-mode', savedMode); + } + } else { + // If no saved state, set to the currently checked radio button + const checkedRadio = document.querySelector('input[name="mode"]:checked'); + if (checkedRadio) { + document.documentElement.setAttribute('data-website-mode', checkedRadio.id); + } + } + + // Save state when radio button changes + radioButtons.forEach(radio => { + radio.addEventListener('change', function() { + if (this.checked) { + localStorage.setItem('selectedMode', this.id); + // Update the data attribute when mode changes + document.documentElement.setAttribute('data-website-mode', this.id); + } + }); + }); +} + +export { initRadioState }; \ No newline at end of file diff --git a/assets/javascript/theme.js b/assets/javascript/theme.js new file mode 100644 index 0000000..8acf738 --- /dev/null +++ b/assets/javascript/theme.js @@ -0,0 +1,18 @@ +// Theme detection and switching +const getPreferredTheme = () => { + return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' +} + +const setTheme = theme => { + document.documentElement.setAttribute('data-bs-theme', theme) +} + +// Set initial theme +setTheme(getPreferredTheme()) + +// Listen for changes in user's preferred color scheme +window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { + setTheme(getPreferredTheme()) +}) + +export { getPreferredTheme, setTheme }; \ No newline at end of file diff --git a/assets/styles/app.css b/assets/styles/app.css index dd6181a..7400735 100644 --- a/assets/styles/app.css +++ b/assets/styles/app.css @@ -1,3 +1,179 @@ -body { - background-color: skyblue; -} +/* + * ================================================================================================= + * 💖 BUBBLEGUM PUNK THEME (LIGHT) 💖 + * + * This isn't just a theme. It's a statement. + * Unapologetically loud, pink, and quirky. + * ================================================================================================= + */ + :root, + [data-bs-theme=light] { + /* --- CORE VIBE --- */ + --bs-pink: #FF007A; /* 💖 Hyper Pink (Our Queen) */ + --bs-green: #CFFF50; /* 🧪 Toxic Slime */ + --bs-purple: #A328D6; /* 👾 Graffiti Purple */ + --bs-yellow: #F9F871; /* ⚡ Neon Lemon */ + --bs-cyan: #00F5D4; /* 💎 Glitchy Teal */ + --bs-blue: #00A9E0; /* 💦 Splash Zone */ + + /* Let's redefine ALL the core colors to match the new energy */ + --bs-primary: var(--bs-pink); + --bs-secondary: var(--bs-green); + --bs-success: var(--bs-cyan); + --bs-info: var(--bs-blue); + --bs-warning: var(--bs-yellow); + --bs-danger: #FF3D3D; /* 🚨 Code Red Rave */ + + /* --- BACKGROUNDS & TEXT --- */ + /* No more boring white! */ + --bs-body-bg: #FFF5FD; /* A soft, dreamy pink canvas */ + --bs-body-color: #4A003D; /* Dark Plum (instead of black) for text */ + --bs-heading-color: var(--bs-purple); /* Make headings POP */ + --bs-secondary-color: rgba(74, 0, 61, 0.75); /* Plum, but softer */ + --bs-tertiary-color: rgba(74, 0, 61, 0.5); + + /* Make cards and containers pure white to contrast the pink background */ + --bs-tertiary-bg: #FFFFFF; + --bs-secondary-bg: #FEF9FE; + + /* --- LINKS & CODE --- */ + --bs-link-color: var(--bs-pink); + --bs-link-hover-color: var(--bs-purple); + --bs-code-color: var(--bs-purple); + + /* --- BORDERS & SHADOWS: LET'S GET QUIRKY --- */ + --bs-border-width: 2px; /* Chunky borders! */ + --bs-border-color: #FFD6F5; /* Pink-tinted border color */ + --bs-border-color-translucent: rgba(74, 0, 61, 0.2); + --bs-border-radius: 1rem; /* Super bubbly and round */ + --bs-border-radius-sm: 0.5rem; + --bs-border-radius-lg: 1.5rem; + --bs-border-radius-pill: 50rem; + + /* Say goodbye to black shadows, hello to colored glows! */ + --bs-box-shadow: 0 4px 12px rgba(255, 0, 122, 0.2); + --bs-box-shadow-sm: 0 2px 4px rgba(255, 0, 122, 0.15); + --bs-box-shadow-lg: 0 8px 30px rgba(255, 0, 122, 0.25); + --bs-box-shadow-inset: inset 0 1px 4px rgba(74, 0, 61, 0.2); + + /* --- THE GRADIENT: THE SOUL OF THE THEME --- */ + --bs-gradient: linear-gradient(75deg, var(--bs-primary), var(--bs-secondary)); + + /* --- Don't forget the RGB values for Bootstrap components! --- */ + --bs-primary-rgb: 255, 0, 122; + --bs-secondary-rgb: 207, 255, 80; + --bs-body-color-rgb: 74, 0, 61; + --bs-body-bg-rgb: 255, 245, 253; + } + + + /* + * ================================================================================================= + * 🌙🦇 CYBER GOTH THEME (DARK) 🦇🌙 + * + * The lights are out, the neon is ON. + * A dark, moody theme with vibrant, glowing accents. + * ================================================================================================= + */ + [data-bs-theme=dark] { + color-scheme: dark; + + /* --- BACKGROUNDS & TEXT --- */ + --bs-body-bg: #1D001A; /* Deep, dark space purple */ + --bs-body-color: #FFE9FA; /* Light pink text for high contrast */ + --bs-heading-color: var(--bs-cyan); /* Glowing cyan headings */ + + --bs-tertiary-bg: #2E0028; /* A slightly lighter container background */ + --bs-secondary-bg: #3A0033; + --bs-secondary-color: rgba(255, 233, 250, 0.75); + --bs-tertiary-color: rgba(255, 233, 250, 0.5); + + /* --- LINKS & CODE --- */ + /* Using the Toxic Slime for links gives it that cyber look */ + --bs-link-color: var(--bs-green); + --bs-link-hover-color: var(--bs-cyan); + --bs-code-color: var(--bs-pink); + + /* --- BORDERS & SHADOWS: NEON GLOWS --- */ + --bs-border-color: #5C004F; + --bs-border-color-translucent: rgba(255, 255, 255, 0.15); + + /* Redefine shadows to be neon glows */ + --bs-box-shadow: 0 0 15px rgba(var(--bs-primary-rgb), 0.4); + --bs-box-shadow-lg: 0 0 30px rgba(var(--bs-primary-rgb), 0.5); + + /* --- EMPHASIS & SUBTLE BACKGROUNDS --- */ + /* These are for alerts, badges, etc. They'll be dark with glowing text. */ + --bs-primary-text-emphasis: #FF8AD1; + --bs-secondary-text-emphasis: #E2FF8A; + --bs-success-text-emphasis: #8AFFEB; + --bs-info-text-emphasis: #7ADCF5; + --bs-warning-text-emphasis: #FAF8A8; + --bs-danger-text-emphasis: #FF8A8A; + + --bs-primary-bg-subtle: #3D002B; + --bs-secondary-bg-subtle: #415215; + --bs-success-bg-subtle: #00332B; + --bs-info-bg-subtle: #00313D; + --bs-warning-bg-subtle: #3E3D1C; + --bs-danger-bg-subtle: #520E0E; + } + +/* === EMOJI LEVELS === */ +.emoji-normal .emoji-normal { display: inline; } +.emoji-normal .emoji-enhanced, +.emoji-normal .emoji-bonkers { display: none; } + +.emoji-enhanced .emoji-enhanced { display: inline; } +.emoji-enhanced .emoji-normal, +.emoji-enhanced .emoji-bonkers { display: none; } + +.emoji-bonkers .emoji-bonkers { display: inline; } +.emoji-bonkers .emoji-normal, +.emoji-bonkers .emoji-enhanced { display: none; } + /* + * ================================================================================================= + * 🌈 RAINBOW PRIDE ELEMENTS 🌈 + * + * Fabulous rainbow-themed elements to celebrate diversity and pride! + * ================================================================================================= + */ + .bg-rainbow { + background: linear-gradient( + to right, + #FF5757, /* Red */ + #FFBD59, /* Orange */ + #F9F871, /* Yellow */ + #CFFF50, /* Green */ + #00F5D4, /* Teal */ + #00A9E0, /* Blue */ + #A328D6 /* Purple */ + ); + color: white; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5); + font-weight: bold; + border: none; + } + + .fun-fact { + font-size: 1.1rem; + line-height: 1.6; + font-style: italic; + } + + /* Add a subtle rainbow border to the fun facts card */ + .card:has(.fun-fact) { + border-width: 2px; + border-style: solid; + border-image: linear-gradient( + to right, + #FF5757, /* Red */ + #FFBD59, /* Orange */ + #F9F871, /* Yellow */ + #CFFF50, /* Green */ + #00F5D4, /* Teal */ + #00A9E0, /* Blue */ + #A328D6 /* Purple */ + ) 1; + box-shadow: 0 4px 15px rgba(163, 40, 214, 0.2); + } diff --git a/assets/styles/emoji-footprint.css b/assets/styles/emoji-footprint.css new file mode 100644 index 0000000..b076361 --- /dev/null +++ b/assets/styles/emoji-footprint.css @@ -0,0 +1,30 @@ + +/* Emoji Footprint Animation */ +.emoji-footprint { + position: absolute; + font-size: 1.6rem; + pointer-events: none; + animation: emojiFade 1s ease-out forwards; + transform: translate(-50%, -50%) scale(1); + opacity: 1; + z-index: 9999; + text-shadow: + 0 0 4px #ff00bf, + 0 0 8px #ff80df, + 0 0 12px #ffccff; +} + +@keyframes emojiFade { + 0% { + transform: translate(-50%, -50%) scale(1); + opacity: 1; + } + 50% { + transform: translate(-50%, -50%) scale(1.5); + opacity: 0.7; + } + 100% { + transform: translate(-50%, -50%) scale(2); + opacity: 0; + } +} \ No newline at end of file diff --git a/assets/styles/modes.css b/assets/styles/modes.css new file mode 100644 index 0000000..383815b --- /dev/null +++ b/assets/styles/modes.css @@ -0,0 +1,565 @@ +/* 🌈✨ BONKERS MODE ANIMATIONS ✨🌈 */ +@keyframes rainbowGradient { + 0% { background-position: 0% 50%; } + 50% { background-position: 100% 50%; } + 100% { background-position: 0% 50%; } +} + +@keyframes discoFlash { + 0%, 100% { + background-color: var(--bs-pink); + box-shadow: 0 0 20px var(--bs-pink), 0 0 40px var(--bs-pink); + } + 16.66% { + background-color: var(--bs-purple); + box-shadow: 0 0 20px var(--bs-purple), 0 0 40px var(--bs-purple); + } + 33.33% { + background-color: var(--bs-cyan); + box-shadow: 0 0 20px var(--bs-cyan), 0 0 40px var(--bs-cyan); + } + 50% { + background-color: var(--bs-yellow); + box-shadow: 0 0 20px var(--bs-yellow), 0 0 40px var(--bs-yellow); + } + 66.66% { + background-color: var(--bs-green); + box-shadow: 0 0 20px var(--bs-green), 0 0 40px var(--bs-green); + } + 83.33% { + background-color: var(--bs-orange); + box-shadow: 0 0 20px var(--bs-orange), 0 0 40px var(--bs-orange); + } +} + +@keyframes wiggle { + 0%, 100% { transform: rotate(0deg); } + 25% { transform: rotate(-2deg); } + 75% { transform: rotate(2deg); } +} + +@keyframes pulse { + 0%, 100% { transform: scale(1); } + 50% { transform: scale(1.05); } +} + +@keyframes spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + +@keyframes rainbowText { + 0% { color: var(--bs-red); } + 14.28% { color: var(--bs-orange); } + 28.57% { color: var(--bs-yellow); } + 42.85% { color: var(--bs-green); } + 57.14% { color: var(--bs-cyan); } + 71.42% { color: var(--bs-purple); } + 85.71% { color: var(--bs-pink); } + 100% { color: var(--bs-red); } +} + +@keyframes shine { + 0% { left: -100%; } + 50% { left: 100%; } + 100% { left: 100%; } +} + +@keyframes slayFloat { + 0% { + transform: translateY(0) scale(0.5); + opacity: 0; + } + 20% { + transform: translateY(-20px) scale(1); + opacity: 1; + } + 80% { + transform: translateY(-60px) scale(1.2); + opacity: 0.8; + } + 100% { + transform: translateY(-100px) scale(1.5); + opacity: 0; + } +} + +/* 🎭 BONKERS MODE CLASSES 🎭 */ +.bonkers-mode { + transition: all 0.3s ease-in-out; +} + +.bonkers-mode .btn { + animation: discoFlash 0.3s infinite, wiggle 0.2s infinite; + background: linear-gradient(45deg, var(--bs-pink), var(--bs-purple), var(--bs-cyan), var(--bs-yellow), var(--bs-green), var(--bs-orange), var(--bs-red)); + background-size: 400% 400%; + animation: discoFlash 0.3s infinite, wiggle 0.2s infinite, rainbowGradient 1s ease infinite; + border: 4px solid var(--bs-white); + font-weight: bold; + text-shadow: 2px 2px 4px rgba(0,0,0,0.5); + position: relative; + overflow: hidden; + transition: all 0.2s ease; +} + +.bonkers-mode .btn:hover { + animation: discoFlash 0.2s infinite, wiggle 0.1s infinite, rainbowGradient 0.5s ease infinite; + box-shadow: 0 0 30px var(--bs-pink), 0 0 60px var(--bs-purple); +} + +.bonkers-mode .btn::before { + content: ''; + position: absolute; + top: -50%; + left: -50%; + width: 200%; + height: 200%; + background: linear-gradient(45deg, transparent, rgba(255,255,255,0.5), transparent); + transform: rotate(45deg); + animation: spin 0.5s linear infinite; +} + +.bonkers-mode .navbar { + background: linear-gradient(90deg, var(--bs-pink), var(--bs-purple), var(--bs-cyan), var(--bs-yellow), var(--bs-green), var(--bs-orange), var(--bs-red)); + background-size: 200% 200%; + animation: rainbowGradient 2s ease infinite; + box-shadow: 0 0 50px rgba(255, 105, 180, 0.9); + height: auto !important; + min-height: 56px; +} + +.bonkers-mode .navbar-brand { + animation: rainbowText 0.8s infinite, wiggle 0.4s infinite; + font-size: 1.8em; + text-shadow: 3px 3px 6px rgba(0,0,0,0.5); + position: relative; + overflow: hidden; +} + +.bonkers-mode .navbar-brand::before { + content: ''; + position: absolute; + top: -50%; + left: -50%; + width: 200%; + height: 200%; + background: linear-gradient(45deg, transparent, rgba(255,255,255,0.3), transparent); + transform: rotate(45deg); + animation: spin 2s linear infinite; +} + +.bonkers-mode .navbar-nav .nav-link { + animation: rainbowText 1.2s infinite, wiggle 0.3s infinite; + font-weight: bold; + text-shadow: 2px 2px 4px rgba(0,0,0,0.5); + border: 2px solid transparent; + border-radius: 8px; + padding: 8px 16px; + margin: 0 4px; + transition: all 0.3s ease; + position: relative; + overflow: hidden; +} + +.bonkers-mode .navbar-nav .nav-link::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent); + animation: shine 1.5s ease-in-out infinite; +} + +.bonkers-mode .navbar-nav .nav-link:hover { + background: linear-gradient(45deg, var(--bs-pink), var(--bs-purple)); + border-color: var(--bs-white); + box-shadow: 0 0 20px var(--bs-pink); + animation: discoFlash 0.5s infinite, wiggle 0.2s infinite; +} + +.bonkers-mode .navbar-nav .nav-link.active { + background: linear-gradient(45deg, var(--bs-yellow), var(--bs-orange)); + border-color: var(--bs-white); + box-shadow: 0 0 25px var(--bs-yellow); + animation: discoFlash 0.8s infinite, wiggle 0.3s infinite; +} + +.bonkers-mode .navbar-text { + animation: rainbowText 1.5s infinite, wiggle 0.5s infinite; + font-weight: bold; + text-shadow: 2px 2px 4px rgba(0,0,0,0.5); + border: 2px solid var(--bs-white); + border-radius: 8px; + padding: 6px 12px; + background: linear-gradient(45deg, var(--bs-cyan), var(--bs-blue)); + box-shadow: 0 0 15px var(--bs-cyan); +} + +.bonkers-mode .navbar-toggler { + border: 3px solid var(--bs-white); + background: linear-gradient(45deg, var(--bs-pink), var(--bs-purple)); + animation: discoFlash 0.6s infinite, wiggle 0.4s infinite; + box-shadow: 0 0 20px var(--bs-pink); +} + +.bonkers-mode .navbar-toggler:focus { + box-shadow: 0 0 30px var(--bs-pink), 0 0 0 0.2rem rgba(255, 105, 180, 0.5); +} + +.bonkers-mode .navbar-toggler-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(255, 255, 255, 1)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); + animation: spin 1s linear infinite; +} + +.bonkers-mode .dropdown-menu { + background: linear-gradient(135deg, var(--bs-pink), var(--bs-purple), var(--bs-cyan)); + border: 3px solid var(--bs-white); + box-shadow: 0 0 30px rgba(255,105,180,0.8); + animation: rainbowGradient 2s ease infinite; +} + +.bonkers-mode .dropdown-item { + animation: rainbowText 1.8s infinite, wiggle 0.6s infinite; + font-weight: bold; + text-shadow: 1px 1px 2px rgba(0,0,0,0.5); + border-bottom: 1px solid rgba(255,255,255,0.3); + transition: all 0.3s ease; +} + +.bonkers-mode .dropdown-item:hover { + background: linear-gradient(45deg, var(--bs-yellow), var(--bs-orange)); + color: var(--bs-white); + box-shadow: 0 0 15px var(--bs-yellow); + animation: discoFlash 0.5s infinite, wiggle 0.3s infinite; +} + +.bonkers-mode .navbar-collapse { + background: linear-gradient(135deg, rgba(255,105,180,0.1), rgba(138,43,226,0.1)); + border-radius: 8px; + margin-top: 8px; + padding: 8px; + border: 2px solid var(--bs-pink); +} + +.bonkers-mode h1, .bonkers-mode h2, .bonkers-mode h3 { + animation: rainbowText 1.5s infinite; + text-shadow: 2px 2px 4px rgba(0,0,0,0.3); +} + +.bonkers-mode .table { + background: linear-gradient(135deg, rgba(255,105,180,0.2), rgba(138,43,226,0.2), rgba(0,255,255,0.2)); + animation: rainbowGradient 3s ease infinite; + border: 3px solid var(--bs-pink); + box-shadow: 0 0 30px rgba(255,105,180,0.5); +} + +.bonkers-mode .table th { + background: linear-gradient(45deg, var(--bs-pink), var(--bs-purple)); + color: var(--bs-white); + animation: discoFlash 0.8s infinite; + text-shadow: 1px 1px 2px rgba(0,0,0,0.5); + font-size: 1.1em; +} + +.bonkers-mode .form-control { + border: 3px solid var(--bs-pink); + box-shadow: 0 0 15px var(--bs-pink); + animation: pulse 0.6s infinite; +} + +.bonkers-mode .alert { + animation: discoFlash 0.6s infinite, wiggle 0.3s infinite; + border: 4px solid var(--bs-white); + font-weight: bold; + font-size: 1.1em; +} + +.bonkers-mode .card { + background: linear-gradient(45deg, rgba(255,105,180,0.2), rgba(138,43,226,0.2)); + border: 3px solid var(--bs-purple); + box-shadow: 0 0 35px rgba(138,43,226,0.6); + animation: pulse 1s infinite; +} + +.bonkers-mode .modal-content { + background: linear-gradient(135deg, var(--bs-pink), var(--bs-purple), var(--bs-cyan)); + border: 4px solid var(--bs-white); + box-shadow: 0 0 50px rgba(255,105,180,0.8); + animation: rainbowGradient 2s ease infinite; +} + +.bonkers-mode .modal-header { + background: linear-gradient(90deg, var(--bs-yellow), var(--bs-orange)); + animation: discoFlash 0.8s infinite; + font-size: 1.2em; +} + +.bonkers-mode .number-input-wrapper { + animation: wiggle 0.4s infinite; +} + +.bonkers-mode .number-input-wrapper .btn { + animation: discoFlash 0.3s infinite, wiggle 0.2s infinite; +} + +/* Enhanced mode styles (for future use) */ +[data-website-mode="enhanced"] .btn { + background: linear-gradient(45deg, var(--bs-pink), var(--bs-purple), var(--bs-cyan), var(--bs-yellow), var(--bs-green), var(--bs-orange), var(--bs-red)); + background-size: 400% 400%; + animation: rainbowGradient 1s ease infinite; + border: 4px solid var(--bs-white); + font-weight: bold; + text-shadow: 2px 2px 4px rgba(0,0,0,0.5); + position: relative; + overflow: hidden; + transition: all 0.2s ease; +} + +[data-website-mode="enhanced"] .btn:hover { + animation: rainbowGradient 0.5s ease infinite; + box-shadow: 0 0 30px var(--bs-pink), 0 0 60px var(--bs-purple); +} + +[data-website-mode="enhanced"] .btn::before { + content: ''; + position: absolute; + top: -50%; + left: -50%; + width: 200%; + height: 200%; + background: linear-gradient(45deg, transparent, rgba(255,255,255,0.5), transparent); + transform: rotate(45deg); + animation: spin 0.5s linear infinite; +} + +[data-website-mode="enhanced"] .navbar { + background: linear-gradient(90deg, var(--bs-pink), var(--bs-purple), var(--bs-cyan), var(--bs-yellow), var(--bs-green), var(--bs-orange), var(--bs-red)); + background-size: 200% 200%; + animation: rainbowGradient 2s ease infinite; + box-shadow: 0 0 50px rgba(255, 105, 180, 0.9); + height: auto !important; + min-height: 56px; +} + +[data-website-mode="enhanced"] .navbar-brand { + animation: rainbowText 0.8s infinite; + font-size: 1.8em; + text-shadow: 3px 3px 6px rgba(0,0,0,0.5); + position: relative; + overflow: hidden; +} + +[data-website-mode="enhanced"] .navbar-brand::before { + content: ''; + position: absolute; + top: -50%; + left: -50%; + width: 200%; + height: 200%; + background: linear-gradient(45deg, transparent, rgba(255,255,255,0.3), transparent); + transform: rotate(45deg); + animation: spin 2s linear infinite; +} + +[data-website-mode="enhanced"] .navbar-nav .nav-link { + animation: rainbowText 1.2s infinite; + font-weight: bold; + text-shadow: 2px 2px 4px rgba(0,0,0,0.5); + border: 2px solid transparent; + border-radius: 8px; + padding: 8px 16px; + margin: 0 4px; + transition: all 0.3s ease; + position: relative; + overflow: hidden; +} + +[data-website-mode="enhanced"] .navbar-nav .nav-link::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent); + animation: shine 1.5s ease-in-out infinite; +} + +[data-website-mode="enhanced"] .navbar-nav .nav-link:hover { + background: linear-gradient(45deg, var(--bs-pink), var(--bs-purple)); + border-color: var(--bs-white); + box-shadow: 0 0 20px var(--bs-pink); +} + +[data-website-mode="enhanced"] .navbar-nav .nav-link.active { + background: linear-gradient(45deg, var(--bs-yellow), var(--bs-orange)); + border-color: var(--bs-white); + box-shadow: 0 0 25px var(--bs-yellow); +} + +[data-website-mode="enhanced"] .navbar-text { + animation: rainbowText 1.5s infinite; + font-weight: bold; + text-shadow: 2px 2px 4px rgba(0,0,0,0.5); + border: 2px solid var(--bs-white); + border-radius: 8px; + padding: 6px 12px; + background: linear-gradient(45deg, var(--bs-cyan), var(--bs-blue)); + box-shadow: 0 0 15px var(--bs-cyan); +} + +[data-website-mode="enhanced"] .navbar-toggler { + border: 3px solid var(--bs-white); + background: linear-gradient(45deg, var(--bs-pink), var(--bs-purple)); + animation: rainbowGradient 0.6s ease infinite; + box-shadow: 0 0 20px var(--bs-pink); +} + +[data-website-mode="enhanced"] .navbar-toggler:focus { + box-shadow: 0 0 30px var(--bs-pink), 0 0 0 0.2rem rgba(255, 105, 180, 0.5); +} + +[data-website-mode="enhanced"] .navbar-toggler-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(255, 255, 255, 1)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); + animation: spin 1s linear infinite; +} + +[data-website-mode="enhanced"] .dropdown-menu { + background: linear-gradient(135deg, var(--bs-pink), var(--bs-purple), var(--bs-cyan)); + border: 3px solid var(--bs-white); + box-shadow: 0 0 30px rgba(255,105,180,0.8); + animation: rainbowGradient 2s ease infinite; +} + +[data-website-mode="enhanced"] .dropdown-item { + animation: rainbowText 1.8s infinite; + font-weight: bold; + text-shadow: 1px 1px 2px rgba(0,0,0,0.5); + border-bottom: 1px solid rgba(255,255,255,0.3); + transition: all 0.3s ease; +} + +[data-website-mode="enhanced"] .dropdown-item:hover { + background: linear-gradient(45deg, var(--bs-yellow), var(--bs-orange)); + color: var(--bs-white); + box-shadow: 0 0 15px var(--bs-yellow); +} + +[data-website-mode="enhanced"] .navbar-collapse { + background: linear-gradient(135deg, rgba(255,105,180,0.1), rgba(138,43,226,0.1)); + border-radius: 8px; + margin-top: 8px; + padding: 8px; + border: 2px solid var(--bs-pink); +} + +[data-website-mode="enhanced"] h1, [data-website-mode="enhanced"] h2, [data-website-mode="enhanced"] h3 { + animation: rainbowText 1.5s infinite; + text-shadow: 2px 2px 4px rgba(0,0,0,0.3); +} + +[data-website-mode="enhanced"] .table { + background: linear-gradient(135deg, rgba(255,105,180,0.2), rgba(138,43,226,0.2), rgba(0,255,255,0.2)); + animation: rainbowGradient 3s ease infinite; + border: 3px solid var(--bs-pink); + box-shadow: 0 0 30px rgba(255,105,180,0.5); +} + +[data-website-mode="enhanced"] .table th { + background: linear-gradient(45deg, var(--bs-pink), var(--bs-purple)); + color: var(--bs-white); + animation: rainbowGradient 0.8s ease infinite; + text-shadow: 1px 1px 2px rgba(0,0,0,0.5); + font-size: 1.1em; +} + +[data-website-mode="enhanced"] .form-control { + border: 3px solid var(--bs-pink); + box-shadow: 0 0 15px var(--bs-pink); +} + +[data-website-mode="enhanced"] .alert { + animation: rainbowGradient 0.6s ease infinite; + border: 4px solid var(--bs-white); + font-weight: bold; + font-size: 1.1em; +} + +[data-website-mode="enhanced"] .card { + background: linear-gradient(45deg, rgba(255,105,180,0.2), rgba(138,43,226,0.2)); + border: 3px solid var(--bs-purple); + box-shadow: 0 0 35px rgba(138,43,226,0.6); +} + +[data-website-mode="enhanced"] .modal-content { + background: linear-gradient(135deg, var(--bs-pink), var(--bs-purple), var(--bs-cyan)); + border: 4px solid var(--bs-white); + box-shadow: 0 0 50px rgba(255,105,180,0.8); + animation: rainbowGradient 2s ease infinite; +} + +[data-website-mode="enhanced"] .modal-header { + background: linear-gradient(90deg, var(--bs-yellow), var(--bs-orange)); + animation: rainbowGradient 0.8s ease infinite; + font-size: 1.2em; +} + +[data-website-mode="enhanced"] .number-input-wrapper { +} + +[data-website-mode="enhanced"] .number-input-wrapper .btn { + animation: rainbowGradient 0.3s ease infinite; +} + +/* Emoji Footprint Animation */ +.emoji-footprint { + position: absolute; + font-size: 1.6rem; + pointer-events: none; + animation: emojiFade 1s ease-out forwards; + transform: translate(-50%, -50%) scale(1); + opacity: 1; + z-index: 9999; + text-shadow: + 0 0 4px #ff00bf, + 0 0 8px #ff80df, + 0 0 12px #ffccff; +} + +@keyframes emojiFade { + 0% { + transform: translate(-50%, -50%) scale(1); + opacity: 1; + } + 50% { + transform: translate(-50%, -50%) scale(1.5); + opacity: 0.7; + } + 100% { + transform: translate(-50%, -50%) scale(2); + opacity: 0; + } +} + +/* 💅 SLAY TEXT EFFECTS 💅 */ +.slay-text { + position: fixed; + font-size: 2rem; + font-weight: bold; + pointer-events: none; + z-index: 10000; + animation: slayFloat 3s ease-out forwards; + text-shadow: + 0 0 10px #ff00bf, + 0 0 20px #ff80df, + 0 0 30px #ffccff, + 2px 2px 4px rgba(0,0,0,0.5); + background: linear-gradient(45deg, var(--bs-pink), var(--bs-purple), var(--bs-cyan), var(--bs-yellow)); + background-size: 400% 400%; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + animation: slayFloat 3s ease-out forwards, rainbowGradient 1s ease infinite; +} diff --git a/config/packages/twig.php b/config/packages/twig.php index d08fe3f..1c06a5b 100644 --- a/config/packages/twig.php +++ b/config/packages/twig.php @@ -1,17 +1,16 @@ extension('twig', [ - 'file_name_pattern' => '*.twig', - 'globals' => [ - 'favicon' => '@App\Service\Favicon', - ], - ]); +return static function ( + ContainerConfigurator $containerConfigurator, + TwigConfig $twig, + ): void { if ($containerConfigurator->env() === 'test') { - $containerConfigurator->extension('twig', [ - 'strict_variables' => true, - ]); + $twig->strictVariables(true); } + $twig->formThemes(['bootstrap_5_layout.html.twig']); + $twig->fileNamePattern('*.twig'); + $twig->global('favicon', '@App\Service\Favicon'); }; diff --git a/templates/_form.html.twig b/templates/_form.html.twig index bf20b98..e0ed7ee 100644 --- a/templates/_form.html.twig +++ b/templates/_form.html.twig @@ -1,4 +1,4 @@ -{{ form_start(form) }} - {{ form_widget(form) }} - +{{ form_start(form, {'attr': {'class': 'mb-3'}}) }} + {{ form_widget(form, {'attr': {'class': 'form-control'}}) }} + {{ form_end(form) }} diff --git a/templates/base.html.twig b/templates/base.html.twig index 942c876..6e78f0a 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -11,19 +11,53 @@ {% endblock %} -
-

Hello {{ app.request.cookies.get('username', 'nobody') }} - change name

-