diff --git a/package-lock.json b/package-lock.json index 6f01c70..359041f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1400,6 +1400,25 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==" }, + "@popperjs/core": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.4.4.tgz", + "integrity": "sha512-1oO6+dN5kdIA3sKPZhRGJTfGVP4SWV6KqlMOwry4J3HfyD68sl/3KmG7DeYUzvN+RbhXDnv/D8vNNB8168tAMg==" + }, + "@restart/context": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@restart/context/-/context-2.1.4.tgz", + "integrity": "sha512-INJYZQJP7g+IoDUh/475NlGiTeMfwTXUEr3tmRneckHIxNolGOW9CTq83S8cxq0CgJwwcMzMJFchxvlwe7Rk8Q==" + }, + "@restart/hooks": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.3.25.tgz", + "integrity": "sha512-m2v3N5pxTsIiSH74/sb1yW8D9RxkJidGW+5Mfwn/lHb2QzhZNlaU1su7abSyT9EGf0xS/0waLjrf7/XxQHUk7w==", + "requires": { + "lodash": "^4.17.15", + "lodash-es": "^4.17.15" + } + }, "@sheerun/mutationobserver-shim": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz", @@ -1681,6 +1700,11 @@ "@babel/types": "^7.3.0" } }, + "@types/classnames": { + "version": "2.2.10", + "resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.10.tgz", + "integrity": "sha512-1UzDldn9GfYYEsWWnn/P4wkTlkZDH7lDb0wBMGbtIQc9zXEQq7FlKBdZUn6OBqD8sKZZ2RQO2mAjGpXiDGoRmQ==" + }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", @@ -1700,6 +1724,11 @@ "@types/node": "*" } }, + "@types/invariant": { + "version": "2.2.34", + "resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.34.tgz", + "integrity": "sha512-lYUtmJ9BqUN688fGY1U1HZoWT1/Jrmgigx2loq4ZcJpICECm/Om3V314BxdzypO0u5PORKGMM6x0OXaljV1YFg==" + }, "@types/istanbul-lib-coverage": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", @@ -1769,6 +1798,14 @@ "@types/react": "*" } }, + "@types/react-transition-group": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.0.tgz", + "integrity": "sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==", + "requires": { + "@types/react": "*" + } + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", @@ -1868,6 +1905,11 @@ } } }, + "@types/warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-DSUBJorY+ZYrdA04fEZU9fjiPlI=" + }, "@types/yargs": { "version": "13.0.10", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.10.tgz", @@ -2482,6 +2524,14 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz", "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==" }, + "axios": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.20.0.tgz", + "integrity": "sha512-ANA4rr2BDcmmAQLOKft2fufrtuvlqR+cXNNinUmvfeSNCOF98PZL+7M/v1zIdGo7OLjEA9J2gXJL+j4zGsl0bA==", + "requires": { + "follow-redirects": "^1.10.0" + } + }, "axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", @@ -3501,6 +3551,11 @@ } } }, + "classnames": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", + "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" + }, "clean-css": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", @@ -4536,6 +4591,15 @@ "utila": "~0.4" } }, + "dom-helpers": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.0.tgz", + "integrity": "sha512-Ru5o9+V8CpunKnz5LGgWXkmrH/20cGKwcHwS4m73zIvs54CN9epEmT/HLqFJW3kXpakAFkEdzgy1hzlJe3E4OQ==", + "requires": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "dom-serializer": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", @@ -7627,6 +7691,11 @@ } } }, + "jquery": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz", + "integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==" + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -7935,6 +8004,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" }, + "lodash-es": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.15.tgz", + "integrity": "sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ==" + }, "lodash._reinterpolate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", @@ -9156,6 +9230,11 @@ "ts-pnp": "^1.1.6" } }, + "popper.js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==" + }, "portfinder": { "version": "1.0.28", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", @@ -10195,6 +10274,15 @@ "react-is": "^16.8.1" } }, + "prop-types-extra": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", + "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", + "requires": { + "react-is": "^16.3.2", + "warning": "^4.0.0" + } + }, "proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -10374,6 +10462,31 @@ "whatwg-fetch": "^3.0.0" } }, + "react-bootstrap": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-1.3.0.tgz", + "integrity": "sha512-GYj0c6FO9mx7DaO8Xyz2zs0IcQ6CGCtM3O6/feIoCaG4N8B0+l4eqL7stlMcLpqO4d8NG2PoMO/AbUOD+MO7mg==", + "requires": { + "@babel/runtime": "^7.4.2", + "@restart/context": "^2.1.4", + "@restart/hooks": "^0.3.21", + "@types/classnames": "^2.2.10", + "@types/invariant": "^2.2.33", + "@types/prop-types": "^15.7.3", + "@types/react": "^16.9.35", + "@types/react-transition-group": "^4.4.0", + "@types/warning": "^3.0.0", + "classnames": "^2.2.6", + "dom-helpers": "^5.1.2", + "invariant": "^2.2.4", + "prop-types": "^15.7.2", + "prop-types-extra": "^1.1.0", + "react-overlays": "^4.1.0", + "react-transition-group": "^4.4.1", + "uncontrollable": "^7.0.0", + "warning": "^4.0.3" + } + }, "react-dev-utils": { "version": "10.2.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-10.2.1.tgz", @@ -10596,6 +10709,26 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "react-overlays": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-4.1.0.tgz", + "integrity": "sha512-vdRpnKe0ckWOOD9uWdqykLUPHLPndIiUV7XfEKsi5008xiyHCfL8bxsx4LbMrfnxW1LzRthLyfy50XYRFNQqqw==", + "requires": { + "@babel/runtime": "^7.4.5", + "@popperjs/core": "^2.0.0", + "@restart/hooks": "^0.3.12", + "@types/warning": "^3.0.0", + "dom-helpers": "^5.1.0", + "prop-types": "^15.7.2", + "uncontrollable": "^7.0.0", + "warning": "^4.0.3" + } + }, "react-router": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz", @@ -10702,6 +10835,27 @@ "workbox-webpack-plugin": "4.3.1" } }, + "react-toastify": { + "version": "6.0.8", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-6.0.8.tgz", + "integrity": "sha512-NSqCNwv+C4IfR+c92PFZiNyeBwOJvigrP2bcRi2f6Hg3WqcHhEHOknbSQOs9QDFuqUjmK3SOrdvScQ3z63ifXg==", + "requires": { + "classnames": "^2.2.6", + "prop-types": "^15.7.2", + "react-transition-group": "^4.4.1" + } + }, + "react-transition-group": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", + "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==", + "requires": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + } + }, "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -12619,6 +12773,22 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, + "typescript": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.2.tgz", + "integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==" + }, + "uncontrollable": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.1.1.tgz", + "integrity": "sha512-EcPYhot3uWTS3w00R32R2+vS8Vr53tttrvMj/yA1uYRhf8hbTG2GyugGqWDY0qIskxn0uTTojVd6wPYW9ZEf8Q==", + "requires": { + "@babel/runtime": "^7.6.3", + "@types/react": "^16.9.11", + "invariant": "^2.2.4", + "react-lifecycles-compat": "^3.0.4" + } + }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", @@ -12910,6 +13080,14 @@ "makeerror": "1.0.x" } }, + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + }, "watchpack": { "version": "1.7.4", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.4.tgz", diff --git a/package.json b/package.json index ebe369f..0bcf7e2 100644 --- a/package.json +++ b/package.json @@ -6,11 +6,17 @@ "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.5.0", "@testing-library/user-event": "^7.2.1", + "axios": "^0.20.0", "bootstrap": "^4.5.2", + "jquery": "^3.5.1", + "popper.js": "^1.16.1", "react": "^16.13.1", + "react-bootstrap": "^1.3.0", "react-dom": "^16.13.1", "react-router-dom": "^5.2.0", - "react-scripts": "3.4.3" + "react-scripts": "3.4.3", + "react-toastify": "^6.0.8", + "typescript": "^4.0.2" }, "scripts": { "start": "react-scripts start", diff --git a/src/App.css b/src/App.css index 959c307..3f46525 100644 --- a/src/App.css +++ b/src/App.css @@ -2,6 +2,9 @@ text-align: center; } -.container { - margin-top: 5px; +.header{ + background-color: #337ab7; + color: white; + padding: 5px 0px; + margin: 0px 0px 20px 0px; } \ No newline at end of file diff --git a/src/App.js b/src/App.js index 13f4daa..c86d2d2 100644 --- a/src/App.js +++ b/src/App.js @@ -4,10 +4,16 @@ import './App.css'; import Movies from "./components/movies"; import MovieDetails from "./components/movieDetails"; import NotFound from "./components/notFound"; +import { ToastContainer} from "react-toastify"; +import 'react-toastify/dist/ReactToastify.css' function App() { return (
+ +
+

OMDB-API Search Movies

+
diff --git a/src/components/movieDetails.jsx b/src/components/movieDetails.jsx index 0f366f0..4988d76 100644 --- a/src/components/movieDetails.jsx +++ b/src/components/movieDetails.jsx @@ -1,14 +1,112 @@ import React, { Component } from "react"; +import restService from '../services/restService'; +import config from "../config.json"; +import { Row, Col } from "react-bootstrap"; +import { Link } from "react-router-dom"; + class MovieDetails extends Component { constructor(props) { super(props); + this.state = { + movie: null, + isLoading: false, + } } - render() { + async performSearch() { + try { + this.setState({ isLoading: true }); + let url = config.endpointBase + '&plot=full&i=' + this.props.match.params.id; + const movie = await restService.get(url); + this.setState({ isLoading: false, movie: movie.data }); + } catch (ex) { + this.setState({ isLoading: false }); + if (ex.response && ex.response.status === 404) { + alert("not found") + } + } + } + + async componentDidMount() { + this.performSearch(); + } + + displayColumn(content) { + return ( + + {content} + + ); + } + + displayTitledData(title, content) { return ( <> + + + {title} + + + {content} + + + + ) + } + displayTitledDataWithLine(title, content) { + return ( + <> +
+ {this.displayTitledData(title, content)} + + ) + } + + + render() { + const { movie, isLoading } = this.state; + + if (!movie && !isLoading) return null; + + if (isLoading) return (

Loading Movie Details...

); + return ( + <> +

{movie.Title} {"(" + movie.Year + ")"}

+
+ + + + + + + {this.displayColumn(movie.Rated)} + {this.displayColumn(movie.Runtime)} + {this.displayColumn(movie.Genre)} + +
+ + + {movie.Plot}} + + + {this.displayTitledDataWithLine("Writer:", movie.Writer)} + {this.displayTitledData("Actors:", movie.Actors)} + {this.displayTitledData("Director:", movie.Director)} + {this.displayTitledData("Country:", movie.Country)} + {this.displayTitledDataWithLine("Awards:", movie.Awards)} + {this.displayTitledData("Metascore:", movie.Metascore)} + {this.displayTitledData("Imdb Rating:", movie.imdbRating)} + {this.displayTitledData("Imdb Votes:", movie.imdbVotes)} + {this.displayTitledDataWithLine("Box Office:", movie.BoxOffice)} + {this.displayTitledData("Production:", movie.Production)} + +
+
+ + Back to Movies Search + ); } diff --git a/src/components/movies.jsx b/src/components/movies.jsx index 1857ea6..20dbfd9 100644 --- a/src/components/movies.jsx +++ b/src/components/movies.jsx @@ -1,19 +1,122 @@ import React, { Component } from "react"; +import MoviesTable from "./moviesTable"; +import restService from '../services/restService'; +import config from "../config.json"; +import { Form, Row, Col } from "react-bootstrap"; +import { toast } from 'react-toastify'; + class Movies extends Component { constructor(props) { super(props); + this.state = { + movies: [], + totalMovies: 0, + isLoading: false, + didSearch: false, + movieSearchName: null, + movieYear: null, + lastPage: 1, + errors: {} + } + } + + validate() { + const errors = {}; + const { movieSearchName, movieYear } = this.state; + + if (movieSearchName.trim() === '') + errors.movieSearchName = "Name of movie is required"; + if (movieYear.trim().length > 0 && isNaN(parseInt(movieYear.trim()))) { + errors.movieYear = "Year is invalid"; + } + return errors; + } + + handleSubmit = event => { + event.preventDefault(); + const errors = this.validate(); + this.setState({ errors }); + for (let key in errors) { + toast.error(errors[key]); + return; // return on first error + } + + if (this.state.movieSearchName) { + this.performSearch(1) + } + } + + getMoreMovies = event => { + this.performSearch(this.state.lastPage + 1); + } + + getUrl(page) { + let url = config.endpointBase + '&s=' + this.state.movieSearchName; + if (this.state.movieYear) + url += "&y=" + this.state.movieYear; + if (page > 1) + url += "&page=" + page; + return url; + } + + + async performSearch(page) { + try { + this.setState({ isLoading: true }); + const moviesRetrieved = await restService.get(this.getUrl(page)); + if (page > 1) + this.setState({ movies: this.state.movies.concat(moviesRetrieved.data.Search), totalMovies: moviesRetrieved.data.totalResults }); + else + this.setState({ movies: moviesRetrieved.data.Search, totalMovies: moviesRetrieved.data.totalResults }); + this.setState({ isLoading: false, lastPage: page, didSearch: true }); + } catch (ex) { + this.setState({ isLoading: false }); + if (ex.response && ex.response.status === 404) { + alert("not found") + } + } + } + + handleChange = event => { + const { id, value } = event.target; + this.setState(prevState => ({ + ...prevState, + [id]: value + })); + } + + showMovies() { + const { totalMovies, movies } = this.state; + + return ( +
+
+ {totalMovies > 0 ?

Found {totalMovies} matching movies

:

No matching movies found

} + {movies ? : null} +
+
+ ); } render() { + const { isLoading, movies, totalMovies, movieSearchName, movieYear } = this.state; + if (isLoading) return

Loading...

+ return ( <> -

Movies

+
+ + + + + +
+ {this.state.didSearch ? this.showMovies() : null} + {movies && movies.length > 0 && movies.length < totalMovies ? : null} ); } - } export default Movies; - diff --git a/src/components/moviesTable.jsx b/src/components/moviesTable.jsx new file mode 100644 index 0000000..a2ac92e --- /dev/null +++ b/src/components/moviesTable.jsx @@ -0,0 +1,23 @@ +import React, { Component } from "react"; +import { Link } from "react-router-dom"; + +class MoviesTable extends Component { + render() { + const { movies } = this.props; + + return ( +
+ {movies.map(movie => ( +
+ + {"Movie +

{movie.Title} {"(" + movie.Year + ")"}

+ +
+ ))} +
+ ); + } +} + +export default MoviesTable; diff --git a/src/config.json b/src/config.json new file mode 100644 index 0000000..def6e10 --- /dev/null +++ b/src/config.json @@ -0,0 +1,3 @@ +{ + "endpointBase": "http://www.omdbapi.com?apikey=157f34ed&type=movie" +} \ No newline at end of file diff --git a/src/index.css b/src/index.css index ec2585e..55eab58 100644 --- a/src/index.css +++ b/src/index.css @@ -7,7 +7,80 @@ body { -moz-osx-font-smoothing: grayscale; } -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; + +.btn-custom { + border-radius: 5px; + background-color: #337ab7; + color: white; + font-size: 18px; + font-weight: 700; + padding: 5px 10px; + border-color: #337ab7; +} + +.btn-custom:hover{ + background-color: #236aa7; + color: white; +} + + +.container { + margin-top: 5px; +} + +.movieListEntry{ + margin: 5px 0px; +} + +.movieListImage{ + width: 100%; + border-radius: 10px +} + +.movieListTitle { + font-size: 18px; + font-weight: bold; +} + +.searchControl{ + margin-top: 5px; +} + +.searchControlButton{ + margin-top: 2px; } + +.wideButton{ + width: 100% +} + + +.margin-top-10{ + margin-top: 10px; +} + +.margin-top-20{ + margin-top: 20px; +} + +.margin-top-40{ + margin-top: 40px; +} + +.margin-bottom-40{ + margin-bottom: 40px; +} + +.description{ + text-align: left; +} + +.title{ + font-weight: bold; + color: #337ab7; + text-align: left; +} + +.content{ + text-align: left +} \ No newline at end of file diff --git a/src/services/loggerService.js b/src/services/loggerService.js new file mode 100644 index 0000000..f3469a0 --- /dev/null +++ b/src/services/loggerService.js @@ -0,0 +1,17 @@ +// calls to console.log should be replaced with calls to a logging service such as loggly or sentry + +export function debug(message) { + console.log("DEBUG: " + message); +} + +export function info(message) { + console.log("INFO: " + message); +} + +export function error(message) { + console.log("ERROR: " + message); +} + +export function warning(message) { + console.log("warn: " + message); +} \ No newline at end of file diff --git a/src/services/restService.js b/src/services/restService.js new file mode 100644 index 0000000..142a8ce --- /dev/null +++ b/src/services/restService.js @@ -0,0 +1,14 @@ +import axios from 'axios'; +import { toast } from 'react-toastify'; + +axios.interceptors.response.use(null, error => { + if (!error.response || error.response.status < 400 || error.response.status >= 500) { + console.log("Error getting movies from server - " + error); + toast.error("an unexpected error happened"); + } + return Promise.reject(error); +}); + +export default { + get: axios.get +} \ No newline at end of file