Updated PosterSelector and UploadDiary components

This commit is contained in:
2024-09-14 22:49:14 +02:00
parent 480d47de01
commit 704f1d38c7
3 changed files with 244 additions and 124 deletions

View File

@@ -16,6 +16,7 @@ import {
import Pagination from "@mui/material/Pagination"; import Pagination from "@mui/material/Pagination";
import { createTheme, ThemeProvider } from "@mui/material/styles"; import { createTheme, ThemeProvider } from "@mui/material/styles";
import queryString from "query-string"; import queryString from "query-string";
import pulpGif from "../static/images/pulp.gif";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
root: { root: {
@@ -25,11 +26,15 @@ const useStyles = makeStyles((theme) => ({
padding: theme.spacing(3), padding: theme.spacing(3),
borderRadius: theme.shape.borderRadius, borderRadius: theme.shape.borderRadius,
backgroundColor: "#1c1f23", backgroundColor: "#1c1f23",
color: "white",
}, },
title: { title: {
color: "white",
marginBottom: theme.spacing(3), marginBottom: theme.spacing(3),
}, },
username: {
color: "#00A346",
marginBottom: theme.spacing(2),
},
list: { list: {
maxHeight: "800px", maxHeight: "800px",
minHeight: "1000px", minHeight: "1000px",
@@ -94,6 +99,44 @@ const useStyles = makeStyles((theme) => ({
marginTop: theme.spacing(2), marginTop: theme.spacing(2),
color: "white", color: "white",
}, },
noMoviesContainer: {
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
height: "400px",
},
gifContainer: {
display: "flex",
justifyContent: "center",
position: "relative",
width: "200px",
height: "200px",
marginBottom: theme.spacing(2),
},
circleBackground: {
position: "absolute",
width: "120%",
height: "120%",
borderRadius: "50%",
backgroundColor: "#ffffff",
clipPath: "inset(0 0 25% 0)",
bottom: "-28%",
},
gif: {
position: "absolute",
width: "100%",
height: "100%",
objectFit: "contain",
},
backButton: {
marginTop: theme.spacing(2),
backgroundColor: "#00A346",
color: "white",
"&:hover": {
backgroundColor: "#008036",
},
},
})); }));
const paginationTheme = createTheme({ const paginationTheme = createTheme({
@@ -134,6 +177,7 @@ const PosterSelector = () => {
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const [totalPages, setTotalPages] = useState(1); const [totalPages, setTotalPages] = useState(1);
const [progress, setProgress] = useState(0); const [progress, setProgress] = useState(0);
const [username, setUsername] = useState("");
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
const moviesPerPage = 10; const moviesPerPage = 10;
@@ -143,8 +187,18 @@ const PosterSelector = () => {
const pageNumber = parsed.page ? parseInt(parsed.page, 10) : 1; const pageNumber = parsed.page ? parseInt(parsed.page, 10) : 1;
setPage(pageNumber); setPage(pageNumber);
fetchMovies(pageNumber); fetchMovies(pageNumber);
fetchUsername();
}, [location.search]); }, [location.search]);
const fetchUsername = async () => {
try {
const response = await axios.get("http://localhost:5000/api/username");
setUsername(response.data.username);
} catch (error) {
console.error("Error fetching username:", error);
}
};
const fetchMovies = async (pageNumber) => { const fetchMovies = async (pageNumber) => {
try { try {
setLoading(true); setLoading(true);
@@ -195,6 +249,10 @@ const PosterSelector = () => {
navigate(`?page=${value}`); navigate(`?page=${value}`);
}; };
const handleGoBack = () => {
navigate("/");
};
const formatWatchedDate = (dateString) => { const formatWatchedDate = (dateString) => {
const date = new Date(dateString); const date = new Date(dateString);
const day = date.getDate().toString().padStart(2, "0"); const day = date.getDate().toString().padStart(2, "0");
@@ -210,6 +268,11 @@ const PosterSelector = () => {
<Typography variant="h4" gutterBottom className={classes.title}> <Typography variant="h4" gutterBottom className={classes.title}>
Your diary Your diary
</Typography> </Typography>
{username && (
<Typography variant="h6" className={classes.username}>
Letterboxd User: {username}
</Typography>
)}
{loading ? ( {loading ? (
<Box className={classes.progressContainer}> <Box className={classes.progressContainer}>
<LinearProgress variant="determinate" value={progress} /> <LinearProgress variant="determinate" value={progress} />
@@ -218,93 +281,111 @@ const PosterSelector = () => {
</Typography> </Typography>
</Box> </Box>
) : movies.length > 0 ? ( ) : movies.length > 0 ? (
<List className={classes.list}> <>
{movies.map((movie, index) => ( <List className={classes.list}>
<ListItem {movies.map((movie, index) => (
button <ListItem
key={index} button
onClick={() => handleMovieClick(movie.Name, movie.Year)} key={index}
className={classes.listItem} onClick={() => handleMovieClick(movie.Name, movie.Year)}
> className={classes.listItem}
<Grid container alignItems="center"> >
<Grid item> <Grid container alignItems="center">
<img <Grid item>
src={ <img
`https://image.tmdb.org/t/p/w500${movie.Poster.file_path}` || src={
`/api/placeholder/50/75` `https://image.tmdb.org/t/p/w500${movie.Poster.file_path}` ||
} `/api/placeholder/50/75`
alt={movie.Name} }
className={classes.poster} alt={movie.Name}
/> className={classes.poster}
/>
</Grid>
<Grid item className={classes.movieInfo}>
<Typography
variant="subtitle1"
className={classes.movieTitle}
>
{movie.Name}
</Typography>
<Typography
variant="body2"
color="textSecondary"
className={classes.movieYear}
>
{movie.Year}
</Typography>
</Grid>
<Grid item>
<Box className={classes.watchedDate}>
{(() => {
const { day, month } = formatWatchedDate(
movie["Watched Date"]
);
return (
<>
<Typography
variant="body2"
className={classes.watchedDay}
>
{day}
</Typography>
<Typography
variant="body2"
className={classes.watchedMonth}
>
{month}
</Typography>
</>
);
})()}
</Box>
</Grid>
</Grid> </Grid>
<Grid item className={classes.movieInfo}> </ListItem>
<Typography ))}
variant="subtitle1" </List>
className={classes.movieTitle} <Box className={classes.paginationContainer}>
> <ThemeProvider theme={paginationTheme}>
{movie.Name} <Pagination
</Typography> count={totalPages}
<Typography page={page}
variant="body2" onChange={handlePageChange}
color="textSecondary" color="primary"
className={classes.movieYear} />
> </ThemeProvider>
{movie.Year} </Box>
</Typography> <Button
</Grid> variant="contained"
<Grid item> className={classes.refreshButton}
<Box className={classes.watchedDate}> onClick={handleRefreshFiles}
{(() => { >
const { day, month } = formatWatchedDate( Refresh File
movie["Watched Date"] </Button>
); </>
return (
<>
<Typography
variant="body2"
className={classes.watchedDay}
>
{day}
</Typography>
<Typography
variant="body2"
className={classes.watchedMonth}
>
{month}
</Typography>
</>
);
})()}
</Box>
</Grid>
</Grid>
</ListItem>
))}
</List>
) : ( ) : (
<Box> <Box className={classes.noMoviesContainer}>
<Typography variant="body1" color="textSecondary" gutterBottom> <div className={classes.gifContainer}>
No movies to display. Upload a CSV file to get started. <div className={classes.circleBackground}></div>
<img
src={pulpGif}
alt="Confused reaction"
className={classes.gif}
/>
</div>
<Typography variant="body1" gutterBottom>
No movies found in your diary. Please check your Letterboxd
username or CSV file.
</Typography> </Typography>
<Button
variant="contained"
className={classes.backButton}
onClick={handleGoBack}
>
Go Back
</Button>
</Box> </Box>
)} )}
<Box className={classes.paginationContainer}>
<ThemeProvider theme={paginationTheme}>
<Pagination
count={totalPages}
page={page}
onChange={handlePageChange}
color="primary"
/>
</ThemeProvider>
</Box>
<Button
variant="contained"
className={classes.refreshButton}
onClick={handleRefreshFiles}
>
Refresh File
</Button>
</Paper> </Paper>
</Container> </Container>
); );

View File

@@ -6,6 +6,7 @@ import {
Typography, Typography,
Button, Button,
CircularProgress, CircularProgress,
TextField,
makeStyles, makeStyles,
} from "@material-ui/core"; } from "@material-ui/core";
import { CloudUploadOutlined } from "@material-ui/icons"; import { CloudUploadOutlined } from "@material-ui/icons";
@@ -81,12 +82,31 @@ const useStyles = makeStyles((theme) => ({
marginTop: theme.spacing(4), marginTop: theme.spacing(4),
padding: theme.spacing(3), padding: theme.spacing(3),
}, },
usernameField: {
marginBottom: theme.spacing(3),
"& .MuiOutlinedInput-root": {
color: "#ffffff",
"& fieldset": {
borderColor: "#00A346",
},
"&:hover fieldset": {
borderColor: "#00A346",
},
"&.Mui-focused fieldset": {
borderColor: "#1caff2",
},
},
"& .MuiInputLabel-root": {
color: "#ffffff",
},
},
})); }));
const UploadDiary = () => { const UploadDiary = () => {
const classes = useStyles(); const classes = useStyles();
const [file, setFile] = useState(null); const [file, setFile] = useState(null);
const [uploading, setUploading] = useState(false); const [uploading, setUploading] = useState(false);
const [username, setUsername] = useState("");
const navigate = useNavigate(); const navigate = useNavigate();
useEffect(() => { useEffect(() => {
@@ -112,21 +132,29 @@ const UploadDiary = () => {
}); });
const handleUpload = async () => { const handleUpload = async () => {
if (file) { if (username || file) {
setUploading(true); setUploading(true);
try { try {
const formData = new FormData(); if (username) {
formData.append("file", file); // Here you would implement the logic to fetch the diary from Letterboxd
// For now, we'll just simulate a successful fetch
console.log(`Fetching diary for user: ${username}`);
await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate API call
navigate("/PosterSelector");
} else if (file) {
const formData = new FormData();
formData.append("file", file);
await axios.post("http://localhost:5000/api/upload-csv", formData, { await axios.post("http://localhost:5000/api/upload-csv", formData, {
headers: { headers: {
"Content-Type": "multipart/form-data", "Content-Type": "multipart/form-data",
}, },
}); });
navigate("/PosterSelector"); navigate("/PosterSelector");
}
} catch (error) { } catch (error) {
console.error("Error uploading file:", error); console.error("Error processing diary:", error);
} finally { } finally {
setUploading(false); setUploading(false);
} }
@@ -137,7 +165,22 @@ const UploadDiary = () => {
<Container className={classes.root}> <Container className={classes.root}>
<div className={classes.card}> <div className={classes.card}>
<Typography variant="h4" className={classes.title}> <Typography variant="h4" className={classes.title}>
Upload CSV File Upload Letterboxd Diary
</Typography>
<TextField
className={classes.usernameField}
label="Letterboxd Username"
variant="outlined"
fullWidth
value={username}
onChange={(e) => setUsername(e.target.value)}
placeholder="Enter your Letterboxd username"
/>
<Typography
variant="body2"
style={{ color: "#ffffff", marginBottom: "1rem" }}
>
Or upload a CSV file:
</Typography> </Typography>
<div {...getRootProps()} className={classes.dropzone}> <div {...getRootProps()} className={classes.dropzone}>
<input {...getInputProps()} /> <input {...getInputProps()} />
@@ -163,41 +206,37 @@ const UploadDiary = () => {
</> </>
)} )}
</div> </div>
{file && ( <Grid container justify="center" className={classes.progressContainer}>
<Grid {uploading ? (
container <CircularProgress />
justify="center" ) : (
className={classes.progressContainer} <Button
> className={classes.submitButton}
{uploading ? ( variant="contained"
<CircularProgress /> onClick={handleUpload}
) : ( disabled={!username && !file}
<Button >
className={classes.submitButton} SUBMIT
variant="contained" </Button>
onClick={handleUpload} )}
> </Grid>
SUBMIT
</Button>
)}
</Grid>
)}
</div> </div>
<Typography variant="body1" className={classes.overviewText}> <Typography variant="body1" className={classes.overviewText}>
This tool allows you to easily upload your diary.csv file from This tool allows you to easily import your Letterboxd diary and select
Letterboxd and select your favorite movie poster. Follow these simple your favorite movie poster. Follow these simple steps:
steps:
<br /> <br />
<strong>1. Upload Your CSV File:</strong> Drag and drop your diary.csv <strong>1. Enter Your Letterboxd Username:</strong> Type your Letterboxd
file or click to select it from your device. username to automatically fetch your diary.
<br /> <br />
<strong>2. Automatic Processing:</strong> Once uploaded, the application <strong>2. Or Upload Your CSV File:</strong> If you prefer, you can
will automatically process the file. still upload your diary.csv file directly.
<br /> <br />
<strong>3. Poster Selection:</strong> After the file is successfully <strong>3. Automatic Processing:</strong> Once submitted, the
uploaded, you'll be redirected to the Poster Selector page, where you application will process your diary.
can browse and choose your favorite movie poster based on your <br />
Letterboxd diary. <strong>4. Poster Selection:</strong> After processing, you'll be
redirected to the Poster Selector page, where you can browse and choose
your favorite movie poster based on your Letterboxd diary.
</Typography> </Typography>
</Container> </Container>
); );

BIN
src/static/images/pulp.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB