diff --git a/backend/src/controllers/movie.ts b/backend/src/controllers/movie.ts index ec63b6fc1b728ead4a59901fa7877d833ee6c517..97cc3d91148dee159394cd7383a361a5a06a2caf 100644 --- a/backend/src/controllers/movie.ts +++ b/backend/src/controllers/movie.ts @@ -85,11 +85,29 @@ export default class MovieController { public static async getMovieByTitle (ctx: Context) { const movieRepository: Repository<Movie> = getManager().getRepository(Movie); - const movies: Movie[] = await movieRepository.find({ where: `"Title" ILIKE '%${ctx.params.title}%'`, // order has to be either DESC or ASC - order: {Title: "ASC"}, + order: {Title: ctx.params.order || "ASC"}, + take: ctx.params.take, + skip: Math.max(ctx.params.take-10, 0) + }); + + if(movies) { + ctx.status = 200; + ctx.body = movies; + } else { + ctx.status = 400; + ctx.body = ERROR_MSG1; + } + } + + public static async getMovieByTitleAndGenre (ctx: Context) { + const movieRepository: Repository<Movie> = getManager().getRepository(Movie); + const movies: Movie[] = await movieRepository.find({ + where: `"Title" ILIKE '%${ctx.params.title}%' AND "Genre" ILIKE '%${ctx.params.genre}%'`, + // order has to be either DESC or ASC + order: {Title: ctx.params.order || "ASC"}, take: ctx.params.take, skip: Math.max(ctx.params.take-10, 0) }); diff --git a/backend/src/routes/rest-routes.ts b/backend/src/routes/rest-routes.ts index 4aaa37ef326550d812fed0634069b385271c2a0f..f0fe73b8671b6f3462dcc62e5c53d6dfc08b4c43 100644 --- a/backend/src/routes/rest-routes.ts +++ b/backend/src/routes/rest-routes.ts @@ -12,7 +12,9 @@ restRouter.get("/movies/:id", controller.movie.getMovieId); // Post request for finding movies based on search restRouter.post("/movies/search", controller.movie.searchMovie); // Get request for finding movies based on search -restRouter.get("/movies/:title/:take", controller.movie.getMovieByTitle); +restRouter.get("/movies/:title/:take/:order", controller.movie.getMovieByTitle) +// Get request for finding movies based on search and genre +restRouter.get("/movies/:title/:take/:order/:genre", controller.movie.getMovieByTitleAndGenre); // Post request for finding movies based on category restRouter.post("/movies/genre", controller.movie.searchMoviesByGenre); diff --git a/frontend/app/components/SearchHandler.component.tsx b/frontend/app/components/SearchHandler.component.tsx index 46a40c62e1fab854f46204b19deb7ad42367cf48..cf6a0582ad46aeb240e4a068088363d77604496f 100644 --- a/frontend/app/components/SearchHandler.component.tsx +++ b/frontend/app/components/SearchHandler.component.tsx @@ -20,7 +20,6 @@ const SearchHandler = (): JSX.Element => { } }, [debouncedSearch]); - // TODO: this function should be exported to ensure modularity const searchMovies = async () => { const body = { Title: search, diff --git a/frontend/app/store/slices/movieSlice.ts b/frontend/app/store/slices/movieSlice.ts index 2978c94d9f3f920f10a9ad3e04b1ec21b7b2f551..292f60bb570c5ade38b84bd045684487973293ea 100644 --- a/frontend/app/store/slices/movieSlice.ts +++ b/frontend/app/store/slices/movieSlice.ts @@ -38,8 +38,8 @@ export const movieSlice = createSlice({ searchTerm: state.searchTerm, movies: [...state.movies, action.payload], }), - clearMovies: () => ({ - searchTerm: "", + clearMovies: (state: MovieState) => ({ + searchTerm: state.searchTerm, movies: [], }), }, diff --git a/frontend/app/views/Home.component.tsx b/frontend/app/views/Home.component.tsx index 6c57aeab5e4d2ee23db889bf586ae8ba49814423..8e373a4e999a08b4ba83569b46af01b1c6ff4141 100644 --- a/frontend/app/views/Home.component.tsx +++ b/frontend/app/views/Home.component.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { View, FlatList, StyleSheet } from "react-native"; +import { View, FlatList, StyleSheet, Picker } from "react-native"; import axios from "axios"; import { AppState, useAppDispatch } from "../store/redux/store"; @@ -18,6 +18,8 @@ const Home: React.FC = (): JSX.Element => { const searchTerm = useSelector((state: AppState) => state.movies.searchTerm); const count = useSelector((state: AppState) => state.system.count); const [refreshing, setRefreshing] = useState(false); + const [sortValue, setSortValue] = useState("ASC"); + const [filterValue, setFilterValue] = useState(""); // This does not work, dispatch is async, so we will have to rework pagination. The list works. const refreshMovies = () => { @@ -30,8 +32,18 @@ const Home: React.FC = (): JSX.Element => { if (movies.length >= count) { return; } + + const endpoint = + filterValue == "" + ? `${api.defaults.baseURL}/movies/${searchTerm}/${ + movies.length + 10 + }/${sortValue}` + : `${api.defaults.baseURL}/movies/${searchTerm}/${ + movies.length + 10 + }/${sortValue}/${filterValue}`; + axios - .get(`${api.defaults.baseURL}/movies/${searchTerm}/${movies.length + 10}`) + .get(endpoint) .then((response) => { response.data.map((movie: Movie) => { if (!movies.includes(movie)) { @@ -42,11 +54,40 @@ const Home: React.FC = (): JSX.Element => { .catch((e) => console.log(e)); }; - // TODO: add useEffect to re-render on state change? + useEffect(() => { + dispatch(movieSlice.actions.clearMovies()); + fetchMoreMovies(); + }, [filterValue, sortValue]); return ( <View style={styles.container}> <SearchHandler /> + <View style={{ flexDirection: "row" }}> + <Picker + selectedValue={sortValue} + style={{ height: 50, width: "50%" }} + onValueChange={(itemValue, itemIndex) => setSortValue(itemValue)} + > + <Picker.Item label="Ascending A-Z" value="ASC" /> + <Picker.Item label="Descending Z-A" value="DESC" /> + </Picker> + <Picker + selectedValue={filterValue} + style={{ height: 50, width: "50%" }} + onValueChange={(itemValue, itemIndex) => setFilterValue(itemValue)} + > + <Picker.Item label="All" value="" /> + <Picker.Item label="Action" value="Action" /> + <Picker.Item label="Adventure" value="Adventure" /> + <Picker.Item label="Biography" value="Biography" /> + <Picker.Item label="Comedy" value="Comedy" /> + <Picker.Item label="Crime" value="Crime" /> + <Picker.Item label="Drama" value="Drama" /> + <Picker.Item label="Fantasy" value="Fantasy" /> + <Picker.Item label="History" value="History" /> + <Picker.Item label="Thriller" value="Thriller" /> + </Picker> + </View> <View> <FlatList data={movies} @@ -69,3 +110,9 @@ const styles = StyleSheet.create({ }); export default Home; + +/* + Missing support: + <Picker.Item label="Top-bottom rated" value="top" /> + <Picker.Item label="Bottom-top rated" value="bot" /> + */