Fix poster selection and cart

This commit is contained in:
Pierret Hugo
2024-12-22 01:38:31 +01:00
parent d127beed48
commit 0d08d06aba
10 changed files with 334 additions and 249 deletions

221
package-lock.json generated
View File

@@ -8,13 +8,13 @@
"name": "frontend", "name": "frontend",
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"@emotion/react": "^11.13.3", "@emotion/react": "^11.14.0",
"@emotion/styled": "^11.13.0", "@emotion/styled": "^11.14.0",
"@material-ui/core": "^4.12.4", "@material-ui/core": "^4.12.4",
"@material-ui/icons": "^4.11.3", "@material-ui/icons": "^4.11.3",
"@mui/icons-material": "^6.0.1", "@mui/icons-material": "^6.0.1",
"@mui/lab": "^6.0.0-beta.9", "@mui/lab": "^6.0.0-beta.9",
"@mui/material": "^6.0.1", "@mui/material": "^6.2.0",
"@testing-library/jest-dom": "^5.17.0", "@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
@@ -29,6 +29,7 @@
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"redux": "^5.0.1", "redux": "^5.0.1",
"redux-persist": "^6.0.0", "redux-persist": "^6.0.0",
"reselect": "^5.1.1",
"sass": "^1.80.3", "sass": "^1.80.3",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
}, },
@@ -2127,10 +2128,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@babel/runtime": { "node_modules/@babel/runtime": {
"version": "7.25.6", "version": "7.26.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz",
"integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==", "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==",
"license": "MIT",
"dependencies": { "dependencies": {
"regenerator-runtime": "^0.14.0" "regenerator-runtime": "^0.14.0"
}, },
@@ -2477,16 +2477,15 @@
} }
}, },
"node_modules/@emotion/babel-plugin": { "node_modules/@emotion/babel-plugin": {
"version": "11.12.0", "version": "11.13.5",
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
"integrity": "sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==", "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"@babel/helper-module-imports": "^7.16.7", "@babel/helper-module-imports": "^7.16.7",
"@babel/runtime": "^7.18.3", "@babel/runtime": "^7.18.3",
"@emotion/hash": "^0.9.2", "@emotion/hash": "^0.9.2",
"@emotion/memoize": "^0.9.0", "@emotion/memoize": "^0.9.0",
"@emotion/serialize": "^1.2.0", "@emotion/serialize": "^1.3.3",
"babel-plugin-macros": "^3.1.0", "babel-plugin-macros": "^3.1.0",
"convert-source-map": "^1.5.0", "convert-source-map": "^1.5.0",
"escape-string-regexp": "^4.0.0", "escape-string-regexp": "^4.0.0",
@@ -2498,20 +2497,17 @@
"node_modules/@emotion/babel-plugin/node_modules/@emotion/hash": { "node_modules/@emotion/babel-plugin/node_modules/@emotion/hash": {
"version": "0.9.2", "version": "0.9.2",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
"integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g=="
"license": "MIT"
}, },
"node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": {
"version": "1.9.0", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
"license": "MIT"
}, },
"node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": { "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"license": "MIT",
"engines": { "engines": {
"node": ">=10" "node": ">=10"
}, },
@@ -2523,20 +2519,18 @@
"version": "0.5.7", "version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
"license": "BSD-3-Clause",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/@emotion/cache": { "node_modules/@emotion/cache": {
"version": "11.13.1", "version": "11.14.0",
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.1.tgz", "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz",
"integrity": "sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==", "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==",
"license": "MIT",
"dependencies": { "dependencies": {
"@emotion/memoize": "^0.9.0", "@emotion/memoize": "^0.9.0",
"@emotion/sheet": "^1.4.0", "@emotion/sheet": "^1.4.0",
"@emotion/utils": "^1.4.0", "@emotion/utils": "^1.4.2",
"@emotion/weak-memoize": "^0.4.0", "@emotion/weak-memoize": "^0.4.0",
"stylis": "4.2.0" "stylis": "4.2.0"
} }
@@ -2563,17 +2557,16 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@emotion/react": { "node_modules/@emotion/react": {
"version": "11.13.3", "version": "11.14.0",
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.13.3.tgz", "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
"integrity": "sha512-lIsdU6JNrmYfJ5EbUCf4xW1ovy5wKQ2CkPRM4xogziOxH1nXxBSjpC9YqbFAP7circxMfYp+6x676BqWcEiixg==", "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
"license": "MIT",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.18.3", "@babel/runtime": "^7.18.3",
"@emotion/babel-plugin": "^11.12.0", "@emotion/babel-plugin": "^11.13.5",
"@emotion/cache": "^11.13.0", "@emotion/cache": "^11.14.0",
"@emotion/serialize": "^1.3.1", "@emotion/serialize": "^1.3.3",
"@emotion/use-insertion-effect-with-fallbacks": "^1.1.0", "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
"@emotion/utils": "^1.4.0", "@emotion/utils": "^1.4.2",
"@emotion/weak-memoize": "^0.4.0", "@emotion/weak-memoize": "^0.4.0",
"hoist-non-react-statics": "^3.3.1" "hoist-non-react-statics": "^3.3.1"
}, },
@@ -2587,23 +2580,21 @@
} }
}, },
"node_modules/@emotion/serialize": { "node_modules/@emotion/serialize": {
"version": "1.3.1", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.1.tgz", "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz",
"integrity": "sha512-dEPNKzBPU+vFPGa+z3axPRn8XVDetYORmDC0wAiej+TNcOZE70ZMJa0X7JdeoM6q/nWTMZeLpN/fTnD9o8MQBA==", "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==",
"license": "MIT",
"dependencies": { "dependencies": {
"@emotion/hash": "^0.9.2", "@emotion/hash": "^0.9.2",
"@emotion/memoize": "^0.9.0", "@emotion/memoize": "^0.9.0",
"@emotion/unitless": "^0.10.0", "@emotion/unitless": "^0.10.0",
"@emotion/utils": "^1.4.0", "@emotion/utils": "^1.4.2",
"csstype": "^3.0.2" "csstype": "^3.0.2"
} }
}, },
"node_modules/@emotion/serialize/node_modules/@emotion/hash": { "node_modules/@emotion/serialize/node_modules/@emotion/hash": {
"version": "0.9.2", "version": "0.9.2",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
"integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g=="
"license": "MIT"
}, },
"node_modules/@emotion/sheet": { "node_modules/@emotion/sheet": {
"version": "1.4.0", "version": "1.4.0",
@@ -2612,17 +2603,16 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@emotion/styled": { "node_modules/@emotion/styled": {
"version": "11.13.0", "version": "11.14.0",
"resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.13.0.tgz", "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz",
"integrity": "sha512-tkzkY7nQhW/zC4hztlwucpT8QEZ6eUzpXDRhww/Eej4tFfO0FxQYWRyg/c5CCXa4d/f174kqeXYjuQRnhzf6dA==", "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==",
"license": "MIT",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.18.3", "@babel/runtime": "^7.18.3",
"@emotion/babel-plugin": "^11.12.0", "@emotion/babel-plugin": "^11.13.5",
"@emotion/is-prop-valid": "^1.3.0", "@emotion/is-prop-valid": "^1.3.0",
"@emotion/serialize": "^1.3.0", "@emotion/serialize": "^1.3.3",
"@emotion/use-insertion-effect-with-fallbacks": "^1.1.0", "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
"@emotion/utils": "^1.4.0" "@emotion/utils": "^1.4.2"
}, },
"peerDependencies": { "peerDependencies": {
"@emotion/react": "^11.0.0-rc.0", "@emotion/react": "^11.0.0-rc.0",
@@ -2637,23 +2627,20 @@
"node_modules/@emotion/unitless": { "node_modules/@emotion/unitless": {
"version": "0.10.0", "version": "0.10.0",
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
"integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg=="
"license": "MIT"
}, },
"node_modules/@emotion/use-insertion-effect-with-fallbacks": { "node_modules/@emotion/use-insertion-effect-with-fallbacks": {
"version": "1.1.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz",
"integrity": "sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==", "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==",
"license": "MIT",
"peerDependencies": { "peerDependencies": {
"react": ">=16.8.0" "react": ">=16.8.0"
} }
}, },
"node_modules/@emotion/utils": { "node_modules/@emotion/utils": {
"version": "1.4.0", "version": "1.4.2",
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.0.tgz", "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
"integrity": "sha512-spEnrA1b6hDR/C68lC2M7m6ALPUHZC0lIY7jAS/B/9DuuO1ZP04eov8SMv/6fwRd8pzmsn2AuJEznRREWlQrlQ==", "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA=="
"license": "MIT"
}, },
"node_modules/@emotion/weak-memoize": { "node_modules/@emotion/weak-memoize": {
"version": "0.4.0", "version": "0.4.0",
@@ -3933,10 +3920,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@mui/core-downloads-tracker": { "node_modules/@mui/core-downloads-tracker": {
"version": "6.0.1", "version": "6.2.0",
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.0.1.tgz", "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.2.0.tgz",
"integrity": "sha512-TmKkCTwgtwvlFTF1tZzG4lYbi7v6NGweEJwFBZoIWZrkF1OLa0xu4umifmIyd+bVIScsEj//E2AD6bOJbPMOOQ==", "integrity": "sha512-Nn5PSkUqbDrvezpiiiYZiAbX4SFEiy3CbikUL6pWOXEUsq+L1j50OOyyUIHpaX2Hr+5V5UxTh+fPeC4nsGNhdw==",
"license": "MIT",
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
"url": "https://opencollective.com/mui-org" "url": "https://opencollective.com/mui-org"
@@ -4023,22 +4009,21 @@
} }
}, },
"node_modules/@mui/material": { "node_modules/@mui/material": {
"version": "6.0.1", "version": "6.2.0",
"resolved": "https://registry.npmjs.org/@mui/material/-/material-6.0.1.tgz", "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.2.0.tgz",
"integrity": "sha512-gOJS0RKYs9lRACaTluXPNopxFpIBhWVmhf09lHpqpPlR6bujXhuiTE2Q8puensdz3Qm2JGzl1VjccYHieV1g8A==", "integrity": "sha512-7FXXUPIyYzP02a7GvqwJ7ocmdP+FkvLvmy/uxG1TDmTlsr8nEClQp75uxiVznJqAY/jJy4d+Rj/fNWNxwidrYQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.25.0", "@babel/runtime": "^7.26.0",
"@mui/core-downloads-tracker": "^6.0.1", "@mui/core-downloads-tracker": "^6.2.0",
"@mui/system": "^6.0.1", "@mui/system": "^6.2.0",
"@mui/types": "^7.2.16", "@mui/types": "^7.2.19",
"@mui/utils": "^6.0.1", "@mui/utils": "^6.2.0",
"@popperjs/core": "^2.11.8", "@popperjs/core": "^2.11.8",
"@types/react-transition-group": "^4.4.11", "@types/react-transition-group": "^4.4.11",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"csstype": "^3.1.3", "csstype": "^3.1.3",
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
"react-is": "^18.3.1", "react-is": "^19.0.0",
"react-transition-group": "^4.4.5" "react-transition-group": "^4.4.5"
}, },
"engines": { "engines": {
@@ -4051,7 +4036,7 @@
"peerDependencies": { "peerDependencies": {
"@emotion/react": "^11.5.0", "@emotion/react": "^11.5.0",
"@emotion/styled": "^11.3.0", "@emotion/styled": "^11.3.0",
"@mui/material-pigment-css": "^6.0.1", "@mui/material-pigment-css": "^6.2.0",
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
@@ -4081,19 +4066,17 @@
} }
}, },
"node_modules/@mui/material/node_modules/react-is": { "node_modules/@mui/material/node_modules/react-is": {
"version": "18.3.1", "version": "19.0.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g=="
"license": "MIT"
}, },
"node_modules/@mui/private-theming": { "node_modules/@mui/private-theming": {
"version": "6.0.2", "version": "6.2.0",
"resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.0.2.tgz", "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.2.0.tgz",
"integrity": "sha512-emddFcRhA0hPGVIwIbW5g0V8vtCgw2g/H/A7jTdGe7dpCWEPpp6jPIXRRKcEUWgmg91R6rBNfV+LFHxBxmZXOQ==", "integrity": "sha512-lYd2MrVddhentF1d/cMXKnwlDjr/shbO3A2eGq22PCYUoZaqtAGZMc0U86KnJ/Sh5YzNYePqTOaaowAN8Qea8A==",
"license": "MIT",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.25.0", "@babel/runtime": "^7.26.0",
"@mui/utils": "^6.0.2", "@mui/utils": "^6.2.0",
"prop-types": "^15.8.1" "prop-types": "^15.8.1"
}, },
"engines": { "engines": {
@@ -4114,13 +4097,14 @@
} }
}, },
"node_modules/@mui/styled-engine": { "node_modules/@mui/styled-engine": {
"version": "6.0.2", "version": "6.2.0",
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.0.2.tgz", "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.2.0.tgz",
"integrity": "sha512-qd3Vlhted0SYVGotnCfVNcxff7vW2WN0fclbAexff60NeNS1qs/H/CImHEHUBiUGeNWMPRochbN6VF1arQ7/jA==", "integrity": "sha512-rV4YCu6kcCjMnHFXU/tQcL6XfYVfFVR8n3ZVNGnk2rpXnt/ctOPJsF+eUQuhkHciueLVKpI06+umr1FxWWhVmQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.25.0", "@babel/runtime": "^7.26.0",
"@emotion/cache": "^11.13.1", "@emotion/cache": "^11.13.5",
"@emotion/serialize": "^1.3.3",
"@emotion/sheet": "^1.4.0",
"csstype": "^3.1.3", "csstype": "^3.1.3",
"prop-types": "^15.8.1" "prop-types": "^15.8.1"
}, },
@@ -4146,16 +4130,15 @@
} }
}, },
"node_modules/@mui/system": { "node_modules/@mui/system": {
"version": "6.0.2", "version": "6.2.0",
"resolved": "https://registry.npmjs.org/@mui/system/-/system-6.0.2.tgz", "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.2.0.tgz",
"integrity": "sha512-AZv1/C4PuHgWFTA8YraIzl3FTVLdRz0RIMRwEADWZBdIhnuTHS/4+r8qE9+3CcpTHg1WsEu8btaO3AhQahSM9A==", "integrity": "sha512-DCeqev9Cd4f4pm3O1lqSGW/DIHHBG6ZpqMX9iIAvN4asYv+pPWv2/lKov9kWk5XThhxFnGSv93SRNE1kNRRg5Q==",
"license": "MIT",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.25.0", "@babel/runtime": "^7.26.0",
"@mui/private-theming": "^6.0.2", "@mui/private-theming": "^6.2.0",
"@mui/styled-engine": "^6.0.2", "@mui/styled-engine": "^6.2.0",
"@mui/types": "^7.2.16", "@mui/types": "^7.2.19",
"@mui/utils": "^6.0.2", "@mui/utils": "^6.2.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"csstype": "^3.1.3", "csstype": "^3.1.3",
"prop-types": "^15.8.1" "prop-types": "^15.8.1"
@@ -4195,10 +4178,9 @@
} }
}, },
"node_modules/@mui/types": { "node_modules/@mui/types": {
"version": "7.2.16", "version": "7.2.19",
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.16.tgz", "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.19.tgz",
"integrity": "sha512-qI8TV3M7ShITEEc8Ih15A2vLzZGLhD+/UPNwck/hcls2gwg7dyRjNGXcQYHKLB5Q7PuTRfrTkAoPa2VV1s67Ag==", "integrity": "sha512-6XpZEM/Q3epK9RN8ENoXuygnqUQxE+siN/6rGRi2iwJPgBUR25mphYQ9ZI87plGh58YoZ5pp40bFvKYOCDJ3tA==",
"license": "MIT",
"peerDependencies": { "peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
}, },
@@ -4209,17 +4191,16 @@
} }
}, },
"node_modules/@mui/utils": { "node_modules/@mui/utils": {
"version": "6.0.2", "version": "6.2.0",
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.0.2.tgz", "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.2.0.tgz",
"integrity": "sha512-TeFrYsxcmeoDSlkoPhX+LjIuuqC5Pyj+xz2kRceKCkUpwMNTEeVOfowXDPe+mboZwmpJ5ZxP4eiAgQMdeEasjg==", "integrity": "sha512-77CaFJi+OIi2SjbPwCis8z5DXvE0dfx9hBz5FguZHt1VYFlWEPCWTHcMsQCahSErnfik5ebLsYK8+D+nsjGVfw==",
"license": "MIT",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.25.0", "@babel/runtime": "^7.26.0",
"@mui/types": "^7.2.16", "@mui/types": "^7.2.19",
"@types/prop-types": "^15.7.12", "@types/prop-types": "^15.7.14",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
"react-is": "^18.3.1" "react-is": "^19.0.0"
}, },
"engines": { "engines": {
"node": ">=14.0.0" "node": ">=14.0.0"
@@ -4248,10 +4229,9 @@
} }
}, },
"node_modules/@mui/utils/node_modules/react-is": { "node_modules/@mui/utils/node_modules/react-is": {
"version": "18.3.1", "version": "19.0.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g=="
"license": "MIT"
}, },
"node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
"version": "5.1.1-v1", "version": "5.1.1-v1",
@@ -5726,10 +5706,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/prop-types": { "node_modules/@types/prop-types": {
"version": "15.7.12", "version": "15.7.14",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz",
"integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ=="
"license": "MIT"
}, },
"node_modules/@types/q": { "node_modules/@types/q": {
"version": "1.5.8", "version": "1.5.8",
@@ -10311,8 +10290,7 @@
"node_modules/find-root": { "node_modules/find-root": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
"integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
"license": "MIT"
}, },
"node_modules/find-up": { "node_modules/find-up": {
"version": "4.1.0", "version": "4.1.0",
@@ -18016,6 +17994,11 @@
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/reselect": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
"integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="
},
"node_modules/resolve": { "node_modules/resolve": {
"version": "1.22.8", "version": "1.22.8",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",

View File

@@ -3,13 +3,13 @@
"version": "0.1.0", "version": "0.1.0",
"homepage": "https://hugyouu.github.io/Letterboxd-Diary-Posters-Picker", "homepage": "https://hugyouu.github.io/Letterboxd-Diary-Posters-Picker",
"dependencies": { "dependencies": {
"@emotion/react": "^11.13.3", "@emotion/react": "^11.14.0",
"@emotion/styled": "^11.13.0", "@emotion/styled": "^11.14.0",
"@material-ui/core": "^4.12.4", "@material-ui/core": "^4.12.4",
"@material-ui/icons": "^4.11.3", "@material-ui/icons": "^4.11.3",
"@mui/icons-material": "^6.0.1", "@mui/icons-material": "^6.0.1",
"@mui/lab": "^6.0.0-beta.9", "@mui/lab": "^6.0.0-beta.9",
"@mui/material": "^6.0.1", "@mui/material": "^6.2.0",
"@testing-library/jest-dom": "^5.17.0", "@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
@@ -24,6 +24,7 @@
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"redux": "^5.0.1", "redux": "^5.0.1",
"redux-persist": "^6.0.0", "redux-persist": "^6.0.0",
"reselect": "^5.1.1",
"sass": "^1.80.3", "sass": "^1.80.3",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
}, },

View File

@@ -1,8 +1,7 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { useSelector, useDispatch } from "react-redux"; import { useSelector, useDispatch } from "react-redux";
import { removePoster } from "../services/action"; import {removeAllPosters, removePoster} from "../services/action";
import DeleteIcon from "@material-ui/icons/Delete"; import DeleteIcon from "@material-ui/icons/Delete";
import { Camera } from "lucide-react";
const apiUrl = process.env.REACT_APP_API_URL; const apiUrl = process.env.REACT_APP_API_URL;
@@ -11,16 +10,24 @@ const Cart = () => {
const selectedPosters = useSelector((state) => { const selectedPosters = useSelector((state) => {
const selections = state.posterSelections; const selections = state.posterSelections;
return Object.entries(selections).flatMap(([movieId, posters]) => return Object.entries(selections).flatMap(([movieId, posters]) =>
posters.map((posterId) => ({ movieId, posterId })) posters.map((poster) => ({
movieId,
posterId: typeof poster === "string" ? poster : poster.posterId,
}))
); );
}); });
const [downloadFormat, setDownloadFormat] = useState("zip"); const [downloadFormat, setDownloadFormat] = useState("zip");
const handleRemovePoster = (movieId, posterId) => { const handleRemovePoster = (movieId, posterId) => {
dispatch(removePoster(movieId, posterId)); if (movieId && posterId) dispatch(removePoster(movieId, posterId));
}; };
const handleRemoveAllPosters = () => {
dispatch(removeAllPosters());
};
const handleDownload = async () => { const handleDownload = async () => {
try { try {
const response = await fetch(`${apiUrl}/api/download-posters`, { const response = await fetch(`${apiUrl}/api/download-posters`, {
@@ -55,72 +62,62 @@ const Cart = () => {
} }
}; };
const totalSlots = 4;
const posterSlots = [...Array(totalSlots)].map((_, index) => {
if (index < selectedPosters.length) {
return selectedPosters[index];
}
return null;
});
return ( return (
<div className="cart-container"> <div className="cart-container">
<div className="poster-grid"> <div className="poster-grid">
{posterSlots.map((poster, index) => ( {selectedPosters.length === 0 ? (
<div <p>No posters selected</p>
key={ ) : (
poster ? `${poster.movieId}-${poster.posterId}` : `empty-${index}` selectedPosters.map((poster) => (
} <div
className="poster-card" key={`${poster.movieId}-${poster.posterId}`}
> className="poster-card"
{poster ? (
<>
<img
src={`https://image.tmdb.org/t/p/w500${poster.posterId}`}
alt="Movie poster"
className="poster-image"
/>
<div className="delete-overlay">
<button
className="delete-button"
onClick={() =>
handleRemovePoster(poster.movieId, poster.posterId)
}
> >
<DeleteIcon /> <img
</button> src={`https://image.tmdb.org/t/p/original${poster.posterId}`}
</div> alt="Movie poster"
</> className="poster-image"
) : ( />
<div className="empty-poster"> <div className="delete-overlay">
<Camera size={48} /> <button
<p>Emplacement disponible</p> className="delete-button"
</div> onClick={() => handleRemovePoster(poster.movieId, poster.posterId)}
)} >
</div> <DeleteIcon/>
))} </button>
</div> </div>
</div>
))
)}
</div>
<div className="action-panel"> <div className="action-panel">
<h2>Download Options</h2> <h2>Download Options</h2>
<select <select
value={downloadFormat} value={downloadFormat}
onChange={(e) => setDownloadFormat(e.target.value)} onChange={(e) => setDownloadFormat(e.target.value)}
className="format-select" className="format-select"
> >
<option value="zip">ZIP</option> <option value="zip">ZIP</option>
<option value="tar">TAR</option> <option value="tar">TAR</option>
<option value="7z">7Z</option> <option value="7z">7Z</option>
</select> </select>
<button <button
className="download-button" className="download-button"
onClick={handleDownload} onClick={handleDownload}
disabled={selectedPosters.length === 0} disabled={selectedPosters.length === 0}
> >
Download Selected Download Selected
</button> </button>
<button
className="clear-button"
onClick={handleRemoveAllPosters}
disabled={selectedPosters.length === 0}
>
Clear All Posters
</button>
</div>
</div> </div>
</div>
); );
}; };

View File

@@ -1,17 +1,20 @@
import React from "react"; import React, { useMemo } from "react";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { AppBar, Toolbar, IconButton, Badge, Button } from '@material-ui/core'; import { AppBar, Toolbar, IconButton, Badge, Button } from '@material-ui/core';
import ShoppingCartIcon from '@material-ui/icons/ShoppingCart'; import ShoppingCartIcon from '@material-ui/icons/ShoppingCart';
import ArrowBackIcon from '@material-ui/icons/ArrowBack'; import ArrowBackIcon from '@material-ui/icons/ArrowBack';
import { useNavigate, useLocation } from 'react-router-dom'; import { useNavigate, useLocation } from 'react-router-dom';
const selectPosterSelections = (state) => state.posterSelections;
const NavBar = ({ onRefreshFiles }) => { const NavBar = ({ onRefreshFiles }) => {
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
const selectedPosters = useSelector((state) => {
const selections = state.posterSelections; const posterSelections = useSelector(selectPosterSelections);
return Object.values(selections).flat(); const selectedPosters = useMemo(() => {
}); return Object.values(posterSelections).flat();
}, [posterSelections]);
const totalSelected = selectedPosters.length; const totalSelected = selectedPosters.length;
@@ -29,20 +32,39 @@ const NavBar = ({ onRefreshFiles }) => {
<AppBar position="fixed" className="navbar"> <AppBar position="fixed" className="navbar">
<Toolbar className="toolbar"> <Toolbar className="toolbar">
{isPosterSelectorPage ? ( {isPosterSelectorPage ? (
<IconButton edge="start" className="back-button" onClick={handleBack}> <IconButton
edge="start"
className="back-button"
onClick={handleBack}
>
<ArrowBackIcon /> <ArrowBackIcon />
</IconButton> </IconButton>
) : ( ) : (
<IconButton edge="start" className="back-button" disabled> <IconButton
{/* Un IconButton vide et désactivé */} edge="start"
className="back-button"
disabled
>
{/* Disabled IconButton */}
</IconButton> </IconButton>
)} )}
<div> <div>
<Button className="refresh-button" onClick={onRefreshFiles}> <Button
className="refresh-button"
onClick={onRefreshFiles}
>
Refresh Files Refresh Files
</Button> </Button>
<IconButton edge="end" className="cart-button" onClick={handleCart}> <IconButton
<Badge badgeContent={totalSelected} color="secondary"> edge="end"
className="cart-button"
onClick={handleCart}
>
<Badge
badgeContent={totalSelected}
color="secondary"
overlap="rectangular"
>
<ShoppingCartIcon /> <ShoppingCartIcon />
</Badge> </Badge>
</IconButton> </IconButton>

View File

@@ -11,18 +11,24 @@ import {
CardMedia, CardMedia,
CircularProgress, CircularProgress,
Box, Box,
Paper, Paper, DialogContent, DialogTitle, DialogActions, Button,
} from "@material-ui/core"; } from "@material-ui/core";
import CheckIcon from "@material-ui/icons/Check"; import CheckIcon from "@material-ui/icons/Check";
import NavBar from "./NavBar"; import NavBar from "./NavBar";
import {Dialog} from "@mui/material";
const apiUrl = process.env.REACT_APP_API_URL; const apiUrl = process.env.REACT_APP_API_URL;
const PosterGallery = ({ movieId }) => { const PosterGallery = () => {
const { movieName, movieYear } = useParams();
const [posters, setPosters] = useState([]); const [posters, setPosters] = useState([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const { movieName, movieYear } = useParams(); const [error, setError] = useState(null);
const [dialogOpen, setDialogOpen] = useState(false);
const dispatch = useDispatch(); const dispatch = useDispatch();
const movieId = `${movieName}-${movieYear}`;
const selectedPosters = useSelector( const selectedPosters = useSelector(
(state) => state.posterSelections[movieId] || [] (state) => state.posterSelections[movieId] || []
); );
@@ -31,10 +37,20 @@ const PosterGallery = ({ movieId }) => {
const fetchPosters = async () => { const fetchPosters = async () => {
try { try {
setLoading(true); setLoading(true);
setError(null);
const encodedName = encodeURIComponent(movieName);
const encodedYear = encodeURIComponent(movieYear);
const response = await axios.get( const response = await axios.get(
`${apiUrl}/api/posters/${movieName}/${movieYear}` `${apiUrl}/api/posters/${encodedName}/${encodedYear}`
); );
setPosters(response.data.posters);
if (response.data && response.data.posters) {
setPosters(response.data.posters);
} else {
throw new Error("No posters data in response");
}
} catch (error) { } catch (error) {
console.error("Error fetching posters:", error); console.error("Error fetching posters:", error);
} finally { } finally {
@@ -42,17 +58,46 @@ const PosterGallery = ({ movieId }) => {
} }
}; };
fetchPosters(); if (movieName && movieYear) {
fetchPosters();
}
}, [movieName, movieYear]); }, [movieName, movieYear]);
const handlePosterSelect = (posterId) => { const handlePosterSelect = (posterId) => {
if (selectedPosters.includes(posterId)) { const postersForCurrentMovie = selectedPosters.filter(
dispatch(deselectPoster(movieId, posterId)); (poster) => poster.movieId !== movieId
);
const isPosterSelected = postersForCurrentMovie.some((poster) => poster.posterId === posterId)
if (postersForCurrentMovie.length > 0 && !isPosterSelected) {
setDialogOpen(true);
return;
}
if (isPosterSelected) {
dispatch(deselectPoster(movieName, movieYear, posterId));
} else { } else {
dispatch(selectPoster(movieId, posterId)); dispatch(selectPoster(movieName, movieYear, posterId));
} }
}; };
const handleDialogClose = () => {
setDialogOpen(false);
}
if (error) {
return (
<Container>
<Paper elevation={3} className="content-paper">
<Typography color="error" align="center">
{error}
</Typography>
</Paper>
<NavBar />
</Container>
);
}
return ( return (
<> <>
<Container className="poster-gallery"> <Container className="poster-gallery">
@@ -64,7 +109,7 @@ const PosterGallery = ({ movieId }) => {
<Box className="loading-container"> <Box className="loading-container">
<CircularProgress /> <CircularProgress />
</Box> </Box>
) : ( ) : posters.length > 0 ? (
<Grid container spacing={3}> <Grid container spacing={3}>
{posters.map((poster, index) => ( {posters.map((poster, index) => (
<Grid item xs={12} sm={6} md={4} lg={3} key={index}> <Grid item xs={12} sm={6} md={4} lg={3} key={index}>
@@ -78,7 +123,7 @@ const PosterGallery = ({ movieId }) => {
> >
<CardMedia <CardMedia
className="poster-image" className="poster-image"
image={`https://image.tmdb.org/t/p/w500${poster.file_path}`} image={`https://image.tmdb.org/t/p/original${poster.file_path}`}
title={`${movieName} poster ${index + 1}`} title={`${movieName} poster ${index + 1}`}
/> />
{selectedPosters.includes(poster.file_path) && ( {selectedPosters.includes(poster.file_path) && (
@@ -88,10 +133,27 @@ const PosterGallery = ({ movieId }) => {
</Grid> </Grid>
))} ))}
</Grid> </Grid>
) : (
<Typography align="center">
No posters found for this movie.
</Typography>
)} )}
</Paper> </Paper>
</Container> </Container>
<NavBar /> <NavBar />
<Dialog open={dialogOpen} onClose={handleDialogClose}>
<DialogTitle>Poster already selected</DialogTitle>
<DialogContent>
<Typography>
You can only select one poster per movie. Please deselect the current poster first.
</Typography>
</DialogContent>
<DialogActions>
<Button onClick={handleDialogClose} color="primary">
OK
</Button>
</DialogActions>
</Dialog>
</> </>
); );
}; };

View File

@@ -109,9 +109,7 @@ const PosterSelector = () => {
const handleMovieClick = (movieName, movieYear) => { const handleMovieClick = (movieName, movieYear) => {
navigate( navigate(
`/posters/${encodeURIComponent(movieName)}/${encodeURIComponent( `/posters/${encodeURIComponent(movieName)}/${encodeURIComponent(movieYear)}`
movieYear
)}`
); );
}; };

View File

@@ -1,17 +1,32 @@
export const selectPoster = (movieId, posterId) => ({ export const SELECT_POSTER = "SELECT_POSTER";
type: "SELECT_POSTER", export const DESELECT_POSTER = "DESELECT_POSTER";
movieId, export const REMOVE_POSTER = "REMOVE_POSTER";
posterId,
}); export const REMOVE_ALL_POSTERS = "REMOVE_ALL_POSTERS";
export const selectPoster = (movieName, movieYear, posterId) => {
const movieId = `${movieName}-${movieYear}`;
return {
type: SELECT_POSTER,
payload: { movieId, posterId },
};
};
export const deselectPoster = (movieName, movieYear, posterId) => {
const movieId = `${movieName}-${movieYear}`;
return {
type: DESELECT_POSTER,
payload: { movieId, posterId },
};
};
export const deselectPoster = (movieId, posterId) => ({
type: "DESELECT_POSTER",
movieId,
posterId,
});
export const removePoster = (movieId, posterId) => ({ export const removePoster = (movieId, posterId) => ({
type: "REMOVE_POSTER", type: "REMOVE_POSTER",
movieId, movieId,
posterId, posterId,
}); });
export const removeAllPosters = () => ({
type: REMOVE_ALL_POSTERS,
});

View File

@@ -1,30 +1,40 @@
import { legacy_createStore as createStore, combineReducers } from "redux"; import { legacy_createStore as createStore, combineReducers } from "redux";
import { persistStore, persistReducer } from "redux-persist"; import { persistStore, persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage"; import storage from "redux-persist/lib/storage";
import {DESELECT_POSTER, REMOVE_ALL_POSTERS, REMOVE_POSTER, SELECT_POSTER} from "./action";
const posterSelectionReducer = (state = {}, action) => { const posterSelectionReducer = (state = {}, action) => {
switch (action.type) { switch (action.type) {
case "SELECT_POSTER": case SELECT_POSTER: {
return { const { movieId, posterId } = action.payload;
...state, return {
[action.movieId]: [...(state[action.movieId] || []), action.posterId], ...state,
}; [movieId]: [...(state[movieId] || []), posterId],
case "DESELECT_POSTER": };
return { }
...state, case DESELECT_POSTER: {
[action.movieId]: state[action.movieId].filter( const { movieId, posterId } = action.payload;
(id) => id !== action.posterId return {
), ...state,
}; [movieId]: state[movieId]?.filter(
case "REMOVE_POSTER": (id) => id !== posterId
return { ),
...state, };
[action.movieId]: state[action.movieId].filter( }
(id) => id !== action.posterId case REMOVE_POSTER: {
), const { movieId, posterId } = action;
}; return {
default: ...state,
return state; [movieId]: state[movieId]?.filter(
(id) => id !== posterId
),
};
}
case REMOVE_ALL_POSTERS: {
return {};
}
default:
return state;
} }
}; };

View File

@@ -10,14 +10,11 @@
grid-template-columns: repeat(4, 1fr); grid-template-columns: repeat(4, 1fr);
gap: 1rem; gap: 1rem;
max-height: calc(100vh - 4rem); max-height: calc(100vh - 4rem);
overflow-y: auto;
padding-right: 1rem; padding-right: 1rem;
.poster-card { .poster-card {
position: relative; position: relative;
aspect-ratio: 2/3; aspect-ratio: 2/3;
border-radius: 8px;
overflow: hidden;
&:hover .delete-overlay { &:hover .delete-overlay {
opacity: 1; opacity: 1;
@@ -26,7 +23,6 @@
.poster-image { .poster-image {
width: 100%; width: 100%;
height: 100%; height: 100%;
object-fit: cover;
} }
.delete-overlay { .delete-overlay {

View File

@@ -35,6 +35,7 @@ $hover-color: rgba(102, 119, 136, 0.2);
} }
.movie-item { .movie-item {
padding: 0 2rem;
margin-bottom: 1rem; margin-bottom: 1rem;
transition: background-color 0.3s ease; transition: background-color 0.3s ease;
display: flex; display: flex;