assetmapper

This commit is contained in:
lubiana 2025-06-11 22:59:15 +02:00
parent 89c54af921
commit caeb663251
Signed by: lubiana
SSH key fingerprint: SHA256:vW1EA0fRR3Fw+dD/sM0K+x3Il2gSry6YRYHqOeQwrfk
23 changed files with 982 additions and 175 deletions

5
.gitignore vendored
View file

@ -19,3 +19,8 @@ phpstan.neon
/phpunit.xml
/.phpunit.cache/
###< phpunit/phpunit ###
###> symfony/asset-mapper ###
/public/assets/
/assets/vendor/
###< symfony/asset-mapper ###

25
assets/app.js Normal file
View file

@ -0,0 +1,25 @@
/*
* Welcome to your app's main JavaScript file!
*
* 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 modules
import './theme.js';
import './emoji-footprint.js';
import { initNumberInputs } from './numberInputs.js';
import { initRadioState } from './radioState.js';
// Initialize everything when DOM is ready
document.addEventListener('DOMContentLoaded', function() {
// Initialize radio button state management
initRadioState();
// Initialize number inputs on page load
initNumberInputs();
});

19
assets/emoji-footprint.js Normal file
View file

@ -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
}

0
assets/modes.js Normal file
View file

55
assets/numberInputs.js Normal file
View file

@ -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 };

35
assets/radioState.js Normal file
View file

@ -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 };

120
assets/styles/app.css Normal file
View file

@ -0,0 +1,120 @@
/*
* =================================================================================================
* 💖 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;
}

View file

@ -27,4 +27,4 @@
transform: translate(-50%, -50%) scale(2);
opacity: 0;
}
}
}

0
assets/styles/modes.css Normal file
View file

18
assets/theme.js Normal file
View file

@ -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 };

View file

@ -11,6 +11,8 @@
"doctrine/doctrine-bundle": "^2.14",
"doctrine/doctrine-migrations-bundle": "^3.4",
"doctrine/orm": "^3.3",
"symfony/asset": "7.3.*",
"symfony/asset-mapper": "7.3.*",
"symfony/console": "7.3.*",
"symfony/dotenv": "7.3.*",
"symfony/flex": "^2",
@ -20,7 +22,10 @@
"symfony/security-csrf": "7.3.*",
"symfony/twig-bundle": "7.3.*",
"symfony/validator": "7.3.*",
"symfony/yaml": "7.3.*"
"symfony/web-link": "7.3.*",
"symfony/yaml": "7.3.*",
"twig/extra-bundle": "^2.12|^3.0",
"twig/twig": "^2.12|^3.0"
},
"require-dev": {
"pestphp/pest": "^3.8",
@ -69,7 +74,8 @@
"scripts": {
"auto-scripts": {
"cache:clear": "symfony-cmd",
"assets:install %PUBLIC_DIR%": "symfony-cmd"
"assets:install %PUBLIC_DIR%": "symfony-cmd",
"importmap:install": "symfony-cmd"
},
"post-install-cmd": [
"@auto-scripts"

618
composer.lock generated
View file

@ -4,8 +4,89 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "7dbc573cc9b3d6ab3fb532611e469c70",
"content-hash": "c4121f65f6a9cee00e98a29d74d53e2a",
"packages": [
{
"name": "composer/semver",
"version": "3.4.3",
"source": {
"type": "git",
"url": "https://github.com/composer/semver.git",
"reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12",
"reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12",
"shasum": ""
},
"require": {
"php": "^5.3.2 || ^7.0 || ^8.0"
},
"require-dev": {
"phpstan/phpstan": "^1.11",
"symfony/phpunit-bridge": "^3 || ^7"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.x-dev"
}
},
"autoload": {
"psr-4": {
"Composer\\Semver\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nils Adermann",
"email": "naderman@naderman.de",
"homepage": "http://www.naderman.de"
},
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be",
"homepage": "http://seld.be"
},
{
"name": "Rob Bast",
"email": "rob.bast@gmail.com",
"homepage": "http://robbast.nl"
}
],
"description": "Semver library that offers utilities, version constraint parsing and validation.",
"keywords": [
"semantic",
"semver",
"validation",
"versioning"
],
"support": {
"irc": "ircs://irc.libera.chat:6697/composer",
"issues": "https://github.com/composer/semver/issues",
"source": "https://github.com/composer/semver/tree/3.4.3"
},
"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-09-19T14:15:21+00:00"
},
{
"name": "doctrine/cache",
"version": "2.2.0",
@ -1375,6 +1456,62 @@
},
"time": "2019-01-08T18:20:26+00:00"
},
{
"name": "psr/link",
"version": "2.0.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/link.git",
"reference": "84b159194ecfd7eaa472280213976e96415433f7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/link/zipball/84b159194ecfd7eaa472280213976e96415433f7",
"reference": "84b159194ecfd7eaa472280213976e96415433f7",
"shasum": ""
},
"require": {
"php": ">=8.0.0"
},
"suggest": {
"fig/link-util": "Provides some useful PSR-13 utilities"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Link\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interfaces for HTTP links",
"homepage": "https://github.com/php-fig/link",
"keywords": [
"http",
"http-link",
"link",
"psr",
"psr-13",
"rest"
],
"support": {
"source": "https://github.com/php-fig/link/tree/2.0.1"
},
"time": "2021-03-11T23:00:27+00:00"
},
{
"name": "psr/log",
"version": "3.0.2",
@ -1425,6 +1562,155 @@
},
"time": "2024-09-11T13:17:53+00:00"
},
{
"name": "symfony/asset",
"version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/asset.git",
"reference": "56c4d9f759247c4e07d8549e3baf7493cb9c3e4b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/asset/zipball/56c4d9f759247c4e07d8549e3baf7493cb9c3e4b",
"reference": "56c4d9f759247c4e07d8549e3baf7493cb9c3e4b",
"shasum": ""
},
"require": {
"php": ">=8.2"
},
"conflict": {
"symfony/http-foundation": "<6.4"
},
"require-dev": {
"symfony/http-client": "^6.4|^7.0",
"symfony/http-foundation": "^6.4|^7.0",
"symfony/http-kernel": "^6.4|^7.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Asset\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Manages URL generation and versioning of web assets such as CSS stylesheets, JavaScript files and image files",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/asset/tree/v7.3.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": "2025-03-05T10:15:41+00:00"
},
{
"name": "symfony/asset-mapper",
"version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/asset-mapper.git",
"reference": "6516f38868b75c4902ea72a9fa44967628375ae7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/asset-mapper/zipball/6516f38868b75c4902ea72a9fa44967628375ae7",
"reference": "6516f38868b75c4902ea72a9fa44967628375ae7",
"shasum": ""
},
"require": {
"composer/semver": "^3.0",
"php": ">=8.2",
"symfony/deprecation-contracts": "^2.1|^3",
"symfony/filesystem": "^7.1",
"symfony/http-client": "^6.4|^7.0"
},
"conflict": {
"symfony/framework-bundle": "<6.4"
},
"require-dev": {
"symfony/asset": "^6.4|^7.0",
"symfony/browser-kit": "^6.4|^7.0",
"symfony/console": "^6.4|^7.0",
"symfony/event-dispatcher-contracts": "^3.0",
"symfony/finder": "^6.4|^7.0",
"symfony/framework-bundle": "^6.4|^7.0",
"symfony/http-foundation": "^6.4|^7.0",
"symfony/http-kernel": "^6.4|^7.0",
"symfony/process": "^6.4|^7.0",
"symfony/web-link": "^6.4|^7.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\AssetMapper\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Maps directories of assets & makes them available in a public directory with versioned filenames.",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/asset-mapper/tree/v7.3.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": "2025-05-24T14:05:12+00:00"
},
{
"name": "symfony/cache",
"version": "v7.3.0",
@ -2780,6 +3066,179 @@
],
"time": "2025-05-28T06:56:42+00:00"
},
{
"name": "symfony/http-client",
"version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client.git",
"reference": "57e4fb86314015a695a750ace358d07a7e37b8a9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-client/zipball/57e4fb86314015a695a750ace358d07a7e37b8a9",
"reference": "57e4fb86314015a695a750ace358d07a7e37b8a9",
"shasum": ""
},
"require": {
"php": ">=8.2",
"psr/log": "^1|^2|^3",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/http-client-contracts": "~3.4.4|^3.5.2",
"symfony/service-contracts": "^2.5|^3"
},
"conflict": {
"amphp/amp": "<2.5",
"php-http/discovery": "<1.15",
"symfony/http-foundation": "<6.4"
},
"provide": {
"php-http/async-client-implementation": "*",
"php-http/client-implementation": "*",
"psr/http-client-implementation": "1.0",
"symfony/http-client-implementation": "3.0"
},
"require-dev": {
"amphp/http-client": "^4.2.1|^5.0",
"amphp/http-tunnel": "^1.0|^2.0",
"amphp/socket": "^1.1",
"guzzlehttp/promises": "^1.4|^2.0",
"nyholm/psr7": "^1.0",
"php-http/httplug": "^1.0|^2.0",
"psr/http-client": "^1.0",
"symfony/amphp-http-client-meta": "^1.0|^2.0",
"symfony/dependency-injection": "^6.4|^7.0",
"symfony/http-kernel": "^6.4|^7.0",
"symfony/messenger": "^6.4|^7.0",
"symfony/process": "^6.4|^7.0",
"symfony/rate-limiter": "^6.4|^7.0",
"symfony/stopwatch": "^6.4|^7.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\HttpClient\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously",
"homepage": "https://symfony.com",
"keywords": [
"http"
],
"support": {
"source": "https://github.com/symfony/http-client/tree/v7.3.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": "2025-05-02T08:23:16+00:00"
},
{
"name": "symfony/http-client-contracts",
"version": "v3.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client-contracts.git",
"reference": "75d7043853a42837e68111812f4d964b01e5101c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/75d7043853a42837e68111812f4d964b01e5101c",
"reference": "75d7043853a42837e68111812f4d964b01e5101c",
"shasum": ""
},
"require": {
"php": ">=8.1"
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/contracts",
"name": "symfony/contracts"
},
"branch-alias": {
"dev-main": "3.6-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Contracts\\HttpClient\\": ""
},
"exclude-from-classmap": [
"/Test/"
]
},
"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": "Generic abstractions related to HTTP clients",
"homepage": "https://symfony.com",
"keywords": [
"abstractions",
"contracts",
"decoupling",
"interfaces",
"interoperability",
"standards"
],
"support": {
"source": "https://github.com/symfony/http-client-contracts/tree/v3.6.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": "2025-04-29T11:18:49+00:00"
},
{
"name": "symfony/http-foundation",
"version": "v7.3.0",
@ -4758,6 +5217,89 @@
],
"time": "2025-05-15T09:04:05+00:00"
},
{
"name": "symfony/web-link",
"version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/web-link.git",
"reference": "7697f74fce67555665339423ce453cc8216a98ff"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/web-link/zipball/7697f74fce67555665339423ce453cc8216a98ff",
"reference": "7697f74fce67555665339423ce453cc8216a98ff",
"shasum": ""
},
"require": {
"php": ">=8.2",
"psr/link": "^1.1|^2.0"
},
"conflict": {
"symfony/http-kernel": "<6.4"
},
"provide": {
"psr/link-implementation": "1.0|2.0"
},
"require-dev": {
"symfony/http-kernel": "^6.4|^7.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\WebLink\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Kévin Dunglas",
"email": "dunglas@gmail.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Manages links between resources",
"homepage": "https://symfony.com",
"keywords": [
"dns-prefetch",
"http",
"http2",
"link",
"performance",
"prefetch",
"preload",
"prerender",
"psr13",
"push"
],
"support": {
"source": "https://github.com/symfony/web-link/tree/v7.3.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": "2025-05-19T13:28:18+00:00"
},
{
"name": "symfony/yaml",
"version": "v7.3.0",
@ -4830,6 +5372,80 @@
],
"time": "2025-04-04T10:10:33+00:00"
},
{
"name": "twig/extra-bundle",
"version": "v3.21.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/twig-extra-bundle.git",
"reference": "62d1cf47a1aa009cbd07b21045b97d3d5cb79896"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/62d1cf47a1aa009cbd07b21045b97d3d5cb79896",
"reference": "62d1cf47a1aa009cbd07b21045b97d3d5cb79896",
"shasum": ""
},
"require": {
"php": ">=8.1.0",
"symfony/framework-bundle": "^5.4|^6.4|^7.0",
"symfony/twig-bundle": "^5.4|^6.4|^7.0",
"twig/twig": "^3.2|^4.0"
},
"require-dev": {
"league/commonmark": "^1.0|^2.0",
"symfony/phpunit-bridge": "^6.4|^7.0",
"twig/cache-extra": "^3.0",
"twig/cssinliner-extra": "^3.0",
"twig/html-extra": "^3.0",
"twig/inky-extra": "^3.0",
"twig/intl-extra": "^3.0",
"twig/markdown-extra": "^3.0",
"twig/string-extra": "^3.0"
},
"type": "symfony-bundle",
"autoload": {
"psr-4": {
"Twig\\Extra\\TwigExtraBundle\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
}
],
"description": "A Symfony bundle for extra Twig extensions",
"homepage": "https://twig.symfony.com",
"keywords": [
"bundle",
"extra",
"twig"
],
"support": {
"source": "https://github.com/twigphp/twig-extra-bundle/tree/v3.21.0"
},
"funding": [
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/twig/twig",
"type": "tidelift"
}
],
"time": "2025-02-19T14:29:33+00:00"
},
{
"name": "twig/twig",
"version": "v3.21.1",

View file

@ -1,32 +1,11 @@
<?php
declare(strict_types=1);
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Bundle\TwigBundle\TwigBundle;
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
use Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle;
use Symfony\Bundle\MakerBundle\MakerBundle;
use Symfony\Bundle\WebProfilerBundle\WebProfilerBundle;
return [
FrameworkBundle::class => [
'all' => true,
],
TwigBundle::class => [
'all' => true,
],
DoctrineBundle::class => [
'all' => true,
],
DoctrineMigrationsBundle::class => [
'all' => true,
],
MakerBundle::class => [
'dev' => true,
],
WebProfilerBundle::class => [
'dev' => true,
'test' => true,
],
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
];

View file

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$containerConfigurator->extension('framework', [
'asset_mapper' => [
'paths' => [
'assets/',
],
'missing_import_mode' => 'strict',
],
]);
if ($containerConfigurator->env() === 'prod') {
$containerConfigurator->extension('framework', [
'asset_mapper' => [
'missing_import_mode' => 'warn',
],
]);
}
};

29
importmap.php Normal file
View file

@ -0,0 +1,29 @@
<?php
/**
* Returns the importmap for this application.
*
* - "path" is a path inside the asset mapper system. Use the
* "debug:asset-map" command to see the full list of paths.
*
* - "entrypoint" (JavaScript only) set to true for any module that will
* be used as an "entrypoint" (and passed to the importmap() Twig function).
*
* The "importmap:require" command can be used to add new entries to this file.
*/
return [
'app' => [
'path' => './assets/app.js',
'entrypoint' => true,
],
'bootstrap' => [
'version' => '5.3.6',
],
'@popperjs/core' => [
'version' => '2.11.8',
],
'bootstrap/dist/css/bootstrap.min.css' => [
'version' => '5.3.6',
'type' => 'css',
],
];

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

