From 1958d149a4e2316b728be6c589b2cba53124bb59 Mon Sep 17 00:00:00 2001
From: joningehe <jon.heggstad@gmail.com>
Date: Tue, 17 Nov 2020 22:23:17 +0100
Subject: [PATCH] Added option for sort and filtration

- Added option for sort and filtration
- Still some bugs
---
 backend/src/controllers/movie.ts              | 22 +++++++-
 backend/src/routes/rest-routes.ts             |  4 +-
 .../components/SearchHandler.component.tsx    |  1 -
 frontend/app/store/slices/movieSlice.ts       |  4 +-
 frontend/app/views/Home.component.tsx         | 53 +++++++++++++++++--
 5 files changed, 75 insertions(+), 9 deletions(-)

diff --git a/backend/src/controllers/movie.ts b/backend/src/controllers/movie.ts
index ec63b6f..97cc3d9 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 4aaa37e..f0fe73b 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 46a40c6..cf6a058 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 2978c94..292f60b 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 6c57aea..8e373a4 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" />
+ */
-- 
GitLab