From 146c14fc86ae3b23e48d0bd21f67d29f13f0bcfc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Mar 2025 15:20:25 +0000 Subject: [PATCH 01/10] Bump vite from 6.0.7 to 6.2.3 Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.0.7 to 6.2.3. - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v6.2.3/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v6.2.3/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- package-lock.json | 232 +++++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 117 insertions(+), 117 deletions(-) diff --git a/package-lock.json b/package-lock.json index 012e48e7..f2fd8a91 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,7 +44,7 @@ "@rollup/plugin-inject": "^5.0.5", "@types/alpinejs": "^3.13.10", "@types/jquery": "^3.5.31", - "vite": "^6.0.7", + "vite": "^6.2.3", "vite-bundle-visualizer": "^1.2.1", "vite-plugin-static-copy": "^2.1.0" } @@ -1736,9 +1736,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", - "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz", + "integrity": "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==", "cpu": [ "ppc64" ], @@ -1753,9 +1753,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", - "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.1.tgz", + "integrity": "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==", "cpu": [ "arm" ], @@ -1770,9 +1770,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", - "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.1.tgz", + "integrity": "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==", "cpu": [ "arm64" ], @@ -1787,9 +1787,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", - "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.1.tgz", + "integrity": "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==", "cpu": [ "x64" ], @@ -1804,9 +1804,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", - "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.1.tgz", + "integrity": "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==", "cpu": [ "arm64" ], @@ -1821,9 +1821,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", - "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.1.tgz", + "integrity": "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==", "cpu": [ "x64" ], @@ -1838,9 +1838,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", - "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.1.tgz", + "integrity": "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==", "cpu": [ "arm64" ], @@ -1855,9 +1855,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", - "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.1.tgz", + "integrity": "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==", "cpu": [ "x64" ], @@ -1872,9 +1872,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", - "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.1.tgz", + "integrity": "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==", "cpu": [ "arm" ], @@ -1889,9 +1889,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", - "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.1.tgz", + "integrity": "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==", "cpu": [ "arm64" ], @@ -1906,9 +1906,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", - "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.1.tgz", + "integrity": "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==", "cpu": [ "ia32" ], @@ -1923,9 +1923,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", - "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.1.tgz", + "integrity": "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==", "cpu": [ "loong64" ], @@ -1940,9 +1940,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", - "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.1.tgz", + "integrity": "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==", "cpu": [ "mips64el" ], @@ -1957,9 +1957,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", - "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.1.tgz", + "integrity": "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==", "cpu": [ "ppc64" ], @@ -1974,9 +1974,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", - "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.1.tgz", + "integrity": "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==", "cpu": [ "riscv64" ], @@ -1991,9 +1991,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", - "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.1.tgz", + "integrity": "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==", "cpu": [ "s390x" ], @@ -2008,9 +2008,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", - "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.1.tgz", + "integrity": "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==", "cpu": [ "x64" ], @@ -2025,9 +2025,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", - "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.1.tgz", + "integrity": "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==", "cpu": [ "arm64" ], @@ -2042,9 +2042,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", - "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.1.tgz", + "integrity": "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==", "cpu": [ "x64" ], @@ -2059,9 +2059,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", - "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.1.tgz", + "integrity": "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==", "cpu": [ "arm64" ], @@ -2076,9 +2076,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", - "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.1.tgz", + "integrity": "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==", "cpu": [ "x64" ], @@ -2093,9 +2093,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", - "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.1.tgz", + "integrity": "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==", "cpu": [ "x64" ], @@ -2110,9 +2110,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", - "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.1.tgz", + "integrity": "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==", "cpu": [ "arm64" ], @@ -2127,9 +2127,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", - "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.1.tgz", + "integrity": "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==", "cpu": [ "ia32" ], @@ -2144,9 +2144,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", - "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.1.tgz", + "integrity": "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==", "cpu": [ "x64" ], @@ -3681,9 +3681,9 @@ "license": "MIT" }, "node_modules/esbuild": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", - "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz", + "integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -3694,31 +3694,31 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.24.2", - "@esbuild/android-arm": "0.24.2", - "@esbuild/android-arm64": "0.24.2", - "@esbuild/android-x64": "0.24.2", - "@esbuild/darwin-arm64": "0.24.2", - "@esbuild/darwin-x64": "0.24.2", - "@esbuild/freebsd-arm64": "0.24.2", - "@esbuild/freebsd-x64": "0.24.2", - "@esbuild/linux-arm": "0.24.2", - "@esbuild/linux-arm64": "0.24.2", - "@esbuild/linux-ia32": "0.24.2", - "@esbuild/linux-loong64": "0.24.2", - "@esbuild/linux-mips64el": "0.24.2", - "@esbuild/linux-ppc64": "0.24.2", - "@esbuild/linux-riscv64": "0.24.2", - "@esbuild/linux-s390x": "0.24.2", - "@esbuild/linux-x64": "0.24.2", - "@esbuild/netbsd-arm64": "0.24.2", - "@esbuild/netbsd-x64": "0.24.2", - "@esbuild/openbsd-arm64": "0.24.2", - "@esbuild/openbsd-x64": "0.24.2", - "@esbuild/sunos-x64": "0.24.2", - "@esbuild/win32-arm64": "0.24.2", - "@esbuild/win32-ia32": "0.24.2", - "@esbuild/win32-x64": "0.24.2" + "@esbuild/aix-ppc64": "0.25.1", + "@esbuild/android-arm": "0.25.1", + "@esbuild/android-arm64": "0.25.1", + "@esbuild/android-x64": "0.25.1", + "@esbuild/darwin-arm64": "0.25.1", + "@esbuild/darwin-x64": "0.25.1", + "@esbuild/freebsd-arm64": "0.25.1", + "@esbuild/freebsd-x64": "0.25.1", + "@esbuild/linux-arm": "0.25.1", + "@esbuild/linux-arm64": "0.25.1", + "@esbuild/linux-ia32": "0.25.1", + "@esbuild/linux-loong64": "0.25.1", + "@esbuild/linux-mips64el": "0.25.1", + "@esbuild/linux-ppc64": "0.25.1", + "@esbuild/linux-riscv64": "0.25.1", + "@esbuild/linux-s390x": "0.25.1", + "@esbuild/linux-x64": "0.25.1", + "@esbuild/netbsd-arm64": "0.25.1", + "@esbuild/netbsd-x64": "0.25.1", + "@esbuild/openbsd-arm64": "0.25.1", + "@esbuild/openbsd-x64": "0.25.1", + "@esbuild/sunos-x64": "0.25.1", + "@esbuild/win32-arm64": "0.25.1", + "@esbuild/win32-ia32": "0.25.1", + "@esbuild/win32-x64": "0.25.1" } }, "node_modules/escalade": { @@ -4583,9 +4583,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "dev": true, "funding": [ { @@ -4916,9 +4916,9 @@ } }, "node_modules/postcss": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", - "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", "dev": true, "funding": [ { @@ -5688,15 +5688,15 @@ } }, "node_modules/vite": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.7.tgz", - "integrity": "sha512-RDt8r/7qx9940f8FcOIAH9PTViRrghKaK2K1jY3RaAURrEUbm9Du1mJ72G+jlhtG3WwodnfzY8ORQZbBavZEAQ==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.3.tgz", + "integrity": "sha512-IzwM54g4y9JA/xAeBPNaDXiBF8Jsgl3VBQ2YQ/wOY6fyW3xMdSoltIV3Bo59DErdqdE6RxUfv8W69DvUorE4Eg==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.24.2", - "postcss": "^8.4.49", - "rollup": "^4.23.0" + "esbuild": "^0.25.0", + "postcss": "^8.5.3", + "rollup": "^4.30.1" }, "bin": { "vite": "bin/vite.js" diff --git a/package.json b/package.json index 93494819..af289399 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "@rollup/plugin-inject": "^5.0.5", "@types/alpinejs": "^3.13.10", "@types/jquery": "^3.5.31", - "vite": "^6.0.7", + "vite": "^6.2.3", "vite-bundle-visualizer": "^1.2.1", "vite-plugin-static-copy": "^2.1.0" }, From df5838034e417a19e94ced579a940772bff42d7c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Apr 2025 10:09:43 +0000 Subject: [PATCH 02/10] Bump @babel/runtime from 7.26.0 to 7.27.0 Bumps [@babel/runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-runtime) from 7.26.0 to 7.27.0. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.27.0/packages/babel-runtime) --- updated-dependencies: - dependency-name: "@babel/runtime" dependency-version: 7.27.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index d6d496e8..09bb2140 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1514,9 +1514,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" From b0e24350e2b80e77ded402071c472e7650306b61 Mon Sep 17 00:00:00 2001 From: Thomas Girod Date: Sun, 6 Apr 2025 11:37:09 +0200 Subject: [PATCH 03/10] fix com admin pages --- com/tests/test_views.py | 23 +++++++++++++++++++++-- com/views.py | 27 +++++++++++++++------------ 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/com/tests/test_views.py b/com/tests/test_views.py index 03d28adc..607d4b3f 100644 --- a/com/tests/test_views.py +++ b/com/tests/test_views.py @@ -19,7 +19,7 @@ import pytest from django.conf import settings from django.contrib.sites.models import Site from django.core.files.uploadedfile import SimpleUploadedFile -from django.test import TestCase +from django.test import Client, TestCase from django.urls import reverse from django.utils import html from django.utils.timezone import localtime, now @@ -323,7 +323,7 @@ class TestNewsCreation(TestCase): @pytest.mark.django_db -def test_feed(client): +def test_feed(client: Client): """Smoke test that checks that the atom feed is working""" Site.objects.clear_cache() with assertNumQueries(2): @@ -332,3 +332,22 @@ def test_feed(client): resp = client.get(reverse("com:news_feed")) assert resp.status_code == 200 assert resp.headers["Content-Type"] == "application/rss+xml; charset=utf-8" + + +@pytest.mark.django_db +@pytest.mark.parametrize( + "url", + [ + reverse("com:poster_list"), + reverse("com:poster_create"), + reverse("com:poster_moderate_list"), + ], +) +def test_poster_management_views_crash_test(client: Client, url: str): + """Test that poster management views work""" + user = baker.make( + User, groups=[Group.objects.get(pk=settings.SITH_GROUP_COM_ADMIN_ID)] + ) + client.force_login(user) + res = client.get(url) + assert res.status_code == 200 diff --git a/com/views.py b/com/views.py index f6e12fd2..024cb781 100644 --- a/com/views.py +++ b/com/views.py @@ -61,8 +61,7 @@ sith = Sith.objects.first class ComTabsMixin(TabedViewMixin): - def get_tabs_title(self): - return _("Communication administration") + tabs_title = _("Communication administration") def get_list_of_tabs(self): return [ @@ -559,7 +558,11 @@ class MailingModerateView(View): raise PermissionDenied -class PosterListBaseView(ListView): +class PosterAdminViewMixin(IsComAdminMixin, ComTabsMixin): + current_tab = "posters" + + +class PosterListBaseView(PosterAdminViewMixin, ListView): """List communication posters.""" current_tab = "posters" @@ -586,7 +589,7 @@ class PosterListBaseView(ListView): return kwargs -class PosterCreateBaseView(CreateView): +class PosterCreateBaseView(PosterAdminViewMixin, CreateView): """Create communication poster.""" current_tab = "posters" @@ -618,7 +621,7 @@ class PosterCreateBaseView(CreateView): return super().form_valid(form) -class PosterEditBaseView(UpdateView): +class PosterEditBaseView(PosterAdminViewMixin, UpdateView): """Edit communication poster.""" pk_url_kwarg = "poster_id" @@ -664,7 +667,7 @@ class PosterEditBaseView(UpdateView): return super().form_valid(form) -class PosterDeleteBaseView(DeleteView): +class PosterDeleteBaseView(PosterAdminViewMixin, DeleteView): """Edit communication poster.""" pk_url_kwarg = "poster_id" @@ -681,7 +684,7 @@ class PosterDeleteBaseView(DeleteView): return super().dispatch(request, *args, **kwargs) -class PosterListView(IsComAdminMixin, ComTabsMixin, PosterListBaseView): +class PosterListView(PosterListBaseView): """List communication posters.""" def get_context_data(self, **kwargs): @@ -690,7 +693,7 @@ class PosterListView(IsComAdminMixin, ComTabsMixin, PosterListBaseView): return kwargs -class PosterCreateView(IsComAdminMixin, ComTabsMixin, PosterCreateBaseView): +class PosterCreateView(PosterCreateBaseView): """Create communication poster.""" success_url = reverse_lazy("com:poster_list") @@ -701,7 +704,7 @@ class PosterCreateView(IsComAdminMixin, ComTabsMixin, PosterCreateBaseView): return kwargs -class PosterEditView(IsComAdminMixin, ComTabsMixin, PosterEditBaseView): +class PosterEditView(PosterEditBaseView): """Edit communication poster.""" success_url = reverse_lazy("com:poster_list") @@ -712,13 +715,13 @@ class PosterEditView(IsComAdminMixin, ComTabsMixin, PosterEditBaseView): return kwargs -class PosterDeleteView(IsComAdminMixin, ComTabsMixin, PosterDeleteBaseView): +class PosterDeleteView(PosterDeleteBaseView): """Delete communication poster.""" success_url = reverse_lazy("com:poster_list") -class PosterModerateListView(IsComAdminMixin, ComTabsMixin, ListView): +class PosterModerateListView(PosterAdminViewMixin, ListView): """Moderate list communication poster.""" current_tab = "posters" @@ -732,7 +735,7 @@ class PosterModerateListView(IsComAdminMixin, ComTabsMixin, ListView): return kwargs -class PosterModerateView(IsComAdminMixin, ComTabsMixin, View): +class PosterModerateView(PosterAdminViewMixin, View): """Moderate communication poster.""" def get(self, request, *args, **kwargs): From fe5c6852041f113cefebd8a369f35a1ac9beb796 Mon Sep 17 00:00:00 2001 From: Thomas Girod Date: Sun, 6 Apr 2025 12:16:22 +0200 Subject: [PATCH 04/10] fix displayed user tabs --- core/tests/test_user.py | 89 ++++++++++++++++++++++++++++++++++++++++- core/views/user.py | 5 ++- 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/core/tests/test_user.py b/core/tests/test_user.py index 133f26a5..d14bfc3c 100644 --- a/core/tests/test_user.py +++ b/core/tests/test_user.py @@ -4,9 +4,10 @@ import pytest from django.conf import settings from django.contrib import auth from django.core.management import call_command -from django.test import Client, TestCase +from django.test import Client, RequestFactory, TestCase from django.urls import reverse from django.utils.timezone import now +from django.views.generic import DetailView from model_bakery import baker, seq from model_bakery.recipe import Recipe, foreign_key from pytest_django.asserts import assertRedirects @@ -18,6 +19,7 @@ from core.baker_recipes import ( very_old_subscriber_user, ) from core.models import Group, User +from core.views import UserTabsMixin from counter.models import Counter, Refilling, Selling from eboutic.models import Invoice, InvoiceItem @@ -229,3 +231,88 @@ def test_logout(client: Client): res = client.post(reverse("core:logout")) assertRedirects(res, reverse("core:login")) assert auth.get_user(client).is_anonymous + + +class UserTabTestView(UserTabsMixin, DetailView): ... + + +@pytest.mark.django_db +@pytest.mark.parametrize( + ["user_factory", "expected_tabs"], + [ + ( + subscriber_user.make, + [ + "infos", + "godfathers", + "pictures", + "tools", + "edit", + "prefs", + "clubs", + "stats", + "account", + ], + ), + ( + # this user is superuser, but still won't see a few tabs, + # because he is not subscribed + lambda: baker.make(User, is_superuser=True), + [ + "infos", + "godfathers", + "pictures", + "tools", + "edit", + "prefs", + "clubs", + "groups", + ], + ), + ], +) +def test_displayed_user_self_tabs(user_factory, expected_tabs: list[str]): + """Test that a user can view the appropriate tabs in its own profile""" + user = user_factory() + request = RequestFactory().get("") + request.user = user + view = UserTabTestView() + view.setup(request) + view.object = user + tabs = [tab["slug"] for tab in view.get_list_of_tabs()] + assert tabs == expected_tabs + + +@pytest.mark.django_db +@pytest.mark.parametrize( + ["user_factory", "expected_tabs"], + [ + (subscriber_user.make, ["infos", "godfathers", "pictures", "clubs"]), + ( + # this user is superuser, but still won't see a few tabs, + # because he is not subscribed + lambda: baker.make(User, is_superuser=True), + [ + "infos", + "godfathers", + "pictures", + "edit", + "prefs", + "clubs", + "groups", + "stats", + "account", + ], + ), + ], +) +def test_displayed_other_user_tabs(user_factory, expected_tabs: list[str]): + """Test that a user can view the appropriate tabs in another user's profile.""" + request_user = user_factory() + request = RequestFactory().get("") + request.user = request_user + view = UserTabTestView() + view.setup(request) + view.object = subscriber_user.make() # user whose page is being seen + tabs = [tab["slug"] for tab in view.get_list_of_tabs()] + assert tabs == expected_tabs diff --git a/core/views/user.py b/core/views/user.py index a9ce811f..cd27cbba 100644 --- a/core/views/user.py +++ b/core/views/user.py @@ -242,7 +242,10 @@ class UserTabsMixin(TabedViewMixin): if ( hasattr(user, "customer") and user.customer - and (user == self.request.user or user.has_perm("counter.view_customer")) + and ( + user == self.request.user + or self.request.user.has_perm("counter.view_customer") + ) ): tab_list.append( { From 9e0cb7647b191fc066e5554911106346cbde7e52 Mon Sep 17 00:00:00 2001 From: Thomas Girod Date: Sun, 6 Apr 2025 13:37:03 +0200 Subject: [PATCH 05/10] fix counter stats page access --- .../migrations/0031_alter_counter_options.py | 19 ++ counter/models.py | 21 ++- counter/tests/test_counter.py | 170 +++++++----------- counter/views/admin.py | 17 +- 4 files changed, 100 insertions(+), 127 deletions(-) create mode 100644 counter/migrations/0031_alter_counter_options.py diff --git a/counter/migrations/0031_alter_counter_options.py b/counter/migrations/0031_alter_counter_options.py new file mode 100644 index 00000000..c6d68529 --- /dev/null +++ b/counter/migrations/0031_alter_counter_options.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.20 on 2025-04-06 11:29 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("counter", "0030_returnableproduct_returnableproductbalance_and_more") + ] + + operations = [ + migrations.AlterModelOptions( + name="counter", + options={ + "permissions": [("view_counter_stats", "Can view counter stats")], + "verbose_name": "counter", + }, + ), + ] diff --git a/counter/models.py b/counter/models.py index ee6088d9..8581b19d 100644 --- a/counter/models.py +++ b/counter/models.py @@ -526,6 +526,7 @@ class Counter(models.Model): class Meta: verbose_name = _("counter") + permissions = [("view_counter_stats", "Can view counter stats")] def __str__(self): return self.name @@ -598,13 +599,12 @@ class Counter(models.Model): - the promo of the barman - the total number of office hours the barman did attend """ + name_expr = Concat(F("user__first_name"), Value(" "), F("user__last_name")) return ( self.permanencies.exclude(end=None) .annotate( - name=Concat(F("user__first_name"), Value(" "), F("user__last_name")) + name=name_expr, nickname=F("user__nick_name"), promo=F("user__promo") ) - .annotate(nickname=F("user__nick_name")) - .annotate(promo=F("user__promo")) .values("user", "name", "nickname", "promo") .annotate(perm_sum=Sum(F("end") - F("start"))) .exclude(perm_sum=None) @@ -628,18 +628,17 @@ class Counter(models.Model): since = get_start_of_semester() if isinstance(since, date): since = datetime(since.year, since.month, since.day, tzinfo=tz.utc) + name_expr = Concat( + F("customer__user__first_name"), Value(" "), F("customer__user__last_name") + ) return ( self.sellings.filter(date__gte=since) .annotate( - name=Concat( - F("customer__user__first_name"), - Value(" "), - F("customer__user__last_name"), - ) + name=name_expr, + nickname=F("customer__user__nick_name"), + promo=F("customer__user__promo"), + user=F("customer__user"), ) - .annotate(nickname=F("customer__user__nick_name")) - .annotate(promo=F("customer__user__promo")) - .annotate(user=F("customer__user")) .values("user", "promo", "name", "nickname") .annotate( selling_sum=Sum( diff --git a/counter/tests/test_counter.py b/counter/tests/test_counter.py index f0773897..e3b885b7 100644 --- a/counter/tests/test_counter.py +++ b/counter/tests/test_counter.py @@ -18,7 +18,7 @@ from decimal import Decimal import pytest from django.conf import settings -from django.contrib.auth.models import make_password +from django.contrib.auth.models import Permission, make_password from django.core.cache import cache from django.http import HttpResponse from django.shortcuts import resolve_url @@ -28,9 +28,10 @@ from django.utils import timezone from django.utils.timezone import localdate, now from freezegun import freeze_time from model_bakery import baker +from model_bakery.recipe import Recipe from pytest_django.asserts import assertRedirects -from club.models import Club, Membership +from club.models import Membership from core.baker_recipes import board_user, subscriber_user, very_old_subscriber_user from core.models import BanGroup, User from counter.baker_recipes import product_recipe, sale_recipe @@ -572,121 +573,86 @@ class TestCounterClick(TestFullClickBase): class TestCounterStats(TestCase): @classmethod def setUpTestData(cls): - cls.counter = Counter.objects.get(id=2) - cls.krophil = User.objects.get(username="krophil") - cls.skia = User.objects.get(username="skia") - cls.sli = User.objects.get(username="sli") - cls.root = User.objects.get(username="root") - cls.subscriber = User.objects.get(username="subscriber") - cls.old_subscriber = User.objects.get(username="old_subscriber") - cls.counter.sellers.add(cls.sli, cls.root, cls.skia, cls.krophil) - - barbar = Product.objects.get(code="BARB") - - # remove everything to make sure the fixtures bring no side effect - Permanency.objects.all().delete() - Selling.objects.all().delete() - - now = timezone.now() - # total of sli : 5 hours - Permanency.objects.create( - user=cls.sli, start=now, end=now + timedelta(hours=1), counter=cls.counter - ) - Permanency.objects.create( - user=cls.sli, - start=now + timedelta(hours=4), - end=now + timedelta(hours=6), - counter=cls.counter, - ) - Permanency.objects.create( - user=cls.sli, - start=now + timedelta(hours=7), - end=now + timedelta(hours=9), - counter=cls.counter, + cls.users = subscriber_user.make(_quantity=4) + product = product_recipe.make(selling_price=1) + cls.counter = baker.make( + Counter, type=["BAR"], sellers=cls.users[:4], products=[product] ) - # total of skia : 16 days, 2 hours, 35 minutes and 54 seconds - Permanency.objects.create( - user=cls.skia, start=now, end=now + timedelta(hours=1), counter=cls.counter - ) - Permanency.objects.create( - user=cls.skia, - start=now + timedelta(days=4, hours=1), - end=now + timedelta(days=20, hours=2, minutes=35, seconds=54), - counter=cls.counter, - ) + _now = timezone.now() + permanence_recipe = Recipe(Permanency, counter=cls.counter) + perms = [ + *[ # total of user 0 : 5 hours + permanence_recipe.prepare(user=cls.users[0], start=start, end=end) + for start, end in [ + (_now, _now + timedelta(hours=1)), + (_now + timedelta(hours=4), _now + timedelta(hours=6)), + (_now + timedelta(hours=7), _now + timedelta(hours=9)), + ] + ], + *[ # total of user 1 : 16 days, 2 hours, 35 minutes and 54 seconds + permanence_recipe.prepare(user=cls.users[1], start=start, end=end) + for start, end in [ + (_now, _now + timedelta(hours=1)), + ( + _now + timedelta(days=4, hours=1), + _now + timedelta(days=20, hours=2, minutes=35, seconds=54), + ), + ] + ], + *[ # total of user 2 : 2 hour + 20 hours (but the 20 hours were on last year) + permanence_recipe.prepare(user=cls.users[2], start=start, end=end) + for start, end in [ + (_now + timedelta(days=5), _now + timedelta(days=5, hours=1)), + (_now - timedelta(days=300, hours=20), _now - timedelta(days=300)), + ] + ], + ] + # user 3 has 0 hours of permanence + Permanency.objects.bulk_create(perms) - # total of root : 1 hour + 20 hours (but the 20 hours were on last year) - Permanency.objects.create( - user=cls.root, - start=now + timedelta(days=5), - end=now + timedelta(days=5, hours=1), - counter=cls.counter, - ) - Permanency.objects.create( - user=cls.root, - start=now - timedelta(days=300, hours=20), - end=now - timedelta(days=300), - counter=cls.counter, - ) - - # total of krophil : 0 hour - s = Selling( - label=barbar.name, - product=barbar, - club=baker.make(Club), + _sale_recipe = Recipe( + Selling, + club=cls.counter.club, counter=cls.counter, + product=product, unit_price=2, - seller=cls.skia, ) + sales = [ + *_sale_recipe.prepare( + quantity=100, customer=cls.users[0].customer, _quantity=10 + ), # 2000 € + *_sale_recipe.prepare( + quantity=100, customer=cls.users[1].customer, _quantity=5 + ), # 1000 € + _sale_recipe.prepare(quantity=1, customer=cls.users[2].customer), # 2€ + _sale_recipe.prepare(quantity=50, customer=cls.users[3].customer), # 100€ + ] + Selling.objects.bulk_create(sales) - krophil_customer = Customer.get_or_create(cls.krophil)[0] - sli_customer = Customer.get_or_create(cls.sli)[0] - skia_customer = Customer.get_or_create(cls.skia)[0] - root_customer = Customer.get_or_create(cls.root)[0] - - # moderate drinker. Total : 100 € - s.quantity = 50 - s.customer = krophil_customer - s.save(allow_negative=True) - - # Sli is a drunkard. Total : 2000 € - s.quantity = 100 - s.customer = sli_customer - for _ in range(10): - # little trick to make sure the instance is duplicated in db - s.pk = None - s.save(allow_negative=True) # save ten different sales - - # Skia is a heavy drinker too. Total : 1000 € - s.customer = skia_customer - for _ in range(5): - s.pk = None - s.save(allow_negative=True) - - # Root is quite an abstemious one. Total : 2 € - s.pk = None - s.quantity = 1 - s.customer = root_customer - s.save(allow_negative=True) - - def test_not_authenticated_user_fail(self): - # Test with not login user - response = self.client.get(reverse("counter:stats", args=[self.counter.id])) - assert response.status_code == 403 + def test_not_authenticated_access_fail(self): + url = reverse("counter:stats", args=[self.counter.id]) + response = self.client.get(url) + assertRedirects(response, reverse("core:login") + f"?next={url}") def test_unauthorized_user_fails(self): - self.client.force_login(User.objects.get(username="public")) + self.client.force_login(baker.make(User)) response = self.client.get(reverse("counter:stats", args=[self.counter.id])) assert response.status_code == 403 + def test_authorized_user_ok(self): + perm = Permission.objects.get(codename="view_counter_stats") + self.client.force_login(baker.make(User, user_permissions=[perm])) + response = self.client.get(reverse("counter:stats", args=[self.counter.id])) + assert response.status_code == 200 + def test_get_total_sales(self): """Test the result of the Counter.get_total_sales() method.""" assert self.counter.get_total_sales() == 3102 def test_top_barmen(self): """Test the result of Counter.get_top_barmen() is correct.""" - users = [self.skia, self.root, self.sli] + users = [self.users[1], self.users[2], self.users[0]] perm_times = [ timedelta(days=16, hours=2, minutes=35, seconds=54), timedelta(hours=21), @@ -700,12 +666,12 @@ class TestCounterStats(TestCase): "nickname": user.nick_name, "perm_sum": perm_time, } - for user, perm_time in zip(users, perm_times, strict=False) + for user, perm_time in zip(users, perm_times, strict=True) ] def test_top_customer(self): """Test the result of Counter.get_top_customers() is correct.""" - users = [self.sli, self.skia, self.krophil, self.root] + users = [self.users[0], self.users[1], self.users[3], self.users[2]] sale_amounts = [2000, 1000, 100, 2] assert list(self.counter.get_top_customers()) == [ { @@ -715,7 +681,7 @@ class TestCounterStats(TestCase): "nickname": user.nick_name, "selling_sum": sale_amount, } - for user, sale_amount in zip(users, sale_amounts, strict=False) + for user, sale_amount in zip(users, sale_amounts, strict=True) ] diff --git a/counter/views/admin.py b/counter/views/admin.py index f7d4a66b..ddd7a40e 100644 --- a/counter/views/admin.py +++ b/counter/views/admin.py @@ -27,7 +27,7 @@ from django.utils.translation import gettext as _ from django.views.generic import DetailView, ListView, TemplateView from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView -from core.auth.mixins import CanEditMixin, CanViewMixin +from core.auth.mixins import CanViewMixin from core.utils import get_semester_code, get_start_of_semester from counter.forms import ( CloseCustomerAccountForm, @@ -274,12 +274,13 @@ class SellingDeleteView(DeleteView): raise PermissionDenied -class CounterStatView(DetailView, CounterAdminMixin): +class CounterStatView(PermissionRequiredMixin, DetailView): """Show the bar stats.""" model = Counter pk_url_kwarg = "counter_id" template_name = "counter/stats.jinja" + permission_required = "counter.view_counter_stats" def get_context_data(self, **kwargs): """Add stats to the context.""" @@ -301,18 +302,6 @@ class CounterStatView(DetailView, CounterAdminMixin): ) return kwargs - def dispatch(self, request, *args, **kwargs): - try: - return super().dispatch(request, *args, **kwargs) - except PermissionDenied: - if ( - request.user.is_root - or request.user.is_board_member - or self.get_object().is_owned_by(request.user) - ): - return super(CanEditMixin, self).dispatch(request, *args, **kwargs) - raise PermissionDenied - class CounterRefillingListView(CounterAdminTabsMixin, CounterAdminMixin, ListView): """List of refillings on a counter.""" From 65c06dda8b070f4a83a25040588795c2ff5698df Mon Sep 17 00:00:00 2001 From: Thomas Girod Date: Sun, 6 Apr 2025 16:59:38 +0200 Subject: [PATCH 06/10] =?UTF-8?q?partnership=20eurock=C3=A9ennes=202025?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- eboutic/templates/eboutic/eboutic_main.jinja | 44 +++++++++++++++++++- locale/fr/LC_MESSAGES/django.po | 31 +++++++++++--- 2 files changed, 68 insertions(+), 7 deletions(-) diff --git a/eboutic/templates/eboutic/eboutic_main.jinja b/eboutic/templates/eboutic/eboutic_main.jinja index 3e6049f2..ee563dd0 100644 --- a/eboutic/templates/eboutic/eboutic_main.jinja +++ b/eboutic/templates/eboutic/eboutic_main.jinja @@ -78,7 +78,11 @@ {% if not request.user.date_of_birth %}
- {% trans %}You have not filled in your date of birth. As a result, you may not have access to all the products in the online shop. To fill in your date of birth, you can go to{% endtrans %} + {% trans trimmed %} + You have not filled in your date of birth. + As a result, you may not have access to all the products in the online shop. + To fill in your date of birth, you can go to + {% endtrans %} {% trans %}this page{% endtrans %} @@ -88,7 +92,43 @@
{% endif %} - +
+
+