View file

@ -1,123 +0,0 @@
// 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);
});
// 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');
});
}
document.addEventListener('DOMContentLoaded', function() {
// 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;
}
}
// Save state when radio button changes
radioButtons.forEach(radio => {
radio.addEventListener('change', function() {
if (this.checked) {
localStorage.setItem('selectedMode', this.id);
}
});
});
// Bootstrap Modal handling
const htmxModal = document.getElementById('htmxModal');
if (htmxModal) {
htmxModal.addEventListener('show.bs.modal', function(event) {
// Get the button that triggered the modal
const button = event.relatedTarget;
// Extract the drink name from data-* attributes
const drinkName = button.getAttribute('data-drink-name');
// Update the modal title
if (drinkName) {
const modalTitle = htmxModal.querySelector('.modal-title');
if (modalTitle) {
modalTitle.textContent = 'Update Stock for ' + drinkName;
}
}
});
}
// HTMX Modal handling
document.body.addEventListener('htmx:afterSwap', function(event) {
// If the target is the modal body, initialize any form elements inside it
if (event.detail.target.id === 'htmxModalBody') {
// The modal content has been loaded
console.log('Modal content loaded');
// Initialize number inputs in the modal
initNumberInputs(event.detail.target);
}
});
// Initialize number inputs on page load
initNumberInputs();
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -62,6 +62,21 @@
"bin/phpunit"
]
},
"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": {
@ -190,5 +205,8 @@
"config/packages/web_profiler.yaml",
"config/routes/web_profiler.yaml"
]
},
"twig/extra-bundle": {
"version": "v3.21.0"
}
}

View file

@ -1,18 +1,14 @@
<!DOCTYPE html>
<html data-bs-theme="auto">
<html data-bs-theme="auto" data-website-mode="normal">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}Welcome!{% endblock %}</title>
{% block stylesheets %}
<link rel="stylesheet" href="/assets/css/bootstrap.min.css">
<link rel="stylesheet" href="/assets/css/styles.css">
{% endblock %}
{% block javascripts %}
<script src="/assets/js/app.js"></script>
<script src="/assets/js/bootstrap.bundle.min.js"></script>
<script src="/assets/js/htmx.min.js"></script>
{% block importmap %}{{ importmap('app') }}{% endblock %}
{% endblock %}
</head>
<body>