From 129fcb672cdf9acb7d92312b8f3413a23a482082 Mon Sep 17 00:00:00 2001 From: Pierret Hugo Date: Sat, 19 Oct 2024 14:00:38 +0200 Subject: [PATCH] Adding Cart page --- src/App.js | 4 +- src/components/Cart.js | 239 +++++++++++++++++++++++++++++ src/components/NavBar.js | 78 ++++++++++ src/components/PosterGallery.js | 77 +++++----- src/components/PosterSelector.js | 255 +++++++++++++++---------------- src/services/action.js | 6 + src/services/store.js | 9 +- 7 files changed, 502 insertions(+), 166 deletions(-) create mode 100644 src/components/Cart.js create mode 100644 src/components/NavBar.js diff --git a/src/App.js b/src/App.js index 3c2575b..aee739d 100644 --- a/src/App.js +++ b/src/App.js @@ -7,7 +7,7 @@ import { store, persistor } from "./services/store"; import PosterSelector from "./components/PosterSelector"; import PosterGallery from "./components/PosterGallery"; import UploadDiary from "./components/UploadDiary"; -import GlobalDownloadButton from "./components/DownloadButton"; +import Cart from "./components/Cart"; function App() { return ( @@ -23,11 +23,11 @@ function App() { path="/posters/:movieName/:movieYear" element={} /> + } /> } - ); diff --git a/src/components/Cart.js b/src/components/Cart.js new file mode 100644 index 0000000..4fcbfa6 --- /dev/null +++ b/src/components/Cart.js @@ -0,0 +1,239 @@ +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 { makeStyles } from '@material-ui/core/styles'; +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"; + +const useStyles = makeStyles((theme) => ({ + root: { + marginTop: theme.spacing(4), + marginBottom: theme.spacing(8), + display: 'flex', + }, + posterList: { + flex: 2, + marginRight: theme.spacing(2), + maxHeight: 'calc(100vh - 200px)', + overflowY: 'auto', + }, + actionPanel: { + flex: 1, + position: 'sticky', + top: theme.spacing(4), + height: 'fit-content', + }, + card: { + display: 'flex', + marginBottom: theme.spacing(2), + backgroundColor: '#2c3e50', + }, + cardMedia: { + width: 100, + }, + cardContent: { + flex: '1 0 auto', + }, + actions: { + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(2), + }, + slider: { + width: '100%', + marginTop: theme.spacing(2), + }, + button: { + width: '100%', + }, + typography: { + color: '#ecf0f1', + }, + formControl: { + marginBottom: theme.spacing(2), + }, + checkbox: { + color: '#3498db', + }, + paper: { + padding: theme.spacing(3), + backgroundColor: '#34495e', + }, +})); + +const Cart = () => { + const classes = useStyles(); + const dispatch = useDispatch(); + const selectedPosters = useSelector((state) => { + const selections = state.posterSelections; + return Object.entries(selections).flatMap(([movieId, posters]) => + 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 handleRemovePoster = (index) => { + dispatch(removePoster(selectedPosters[index].movieId, selectedPosters[index].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, + }, { + responseType: 'blob', + }); + + const blob = new Blob([response.data], { type: response.headers['content-type'] }); + const link = document.createElement('a'); + link.href = window.URL.createObjectURL(blob); + link.download = `posters.${downloadFormat}`; + link.click(); + } catch (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={classes.checkbox} + /> + } + label="Select for download" + className={classes.typography} + /> + + + + + ))} +
+ +
+ Download Format + setDownloadFormat(e.target.value)} + SelectProps={{ + native: true, + }} + className={classes.formControl} + > + + + + + + +
+
+ setRenameDialogOpen(false)}> + Rename Poster + + setNewPosterName(e.target.value)} + /> + + + + + + +
+ ); +}; + +export default Cart; \ No newline at end of file diff --git a/src/components/NavBar.js b/src/components/NavBar.js new file mode 100644 index 0000000..d86c5b7 --- /dev/null +++ b/src/components/NavBar.js @@ -0,0 +1,78 @@ +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 navigate = useNavigate(); + const location = useLocation(); + const selectedPosters = useSelector((state) => { + const selections = state.posterSelections; + return Object.values(selections).flat(); + }); + + const totalSelected = selectedPosters.length; + + const handleBack = () => { + navigate(-1); + }; + + const handleCart = () => { + navigate('/Cart'); + }; + + const isPosterSelectorPage = location.pathname !== '/PosterSelector'; + + return ( + + + {isPosterSelectorPage ? ( + + + + ) : ( + + {/* Un IconButton vide et désactivé */} + + )} +
+ + + + + + +
+
+
+ ); +}; + +export default NavBar; diff --git a/src/components/PosterGallery.js b/src/components/PosterGallery.js index 4b6c3ba..40d642c 100644 --- a/src/components/PosterGallery.js +++ b/src/components/PosterGallery.js @@ -15,10 +15,12 @@ import { Paper, } from "@material-ui/core"; import CheckIcon from "@material-ui/icons/Check"; +import NavBar from "./NavBar"; const useStyles = makeStyles((theme) => ({ root: { marginTop: theme.spacing(4), + marginBottom: theme.spacing(4), }, paper: { padding: theme.spacing(3), @@ -83,6 +85,8 @@ const PosterGallery = ({ movieId }) => { (state) => state.posterSelections[movieId] || [] ); + console.log(movieId); + useEffect(() => { const fetchPosters = async () => { try { @@ -110,42 +114,45 @@ const PosterGallery = ({ movieId }) => { }; return ( - - - - Posters for {movieName} ({movieYear}) - - {loading ? ( - - - - ) : ( - - {posters.map((poster, index) => ( - - handlePosterSelect(poster.file_path)} - > - - {selectedPosters.includes(poster.file_path) && ( - - )} - + <> + + + + Posters for {movieName} ({movieYear}) + + {loading ? ( + + + + ) : ( + + {posters.map((poster, index) => ( + + handlePosterSelect(poster.file_path)} + > + + {selectedPosters.includes(poster.file_path) && ( + + )} + + + ))} - ))} - - )} - - + )} + + + + ); }; diff --git a/src/components/PosterSelector.js b/src/components/PosterSelector.js index b16c4da..bd59970 100644 --- a/src/components/PosterSelector.js +++ b/src/components/PosterSelector.js @@ -17,10 +17,12 @@ import Pagination from "@mui/material/Pagination"; import { createTheme, ThemeProvider } from "@mui/material/styles"; import queryString from "query-string"; import pulpGif from "../static/images/pulp.gif"; +import NavBar from "./NavBar"; const useStyles = makeStyles((theme) => ({ root: { marginTop: theme.spacing(4), + marginBottom: theme.spacing(8), }, paper: { padding: theme.spacing(3), @@ -37,7 +39,7 @@ const useStyles = makeStyles((theme) => ({ }, list: { maxHeight: "800px", - minHeight: "1000px", + minHeight: "900px", }, listItem: { marginBottom: theme.spacing(2), @@ -93,7 +95,7 @@ const useStyles = makeStyles((theme) => ({ justifyContent: "center", alignItems: "center", flexDirection: "column", - height: "200px", // Adjust height as needed to keep component size consistent + height: "200px", }, progressLabel: { marginTop: theme.spacing(2), @@ -186,8 +188,9 @@ const PosterSelector = () => { const parsed = queryString.parse(location.search); const pageNumber = parsed.page ? parseInt(parsed.page, 10) : 1; setPage(pageNumber); - fetchMovies(pageNumber); - fetchUsername(); + + fetchMovies(pageNumber).then(r => console.log(r)); + // fetchUsername(); }, [location.search]); const fetchUsername = async () => { @@ -231,7 +234,7 @@ const PosterSelector = () => { navigate( `/posters/${encodeURIComponent(movieName)}/${encodeURIComponent( movieYear - )}?page=${page}` + )}` ); }; @@ -263,131 +266,127 @@ const PosterSelector = () => { }; return ( - - - - Your diary - - {username && ( - - Letterboxd User: {username} + <> + + + + Your diary - )} - {loading ? ( - - - - Downloading movies... - - - ) : movies.length > 0 ? ( - <> - - {movies.map((movie, index) => ( - handleMovieClick(movie.Name, movie.Year)} - className={classes.listItem} + {username && ( + + Letterboxd User: {username} + + )} + {loading ? ( + + + + Downloading movies... + + + ) : movies.length > 0 ? ( + <> + + {movies.map((movie, index) => ( + handleMovieClick(movie.Name, movie.Year)} + className={classes.listItem} + > + + + {movie.Name} + + + + {movie.Name} + + + {movie.Year} + + + + + {(() => { + const { day, month } = formatWatchedDate( + movie["Watched Date"] + ); + return ( + <> + + {day} + + + {month} + + + ); + })()} + + + + + ))} + + + + + + + + ) : ( + +
+
+ Confused reaction +
+ + No movies found in your diary. Please check your Letterboxd + username or CSV file. + + - - ) : ( - -
-
- Confused reaction -
- - No movies found in your diary. Please check your Letterboxd - username or CSV file. - - -
- )} -
-
+ Go Back + + + )} +
+
+ + ); }; diff --git a/src/services/action.js b/src/services/action.js index 0a46933..c5acf55 100644 --- a/src/services/action.js +++ b/src/services/action.js @@ -9,3 +9,9 @@ export const deselectPoster = (movieId, posterId) => ({ movieId, posterId, }); + +export const removePoster = (movieId, posterId) => ({ + type: "REMOVE_POSTER", + movieId, + posterId, +}); \ No newline at end of file diff --git a/src/services/store.js b/src/services/store.js index 5a6062e..faae574 100644 --- a/src/services/store.js +++ b/src/services/store.js @@ -13,7 +13,14 @@ const posterSelectionReducer = (state = {}, action) => { return { ...state, [action.movieId]: state[action.movieId].filter( - (id) => id !== action.posterId + (id) => id !== action.posterId + ), + }; + case "REMOVE_POSTER": + return { + ...state, + [action.movieId]: state[action.movieId].filter( + (id) => id !== action.posterId ), }; default: