From 0c5b2b4bbcf841e6756dcaa05e5429f6be113ec2 Mon Sep 17 00:00:00 2001 From: Pierret Hugo Date: Mon, 28 Oct 2024 14:58:14 +0100 Subject: [PATCH] Change Cart disposition --- .github/workflows/node.js.yml | 2 +- package-lock.json | 9 + package.json | 1 + src/components/Cart.js | 264 +++++++-------------------- src/components/NavBar.js | 37 +--- src/components/PosterSelector.js | 2 +- src/components/UploadDiary.js | 2 +- src/styles/App.scss | 1 + src/styles/scss/_cart.scss | 201 +++++++------------- src/styles/scss/_navbar.scss | 32 ++++ src/styles/scss/_posterSelector.scss | 1 + 11 files changed, 195 insertions(+), 357 deletions(-) create mode 100644 src/styles/scss/_navbar.scss diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 053baaa..d56143c 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -33,7 +33,7 @@ jobs: run: npm run build --if-present env: CI: false - REACT_APP_APIUrl: ${{ secrets.REACT_APP_APIUrl }} + REACT_APP_API_URL: ${{ secrets.REACT_APP_API_URL }} - run: npm test -- --passWithNoTests diff --git a/package-lock.json b/package-lock.json index 9311d49..d5a58c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "axios": "^1.7.6", + "lucide-react": "^0.453.0", "material-ui-dropzone": "^3.5.0", "query-string": "^9.1.0", "react": "^18.3.1", @@ -14800,6 +14801,14 @@ "yallist": "^3.0.2" } }, + "node_modules/lucide-react": { + "version": "0.453.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.453.0.tgz", + "integrity": "sha512-kL+RGZCcJi9BvJtzg2kshO192Ddy9hv3ij+cPrVPWSRzgCWCVazoQJxOjAwgK53NomL07HB7GPHW120FimjNhQ==", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" + } + }, "node_modules/lz-string": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", diff --git a/package.json b/package.json index 62c80fa..da66acb 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "axios": "^1.7.6", + "lucide-react": "^0.453.0", "material-ui-dropzone": "^3.5.0", "query-string": "^9.1.0", "react": "^18.3.1", diff --git a/src/components/Cart.js b/src/components/Cart.js index ed82fda..40c733c 100644 --- a/src/components/Cart.js +++ b/src/components/Cart.js @@ -1,224 +1,102 @@ -import React, { useState } from "react"; -import { useSelector, useDispatch } from "react-redux"; -import { - Container, - Typography, - Card, - CardMedia, - CardContent, - Button, - Checkbox, - FormControlLabel, - TextField, - Dialog, - DialogTitle, - DialogContent, - DialogActions, - Paper, -} from "@material-ui/core"; -import DeleteIcon from "@material-ui/icons/Delete"; -import GetAppIcon from "@material-ui/icons/GetApp"; -import ShareIcon from "@material-ui/icons/Share"; -import EditIcon from "@material-ui/icons/Edit"; -import { removePoster } from "../services/action"; -import axios from "axios"; +import React, { useState } from 'react'; +import { useSelector, useDispatch } from 'react-redux'; +import { removePoster } from '../services/action'; +import DeleteIcon from '@material-ui/icons/Delete'; + +const apiUrl = process.env.REACT_APP_API_URL; const Cart = () => { const dispatch = useDispatch(); const selectedPosters = useSelector((state) => { const selections = state.posterSelections; return Object.entries(selections).flatMap(([movieId, posters]) => - posters.map((posterId) => ({ movieId, posterId })) + posters.map((posterId) => ({ movieId, posterId })) ); }); - const [selectedForDownload, setSelectedForDownload] = useState( - selectedPosters.map(() => true) - ); - const [downloadFormat, setDownloadFormat] = useState("zip"); - const [renameDialogOpen, setRenameDialogOpen] = useState(false); - const [currentPosterIndex, setCurrentPosterIndex] = useState(null); - const [newPosterName, setNewPosterName] = useState(""); + const [downloadFormat, setDownloadFormat] = useState('zip'); - const handleRemovePoster = (index) => { - dispatch( - removePoster( - selectedPosters[index].movieId, - selectedPosters[index].posterId - ) - ); + const handleRemovePoster = (movieId, posterId) => { + dispatch(removePoster(movieId, posterId)); }; const handleDownload = async () => { - const postersToDownload = selectedPosters - .filter((_, index) => selectedForDownload[index]) - .map((poster) => ({ - movieId: poster.movieId, - posterId: poster.posterId, - movieName: poster.movieName, - movieYear: poster.movieYear, - })); - - console.log("Downloading posters:", postersToDownload); - try { - const response = await axios.post( - "http://localhost:5000/api/download-posters", - { - posters: postersToDownload, - format: downloadFormat, + const response = await fetch(`${apiUrl}/api/download-posters`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', }, - { - responseType: "blob", - } - ); - - const blob = new Blob([response.data], { - type: response.headers["content-type"], + body: JSON.stringify({ + posters: selectedPosters, + format: downloadFormat, + }), }); - const link = document.createElement("a"); - link.href = window.URL.createObjectURL(blob); - link.download = `posters.${downloadFormat}`; - link.click(); + + if (!response.ok) throw new Error('Download failed'); + + const contentType = response.headers.get('content-type'); + if (contentType && contentType.includes('application/zip')) { + const blob = await response.blob(); + const url = window.URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = 'posters.zip'; + document.body.appendChild(link); + link.click(); + link.remove(); + window.URL.revokeObjectURL(url); + } else { + throw new Error('Invalid response format'); + } } catch (error) { - console.error("Failed to download posters:", error); + console.error('Failed to download posters:', error); } }; - const handleShare = () => { - console.log("Sharing selected posters"); - }; - - const handleRename = (index) => { - setCurrentPosterIndex(index); - setNewPosterName(selectedPosters[index].posterId); - setRenameDialogOpen(true); - }; - - const handleRenameConfirm = () => { - console.log(`Renaming poster ${currentPosterIndex} to ${newPosterName}`); - setRenameDialogOpen(false); - }; - return ( - -
- - Your Cart - - {selectedPosters.map((poster, index) => ( - - - - - {`Movie ID: ${poster.movieId}`} - - - {poster.posterId} - - { - const newSelected = [...selectedForDownload]; - newSelected[index] = e.target.checked; - setSelectedForDownload(newSelected); - }} - className="checkbox" - /> - } - label="Select for download" - /> - - - - - ))} -
- -
- - Download Format - - setDownloadFormat(e.target.value)} - SelectProps={{ - native: true, - }} - className="form-control" +
+
+ {selectedPosters.map((poster) => ( +
+ Movie poster +
+ +
+
+ ))} +
+ +
+

Download Options

+ + - +
- - setRenameDialogOpen(false)} - className="rename-dialog" - > - Rename Poster - - setNewPosterName(e.target.value)} - /> - - - - - - - +
); }; -export default Cart; +export default Cart; \ No newline at end of file diff --git a/src/components/NavBar.js b/src/components/NavBar.js index d86c5b7..1e3350a 100644 --- a/src/components/NavBar.js +++ b/src/components/NavBar.js @@ -1,34 +1,11 @@ import React from "react"; import { useSelector } from "react-redux"; import { AppBar, Toolbar, IconButton, Badge, Button } from '@material-ui/core'; -import { makeStyles } from '@material-ui/core/styles'; import ShoppingCartIcon from '@material-ui/icons/ShoppingCart'; import ArrowBackIcon from '@material-ui/icons/ArrowBack'; import { useNavigate, useLocation } from 'react-router-dom'; -const useStyles = makeStyles((theme) => ({ - appBar: { - top: 'auto', - bottom: 0, - backgroundColor: '#1c1f23', - }, - toolbar: { - justifyContent: 'space-between', - }, - backButton: { - color: 'white', - }, - cartButton: { - color: 'white', - }, - refreshButton: { - color: 'white', - marginRight: theme.spacing(2), - }, -})); - -const NavBar = ({onRefreshFiles}) => { - const classes = useStyles(); +const NavBar = ({ onRefreshFiles }) => { const navigate = useNavigate(); const location = useLocation(); const selectedPosters = useSelector((state) => { @@ -49,22 +26,22 @@ const NavBar = ({onRefreshFiles}) => { const isPosterSelectorPage = location.pathname !== '/PosterSelector'; return ( - - + + {isPosterSelectorPage ? ( - + ) : ( - + {/* Un IconButton vide et désactivé */} )}
- - + diff --git a/src/components/PosterSelector.js b/src/components/PosterSelector.js index b024815..ca1e028 100644 --- a/src/components/PosterSelector.js +++ b/src/components/PosterSelector.js @@ -18,7 +18,7 @@ import queryString from "query-string"; import pulpGif from "../static/images/pulp.gif"; import NavBar from "./NavBar"; -const apiUrl = process.env.REACT_APP_APIUrl; +const apiUrl = process.env.REACT_APP_API_URL; const paginationTheme = createTheme({ palette: { diff --git a/src/components/UploadDiary.js b/src/components/UploadDiary.js index 79497e1..0899f90 100644 --- a/src/components/UploadDiary.js +++ b/src/components/UploadDiary.js @@ -13,7 +13,7 @@ import CloudDoneOutlinedIcon from "@mui/icons-material/CloudDoneOutlined"; import { useDropzone } from "react-dropzone"; import axios from "axios"; -const apiUrl = process.env.REACT_APP_APIUrl; +const apiUrl = process.env.REACT_APP_API_URL; const UploadDiary = () => { const [file, setFile] = useState(null); diff --git a/src/styles/App.scss b/src/styles/App.scss index 18a6c31..632a0ff 100644 --- a/src/styles/App.scss +++ b/src/styles/App.scss @@ -2,6 +2,7 @@ @use "./scss/posterSelector"; @use "./scss/posterGallery"; @use "./scss/cart"; +@use "./scss/navbar"; .App { text-align: center; diff --git a/src/styles/scss/_cart.scss b/src/styles/scss/_cart.scss index f12f1b0..fa868fb 100644 --- a/src/styles/scss/_cart.scss +++ b/src/styles/scss/_cart.scss @@ -1,90 +1,55 @@ -// Variables -$primary-color: #00a346; -$secondary-color: #3498db; -$background-dark: #2c3e50; -$background-light: #34495e; -$text-white: #ecf0f1; -$text-muted: #95a5a6; -$hover-scale: 1.05; -$gap-spacing: 1rem; - +// _cart.scss .cart-container { - margin-top: 2rem; - margin-bottom: 4rem; + padding: 2rem; display: flex; - gap: $gap-spacing; + gap: 2rem; - .poster-list { - flex: 2; - margin-right: 2rem; - max-height: calc(100vh - 200px); + .poster-grid { + flex: 1; + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 1rem; + max-height: calc(100vh - 4rem); overflow-y: auto; - - .cart-title { - color: $text-white; - margin-bottom: 1rem; - font-size: 2rem; - font-weight: 400; - } + padding-right: 1rem; .poster-card { - display: flex; - margin-bottom: 1rem; - background-color: $background-dark; - border-radius: 4px; + position: relative; + aspect-ratio: 2/3; + border-radius: 8px; overflow: hidden; - .poster-media { - width: 120px; - height: 180px; - object-fit: cover; - border-radius: 4px 0 0 4px; + &:hover .delete-overlay { + opacity: 1; } - .poster-content { - flex-grow: 1; - padding: 1rem; + .poster-image { + width: 100%; + height: 100%; + object-fit: cover; + } + + .delete-overlay { + position: absolute; + inset: 0; + background-color: rgba(0, 0, 0, 0.7); display: flex; - flex-direction: column; - justify-content: space-between; + align-items: center; + justify-content: center; + opacity: 0; + transition: opacity 0.2s ease; - .poster-title { - color: $text-white; - font-size: 1.25rem; - margin-bottom: 0.5rem; - } + .delete-button { + background-color: #e74c3c; + border: none; + color: white; + padding: 0.5rem; + border-radius: 50%; + cursor: pointer; + transition: transform 0.2s ease; - .poster-id { - color: $text-muted; - font-size: 1rem; - margin-bottom: 1rem; - } - - .poster-actions { - display: flex; - justify-content: space-between; - gap: 0.5rem; - - .action-button { - flex-grow: 1; - display: flex; - align-items: center; - justify-content: center; - padding: 0.5rem; - border-radius: 4px; - background-color: transparent; - border: 1px solid $text-muted; - color: $text-white; - transition: background-color 0.3s ease; - - &:hover { - background-color: $background-light; - cursor: pointer; - } - - &:not(:last-child) { - margin-right: 0.5rem; - } + &:hover { + transform: scale(1.1); } } } @@ -92,75 +57,49 @@ $gap-spacing: 1rem; } .action-panel { - flex: 1; + width: 300px; position: sticky; top: 2rem; - height: fit-content; - padding: 1.5rem; - background-color: $background-light; + background-color: #34495e; border-radius: 8px; + padding: 1.5rem; + height: fit-content; - .action-title { - color: $text-white; - margin-bottom: 1rem; - font-size: 1.5rem; + h2 { + color: white; + margin-bottom: 1.5rem; + font-size: 1.25rem; } - .form-control { + .format-select { width: 100%; margin-bottom: 1.5rem; - color: $text-white; - - select { - background-color: $background-dark; - color: $text-white; - padding: 0.5rem; - border: 1px solid $text-muted; - border-radius: 4px; - } + padding: 0.5rem; + background-color: #2c3e50; + border: 1px solid #95a5a6; + color: white; + border-radius: 4px; } - .action-buttons { - display: flex; - flex-direction: column; - gap: 1rem; + .download-button { + width: 100%; + padding: 0.75rem; + background-color: #00a346; + color: white; + border: none; + border-radius: 4px; + margin-bottom: 1rem; + cursor: pointer; + transition: background-color 0.2s ease; - .primary-button, - .secondary-button { - width: 100%; - padding: 0.75rem; - border: none; - border-radius: 4px; - font-size: 1rem; - font-weight: bold; - transition: background-color 0.3s ease; - display: flex; - align-items: center; - justify-content: center; + &:hover { + background-color: darken(#00a346, 10%); + } - &.primary-button { - background-color: $primary-color; - color: $text-white; - - &:hover { - background-color: darken($primary-color, 10%); - } - - &:disabled { - background-color: darken($primary-color, 20%); - cursor: not-allowed; - } - } - - &.secondary-button { - background-color: $secondary-color; - color: $text-white; - - &:hover { - background-color: darken($secondary-color, 10%); - } - } + &:disabled { + background-color: darken(#00a346, 20%); + cursor: not-allowed; } } } -} +} \ No newline at end of file diff --git a/src/styles/scss/_navbar.scss b/src/styles/scss/_navbar.scss new file mode 100644 index 0000000..74095ac --- /dev/null +++ b/src/styles/scss/_navbar.scss @@ -0,0 +1,32 @@ +// _navbar.scss + +// Variables de couleur et autres constantes +$background-color: #1c1f23; +$icon-color: white; +$margin-right: 8px; // Correspondant à theme.spacing(2) + +.navbar { + top: auto !important; + bottom: 0; + background-color: $background-color !important; + + .toolbar { + display: flex; + justify-content: space-between; + } + + .back-button, + .cart-button, + .refresh-button { + color: $icon-color; + } + + .refresh-button { + margin-right: $margin-right; + } + + .back-button[disabled] { + opacity: 0.3; + pointer-events: none; + } +} diff --git a/src/styles/scss/_posterSelector.scss b/src/styles/scss/_posterSelector.scss index 94f03e9..aea351d 100644 --- a/src/styles/scss/_posterSelector.scss +++ b/src/styles/scss/_posterSelector.scss @@ -151,6 +151,7 @@ $hover-color: rgba(102, 119, 136, 0.2); align-items: center; justify-content: center; height: 400px; + color: $text-white; } .gif-container {