{% trans %}Eurockéennes 2025 partnership{% endtrans %}

+ {% if user.is_subscribed %} + + Billetterie Weezevent + + + {% else %} +

+ {%- trans trimmed %} + You must be subscribed to benefit from the partnership with the Eurockéennes. + {% endtrans -%} +

+

+ {%- trans trimmed %} + This partnership offers a discount of up to 33% + on tickets for Friday, Saturday and Sunday, + as well as the 3-day package from Friday to Sunday. + {% endtrans -%} +

+ {% endif %} +
+
{% for priority_groups in products|groupby('order') %} {% for category, items in priority_groups.list|groupby('category') %} {% if items|count > 0 %} diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 25d03fa4..f3a6345f 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-04-04 10:35+0200\n" +"POT-Creation-Date: 2025-04-06 16:58+0200\n" "PO-Revision-Date: 2016-07-18\n" "Last-Translator: Maréchal \n" @@ -60,11 +60,11 @@ msgstr "Retirer" msgid "Action" msgstr "Action" -#: club/forms.py club/tests.py +#: club/forms.py club/tests/test_mailing.py msgid "This field is required" msgstr "Ce champ est obligatoire" -#: club/forms.py club/tests.py +#: club/forms.py club/tests/test_mailing.py msgid "One of the selected users doesn't have an email address" msgstr "Un des utilisateurs sélectionnés n'a pas d'adresse email" @@ -72,7 +72,7 @@ msgstr "Un des utilisateurs sélectionnés n'a pas d'adresse email" msgid "An action is required" msgstr "Une action est requise" -#: club/forms.py club/tests.py +#: club/forms.py club/tests/test_mailing.py msgid "You must specify at least an user or an email address" msgstr "vous devez spécifier au moins un utilisateur ou une adresse email" @@ -239,7 +239,7 @@ msgstr "Utilisateur" msgid "At least user or email is required" msgstr "Au moins un utilisateur ou un email est nécessaire" -#: club/models.py club/tests.py +#: club/models.py club/tests/test_mailing.py msgid "This email is already suscribed in this mailing" msgstr "Cet email est déjà abonné à cette mailing" @@ -3792,6 +3792,27 @@ msgstr "" msgid "this page" msgstr "cette page" +#: eboutic/templates/eboutic/eboutic_main.jinja +msgid "Eurockéennes 2025 partnership" +msgstr "Partenariat Eurockéennes 2025" + +#: eboutic/templates/eboutic/eboutic_main.jinja +msgid "" +"You must be subscribed to benefit from the partnership with the Eurockéennes." +msgstr "" +"Vous devez être cotisant pour bénéficier du partenariat avec les " +"Eurockéennes." + +#: eboutic/templates/eboutic/eboutic_main.jinja +#, python-format +msgid "" +"This partnership offers a discount of up to 33%% on tickets for Friday, " +"Saturday and Sunday, as well as the 3-day package from Friday to Sunday." +msgstr "" +"Ce partenariat permet de profiter d'une réduction jusqu'à 33%% sur les " +"billets du vendredi, du samedi et du dimanche, ainsi qu'au forfait 3 jours, " +"du vendredi au dimanche." + #: eboutic/templates/eboutic/eboutic_main.jinja msgid "There are no items available for sale" msgstr "Aucun article n'est disponible à la vente" From 77537a84c25111c90195d516232e91dabecc25c2 Mon Sep 17 00:00:00 2001 From: Thomas Girod Date: Sun, 6 Apr 2025 17:12:00 +0200 Subject: [PATCH 07/10] remove external calendar --- com/api.py | 21 +---- com/ics_calendar.py | 29 ------- .../com/components/ics-calendar-index.ts | 9 --- com/tests/test_api.py | 77 +------------------ 4 files changed, 2 insertions(+), 134 deletions(-) diff --git a/com/api.py b/com/api.py index 6de78a3c..9dd70606 100644 --- a/com/api.py +++ b/com/api.py @@ -1,8 +1,6 @@ -from pathlib import Path from typing import Literal -from django.conf import settings -from django.http import Http404, HttpResponse +from django.http import HttpResponse from ninja import Query from ninja_extra import ControllerBase, api_controller, paginate, route from ninja_extra.pagination import PageNumberPaginationExtra @@ -18,23 +16,6 @@ from core.views.files import send_raw_file @api_controller("/calendar") class CalendarController(ControllerBase): - CACHE_FOLDER: Path = settings.MEDIA_ROOT / "com" / "calendars" - - @route.get("/external.ics", url_name="calendar_external") - def calendar_external(self): - """Return the ICS file of the AE Google Calendar - - Because of Google's cors rules, we can't just do a request to google ics - from the frontend. Google is blocking CORS request in its responses headers. - The only way to do it from the frontend is to use Google Calendar API with an API key - This is not especially desirable as your API key is going to be provided to the frontend. - - This is why we have this backend based solution. - """ - if (calendar := IcsCalendar.get_external()) is not None: - return send_raw_file(calendar) - raise Http404 - @route.get("/internal.ics", url_name="calendar_internal") def calendar_internal(self): return send_raw_file(IcsCalendar.get_internal()) diff --git a/com/ics_calendar.py b/com/ics_calendar.py index 1c95a2b3..e5324c8a 100644 --- a/com/ics_calendar.py +++ b/com/ics_calendar.py @@ -1,8 +1,6 @@ -from datetime import datetime, timedelta from pathlib import Path from typing import final -import requests from dateutil.relativedelta import relativedelta from django.conf import settings from django.db.models import F, QuerySet @@ -19,35 +17,8 @@ from core.models import User @final class IcsCalendar: _CACHE_FOLDER: Path = settings.MEDIA_ROOT / "com" / "calendars" - _EXTERNAL_CALENDAR = _CACHE_FOLDER / "external.ics" _INTERNAL_CALENDAR = _CACHE_FOLDER / "internal.ics" - @classmethod - def get_external(cls, expiration: timedelta = timedelta(hours=1)) -> Path | None: - if ( - cls._EXTERNAL_CALENDAR.exists() - and timezone.make_aware( - datetime.fromtimestamp(cls._EXTERNAL_CALENDAR.stat().st_mtime) - ) - + expiration - > timezone.now() - ): - return cls._EXTERNAL_CALENDAR - return cls.make_external() - - @classmethod - def make_external(cls) -> Path | None: - calendar = requests.get( - "https://calendar.google.com/calendar/ical/ae.utbm%40gmail.com/public/basic.ics" - ) - if not calendar.ok: - return None - - cls._CACHE_FOLDER.mkdir(parents=True, exist_ok=True) - with open(cls._EXTERNAL_CALENDAR, "wb") as f: - _ = f.write(calendar.content) - return cls._EXTERNAL_CALENDAR - @classmethod def get_internal(cls) -> Path: if not cls._INTERNAL_CALENDAR.exists(): diff --git a/com/static/bundled/com/components/ics-calendar-index.ts b/com/static/bundled/com/components/ics-calendar-index.ts index 0b4976b0..d8fc79d7 100644 --- a/com/static/bundled/com/components/ics-calendar-index.ts +++ b/com/static/bundled/com/components/ics-calendar-index.ts @@ -8,7 +8,6 @@ import dayGridPlugin from "@fullcalendar/daygrid"; import iCalendarPlugin from "@fullcalendar/icalendar"; import listPlugin from "@fullcalendar/list"; import { - calendarCalendarExternal, calendarCalendarInternal, calendarCalendarUnpublished, newsDeleteNews, @@ -151,11 +150,6 @@ export class IcsCalendar extends inheritHtmlElement("div") { format: "ics", className: "internal", }, - { - url: `${await makeUrl(calendarCalendarExternal)}${cacheInvalidate}`, - format: "ics", - className: "external", - }, { url: `${await makeUrl(calendarCalendarUnpublished)}${cacheInvalidate}`, format: "ics", @@ -224,9 +218,6 @@ export class IcsCalendar extends inheritHtmlElement("div") { }; const makePopupTools = (event: EventImpl) => { - if (event.source.internalEventSource.ui.classNames.includes("external")) { - return null; - } if (!(this.canDelete || this.canModerate)) { return null; } diff --git a/com/tests/test_api.py b/com/tests/test_api.py index 7c3bcb7b..bfb7bb94 100644 --- a/com/tests/test_api.py +++ b/com/tests/test_api.py @@ -1,8 +1,6 @@ from dataclasses import dataclass -from datetime import datetime, timedelta +from datetime import timedelta from pathlib import Path -from typing import Callable -from unittest.mock import MagicMock, patch from urllib.parse import quote import pytest @@ -11,7 +9,6 @@ from django.contrib.auth.models import Permission from django.http import HttpResponse from django.test import Client, TestCase from django.urls import reverse -from django.utils import timezone from django.utils.timezone import now from model_bakery import baker, seq from pytest_django.asserts import assertNumQueries @@ -41,78 +38,6 @@ def accel_redirect_to_file(response: HttpResponse) -> Path | None: ) -@pytest.mark.django_db -class TestExternalCalendar: - @pytest.fixture - def mock_request(self): - mock = MagicMock() - with patch("requests.get", mock): - yield mock - - @pytest.fixture - def mock_current_time(self): - mock = MagicMock() - original = timezone.now - with patch("django.utils.timezone.now", mock): - yield mock, original - - @pytest.fixture(autouse=True) - def clear_cache(self): - IcsCalendar._EXTERNAL_CALENDAR.unlink(missing_ok=True) - - def test_fetch_error(self, client: Client, mock_request: MagicMock): - mock_request.return_value = MockResponse(ok=False, value="not allowed") - assert client.get(reverse("api:calendar_external")).status_code == 404 - - def test_fetch_success(self, client: Client, mock_request: MagicMock): - external_response = MockResponse(ok=True, value="Definitely an ICS") - mock_request.return_value = external_response - response = client.get(reverse("api:calendar_external")) - assert response.status_code == 200 - out_file = accel_redirect_to_file(response) - assert out_file is not None - assert out_file.exists() - with open(out_file, "r") as f: - assert f.read() == external_response.value - - def test_fetch_caching( - self, - client: Client, - mock_request: MagicMock, - mock_current_time: tuple[MagicMock, Callable[[], datetime]], - ): - fake_current_time, original_timezone = mock_current_time - start_time = original_timezone() - - fake_current_time.return_value = start_time - external_response = MockResponse(200, "Definitely an ICS") - mock_request.return_value = external_response - - with open( - accel_redirect_to_file(client.get(reverse("api:calendar_external"))), "r" - ) as f: - assert f.read() == external_response.value - - mock_request.return_value = MockResponse(200, "This should be ignored") - with open( - accel_redirect_to_file(client.get(reverse("api:calendar_external"))), "r" - ) as f: - assert f.read() == external_response.value - - mock_request.assert_called_once() - - fake_current_time.return_value = start_time + timedelta(hours=1, seconds=1) - external_response = MockResponse(200, "This won't be ignored") - mock_request.return_value = external_response - - with open( - accel_redirect_to_file(client.get(reverse("api:calendar_external"))), "r" - ) as f: - assert f.read() == external_response.value - - assert mock_request.call_count == 2 - - @pytest.mark.django_db class TestInternalCalendar: @pytest.fixture(autouse=True) From e35c1d1928565ddce745a3a9ef8cfb645b1eea62 Mon Sep 17 00:00:00 2001 From: Thomas Girod Date: Sun, 6 Apr 2025 12:22:32 +0200 Subject: [PATCH 08/10] move `eboutic/makecommand.js` to bundled directory --- .../makecommand.js => bundled/eboutic/makecommand-index.ts} | 5 +++-- eboutic/templates/eboutic/eboutic_makecommand.jinja | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) rename eboutic/static/{eboutic/js/makecommand.js => bundled/eboutic/makecommand-index.ts} (94%) diff --git a/eboutic/static/eboutic/js/makecommand.js b/eboutic/static/bundled/eboutic/makecommand-index.ts similarity index 94% rename from eboutic/static/eboutic/js/makecommand.js rename to eboutic/static/bundled/eboutic/makecommand-index.ts index 3ccb4280..da2a6f58 100644 --- a/eboutic/static/eboutic/js/makecommand.js +++ b/eboutic/static/bundled/eboutic/makecommand-index.ts @@ -17,12 +17,13 @@ document.addEventListener("alpine:init", () => { data: etData, async fill() { - document.getElementById("bank-submit-button").disabled = true; + const button = document.getElementById("bank-submit-button") as HTMLButtonElement; + button.disabled = true; // biome-ignore lint/correctness/noUndeclaredVariables: defined in eboutic_makecommand.jinja const res = await fetch(etDataUrl); if (res.ok) { this.data = await res.json(); - document.getElementById("bank-submit-button").disabled = false; + button.disabled = false; } }, }); diff --git a/eboutic/templates/eboutic/eboutic_makecommand.jinja b/eboutic/templates/eboutic/eboutic_makecommand.jinja index e18514e9..1846cdd1 100644 --- a/eboutic/templates/eboutic/eboutic_makecommand.jinja +++ b/eboutic/templates/eboutic/eboutic_makecommand.jinja @@ -9,7 +9,7 @@ {% endblock %} {% block additional_js %} - + {% endblock %} {% block content %} From d03c425a17a397f819b69c187c7bf0661415cc58 Mon Sep 17 00:00:00 2001 From: Thomas Girod Date: Sun, 6 Apr 2025 16:25:55 +0200 Subject: [PATCH 09/10] refactor eboutic command page --- .pre-commit-config.yaml | 2 +- eboutic/api.py | 2 +- .../bundled/eboutic/makecommand-index.ts | 91 ++++++++++--------- eboutic/static/eboutic/css/eboutic.css | 1 - .../eboutic/eboutic_makecommand.jinja | 43 ++++----- eboutic/views.py | 4 +- locale/fr/LC_MESSAGES/django.po | 27 ++---- locale/fr/LC_MESSAGES/djangojs.po | 12 ++- 8 files changed, 90 insertions(+), 92 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8d72985b..b334a7c0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: # Run the formatter. - id: ruff-format - repo: https://github.com/biomejs/pre-commit - rev: "v0.1.0" # Use the sha / tag you want to point at + rev: v0.6.1 hooks: - id: biome-check additional_dependencies: ["@biomejs/biome@1.9.4"] diff --git a/eboutic/api.py b/eboutic/api.py index 0054c02a..797adf20 100644 --- a/eboutic/api.py +++ b/eboutic/api.py @@ -26,7 +26,7 @@ class EtransactionInfoController(ControllerBase): customer=customer, defaults=info.model_dump(exclude_none=True) ) - @route.get("/data", url_name="etransaction_data", include_in_schema=False) + @route.get("/data", url_name="etransaction_data") def fetch_etransaction_data(self): """Generate the data to pay an eboutic command with paybox. diff --git a/eboutic/static/bundled/eboutic/makecommand-index.ts b/eboutic/static/bundled/eboutic/makecommand-index.ts index da2a6f58..c1e4b52f 100644 --- a/eboutic/static/bundled/eboutic/makecommand-index.ts +++ b/eboutic/static/bundled/eboutic/makecommand-index.ts @@ -1,56 +1,61 @@ -/** - * @readonly - * @enum {number} - */ -const BillingInfoReqState = { - // biome-ignore lint/style/useNamingConvention: this feels more like an enum - SUCCESS: 1, - // biome-ignore lint/style/useNamingConvention: this feels more like an enum - FAILURE: 2, - // biome-ignore lint/style/useNamingConvention: this feels more like an enum - SENDING: 3, -}; +import { exportToHtml } from "#core:utils/globals"; +import { + type BillingInfoSchema, + etransactioninfoFetchEtransactionData, + etransactioninfoPutUserBillingInfo, +} from "#openapi"; + +enum BillingInfoReqState { + Success = "0", + Failure = "1", + Sending = "2", +} + +exportToHtml("BillingInfoReqState", BillingInfoReqState); document.addEventListener("alpine:init", () => { - Alpine.store("billing_inputs", { - // biome-ignore lint/correctness/noUndeclaredVariables: defined in eboutic_makecommand.jinja - data: etData, + Alpine.data("etransactionData", (initialData) => ({ + data: initialData, async fill() { const button = document.getElementById("bank-submit-button") as HTMLButtonElement; button.disabled = true; - // biome-ignore lint/correctness/noUndeclaredVariables: defined in eboutic_makecommand.jinja - const res = await fetch(etDataUrl); - if (res.ok) { - this.data = await res.json(); + const res = await etransactioninfoFetchEtransactionData(); + if (res.response.ok) { + this.data = res.data; button.disabled = false; } }, - }); + })); - Alpine.data("billing_infos", () => ({ + Alpine.data("billing_infos", (userId: number) => ({ /** @type {BillingInfoReqState | null} */ reqState: null, async sendForm() { - this.reqState = BillingInfoReqState.SENDING; + this.reqState = BillingInfoReqState.Sending; const form = document.getElementById("billing_info_form"); - document.getElementById("bank-submit-button").disabled = true; + const submitButton = document.getElementById( + "bank-submit-button", + ) as HTMLButtonElement; + submitButton.disabled = true; const payload = Object.fromEntries( Array.from(form.querySelectorAll("input, select")) - .filter((elem) => elem.type !== "submit" && elem.value) - .map((elem) => [elem.name, elem.value]), + .filter((elem: HTMLInputElement) => elem.type !== "submit" && elem.value) + .map((elem: HTMLInputElement) => [elem.name, elem.value]), ); - // biome-ignore lint/correctness/noUndeclaredVariables: defined in eboutic_makecommand.jinja - const res = await fetch(billingInfoUrl, { - method: "PUT", - body: JSON.stringify(payload), + const res = await etransactioninfoPutUserBillingInfo({ + // biome-ignore lint/style/useNamingConvention: API is snake_case + path: { user_id: userId }, + body: payload as unknown as BillingInfoSchema, }); - this.reqState = res.ok - ? BillingInfoReqState.SUCCESS - : BillingInfoReqState.FAILURE; - if (res.status === 422) { - const errors = (await res.json()).detail.flatMap((err) => err.loc); + this.reqState = res.response.ok + ? BillingInfoReqState.Success + : BillingInfoReqState.Failure; + if (res.response.status === 422) { + const errors = await res.response + .json() + .detail.flatMap((err: Record<"loc", string>) => err.loc); for (const elem of Array.from(form.querySelectorAll("input")).filter((elem) => errors.includes(elem.name), )) { @@ -58,29 +63,27 @@ document.addEventListener("alpine:init", () => { elem.reportValidity(); elem.oninput = () => elem.setCustomValidity(""); } - } else if (res.ok) { - Alpine.store("billing_inputs").fill(); + } else if (res.response.ok) { + this.$dispatch("billing-infos-filled"); } }, getAlertColor() { - if (this.reqState === BillingInfoReqState.SUCCESS) { + if (this.reqState === BillingInfoReqState.Success) { return "green"; } - if (this.reqState === BillingInfoReqState.FAILURE) { + if (this.reqState === BillingInfoReqState.Failure) { return "red"; } return ""; }, getAlertMessage() { - if (this.reqState === BillingInfoReqState.SUCCESS) { - // biome-ignore lint/correctness/noUndeclaredVariables: defined in eboutic_makecommand.jinja - return billingInfoSuccessMessage; + if (this.reqState === BillingInfoReqState.Success) { + return gettext("Billing info registration success"); } - if (this.reqState === BillingInfoReqState.FAILURE) { - // biome-ignore lint/correctness/noUndeclaredVariables: defined in eboutic_makecommand.jinja - return billingInfoFailureMessage; + if (this.reqState === BillingInfoReqState.Failure) { + return gettext("Billing info registration failure"); } return ""; }, diff --git a/eboutic/static/eboutic/css/eboutic.css b/eboutic/static/eboutic/css/eboutic.css index abf121d0..6ca6beef 100644 --- a/eboutic/static/eboutic/css/eboutic.css +++ b/eboutic/static/eboutic/css/eboutic.css @@ -158,4 +158,3 @@ flex-direction: column; } } - diff --git a/eboutic/templates/eboutic/eboutic_makecommand.jinja b/eboutic/templates/eboutic/eboutic_makecommand.jinja index 1846cdd1..82ad4c51 100644 --- a/eboutic/templates/eboutic/eboutic_makecommand.jinja +++ b/eboutic/templates/eboutic/eboutic_makecommand.jinja @@ -9,7 +9,7 @@ {% endblock %} {% block additional_js %} - + {% endblock %} {% block content %} @@ -56,7 +56,7 @@
@@ -70,7 +70,7 @@

{% if billing_infos_state == BillingInfoState.EMPTY %}
- {% trans %}You must fill your billing infos if you want to pay with your credit - card{% endtrans %} + {% trans trimmed %} + You must fill your billing infos if you want to pay with your credit card + {% endtrans %}
{% elif billing_infos_state == BillingInfoState.MISSING_PHONE_NUMBER %}
- {% trans %} + {% trans trimmed %} The Crédit Agricole changed its policy related to the billing information that must be provided in order to pay with a credit card. If you want to pay with your credit card, you must add a phone number @@ -112,8 +113,14 @@ {% endtrans %}
{% endif %} -
-