diff --git a/.gitignore b/.gitignore index d30f40ef4..eaa993ae6 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ # misc .DS_Store +.env .env.local .env.development.local .env.test.local @@ -19,3 +20,5 @@ npm-debug.log* yarn-debug.log* yarn-error.log* + +.env diff --git a/package-lock.json b/package-lock.json index 79f52b34f..f394212f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -434,6 +434,15 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==" }, + "axios": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz", + "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", + "requires": { + "follow-redirects": "1.5.0", + "is-buffer": "1.1.6" + } + }, "axobject-query": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-0.1.0.tgz", @@ -2144,6 +2153,15 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "cors": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz", + "integrity": "sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY=", + "requires": { + "object-assign": "4.1.1", + "vary": "1.1.2" + } + }, "cosmiconfig": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", @@ -4657,6 +4675,28 @@ "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=" }, + "history": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/history/-/history-4.7.2.tgz", + "integrity": "sha512-1zkBRWW6XweO0NBcjiphtVJVsIQ+SXF29z9DVkceeaSLVMFXHool+fdCZD4spDCfZJCILPILc3bm7Bc+HRi0nA==", + "requires": { + "invariant": "2.2.4", + "loose-envify": "1.3.1", + "resolve-pathname": "2.2.0", + "value-equal": "0.4.0", + "warning": "3.0.0" + }, + "dependencies": { + "warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", + "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", + "requires": { + "loose-envify": "1.3.1" + } + } + } + }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -4667,6 +4707,11 @@ "minimalistic-crypto-utils": "1.0.1" } }, + "hoist-non-react-statics": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", + "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==" + }, "home-or-tmp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", @@ -8809,6 +8854,33 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-4.0.0.tgz", "integrity": "sha512-FlsPxavEyMuR6TjVbSSywovXSEyOg6ZDj5+Z8nbsRl9EkOzAhEIcS+GLoQDC5fz/t9suhUXWmUrOBrgeUvrMxw==" }, + "react-router": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-4.3.1.tgz", + "integrity": "sha512-yrvL8AogDh2X42Dt9iknk4wF4V8bWREPirFfS9gLU1huk6qK41sg7Z/1S81jjTrGHxa3B8R3J6xIkDAA6CVarg==", + "requires": { + "history": "4.7.2", + "hoist-non-react-statics": "2.5.5", + "invariant": "2.2.4", + "loose-envify": "1.3.1", + "path-to-regexp": "1.7.0", + "prop-types": "15.6.1", + "warning": "4.0.1" + } + }, + "react-router-dom": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.3.1.tgz", + "integrity": "sha512-c/MlywfxDdCp7EnB7YfPMOfMD3tOtIjrQlj/CKfNMBxdmpJP8xcz5P/UAFn3JbnQCNUxsHyVVqllF9LhgVyFCA==", + "requires": { + "history": "4.7.2", + "invariant": "2.2.4", + "loose-envify": "1.3.1", + "prop-types": "15.6.1", + "react-router": "4.3.1", + "warning": "4.0.1" + } + }, "react-scripts": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-1.1.4.tgz", @@ -9231,6 +9303,11 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=" }, + "resolve-pathname": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz", + "integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg==" + }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", @@ -10564,6 +10641,11 @@ "spdx-expression-parse": "3.0.0" } }, + "value-equal": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz", + "integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw==" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -10600,6 +10682,14 @@ "makeerror": "1.0.11" } }, + "warning": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.1.tgz", + "integrity": "sha512-rAVtTNZw+cQPjvGp1ox0XC5Q2IBFyqoqh+QII4J/oguyu83Bax1apbo2eqB8bHRS+fqYUBagys6lqUoVwKSmXQ==", + "requires": { + "loose-envify": "1.3.1" + } + }, "watch": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/watch/-/watch-0.10.0.tgz", diff --git a/package.json b/package.json index 951a38e2f..4fd34fc5b 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,12 @@ "version": "0.1.0", "private": true, "dependencies": { + "axios": "^0.18.0", + "cors": "^2.8.4", "react": "^16.4.1", "react-dom": "^16.4.1", + "react-router": "^4.3.1", + "react-router-dom": "^4.3.1", "react-scripts": "1.1.4" }, "scripts": { @@ -13,4 +17,4 @@ "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" } -} \ No newline at end of file +} diff --git a/src/App.css b/src/App.css index c5c6e8a68..f0ab8fd40 100644 --- a/src/App.css +++ b/src/App.css @@ -1,28 +1,56 @@ -.App { - text-align: center; +ul { + list-style-type: none; } -.App-logo { - animation: App-logo-spin infinite 20s linear; - height: 80px; +.navigation { + display: flex; + flex-wrap: wrap; } -.App-header { - background-color: #222; - height: 150px; - padding: 20px; - color: white; +a { + text-decoration: none; } -.App-title { - font-size: 1.5em; +img { + width: 260px; } -.App-intro { - font-size: large; +.shiba { + border-radius: 5%; } -@keyframes App-logo-spin { - from { transform: rotate(0deg); } - to { transform: rotate(360deg); } +.top-nav { + display: flex; + flex-direction: column; } + +.links img { + height: 200px; + width: 200px; + margin: 0 30px; +} + +.shiba-spot { + margin-right: 40px; +} + +header { + margin: 50px 100px 50px 175px; + padding-left: 50px; +} + +.top-nav .links { + padding: 30px; +} + +.top-nav .rental-container { + font-size: 1.6rem; +} + +.rental-info { + align-content: center; +} + +/* .body { + background-color: black; +} */ diff --git a/src/App.js b/src/App.js index 203067e4d..6ef006253 100644 --- a/src/App.js +++ b/src/App.js @@ -1,21 +1,119 @@ import React, { Component } from 'react'; -import logo from './logo.svg'; import './App.css'; +import Library from './components/Library'; +import Search from './components/Search'; +import CustomerCollection from'./components/CustomerCollection'; +import { BrowserRouter as Router, Route, Link } from "react-router-dom"; +import Rental from './components/Rental'; +import Movie from './components/Movie'; +import axios from 'axios'; +import PropTypes from 'prop-types'; +import Status from './components/Status'; + class App extends Component { - render() { - return ( -
-
- logo -

Welcome to React

-
-

- To get started, edit src/App.js and save to reload. -

-
- ); - } -} - -export default App; + static propTypes = { + updateStatusCallback: PropTypes.func, + } + + constructor() { + super(); + + this.state = { + movieName: "None", + customerName: "None", + customerID: null, + status: { + message: `Welcome to such movie so wow`, + type: 'success' + } + } + } + + updateStatus = (message, type) => { + this.setState({ + status: { + message: message, + type: type + } + }) + } + + selectMovie = (title) => { + this.setState({ + movieName: title + }); + } + + selectCustomer = (name, id) => { + this.setState({ + customerName: name, + customerID: id + }); + } + + + render() { + return ( + +
+
+
+
+ +
+
+
+ + + + + +
+ +
+
+ +
+
+ + + +
+
+ +
+ + + } + /> + + } + /> + } + /> + +
+
+ ); + } + } + + export default App; diff --git a/src/components/Customer.css b/src/components/Customer.css new file mode 100644 index 000000000..3c77aedb4 --- /dev/null +++ b/src/components/Customer.css @@ -0,0 +1,10 @@ +.single-customer { + width: 350px; + height: 475px; + border: 3px solid rgb(59, 89, 152); + border-radius: 5%; + background-color: rgb(223,227,238); + margin: 10px; + text-align: center; + padding-bottom: 17px; +} diff --git a/src/components/Customer.js b/src/components/Customer.js new file mode 100644 index 000000000..374b765ac --- /dev/null +++ b/src/components/Customer.js @@ -0,0 +1,34 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import './Customer.css'; + +class Customer extends React.Component{ + static propTypes = { + name: PropTypes.string, + customerID: PropTypes.number, + phone: PropTypes.string, + selectCustomerCallback: PropTypes.func + } + + selectCustomerCallback = () => { + this.props.selectCustomerCallback(this.props.name, this.props.customerID); + } + + render(){ + const images = ["penguin", "rabbit", "bunny", "panda", "robot", "dog"] + let imageIndex = images[Math.floor((Math.random() * 5) + 0)]; + let avatarImage = require(`../images/${imageIndex}.png`) + + return( +
+
+

{ this.props.name }

+ +

{ this.props.phone }

+ +
+
+ ) + } +} +export default Customer; diff --git a/src/components/CustomerCollection.css b/src/components/CustomerCollection.css new file mode 100644 index 000000000..3946ee129 --- /dev/null +++ b/src/components/CustomerCollection.css @@ -0,0 +1,5 @@ +.customer-display { + display: flex; + flex-wrap: wrap; + justify-content: center; +} diff --git a/src/components/CustomerCollection.js b/src/components/CustomerCollection.js new file mode 100644 index 000000000..93362c629 --- /dev/null +++ b/src/components/CustomerCollection.js @@ -0,0 +1,45 @@ +import React from 'react'; +import Customer from './Customer'; +import axios from 'axios'; +import './CustomerCollection.css'; + +class CustomerCollection extends React.Component{ + constructor(){ + super(); + + this.state = { + customers: [] + } + } + + componentDidMount(){ + axios.get('http://localhost:3000/customers') + .then((response) => { + this.props.updateStatusCallback(`Successfully loaded ${response.data.length} customers`, 'success'); + + this.setState({ customers: response.data}) + }) + .catch((error) => { + this.props.updateStatusCallback( error.message, 'error') + }); + } + render() { + const customerData = this.state.customers.map((customer, index) => { + return + }) + + return( +
+ { customerData } +
+ ) + } +} + +export default CustomerCollection; diff --git a/src/components/Library.css b/src/components/Library.css new file mode 100644 index 000000000..3d05a51a5 --- /dev/null +++ b/src/components/Library.css @@ -0,0 +1,5 @@ +.library { + display: flex; + flex-wrap: wrap; + justify-content: center; +} diff --git a/src/components/Library.js b/src/components/Library.js new file mode 100644 index 000000000..86de77991 --- /dev/null +++ b/src/components/Library.js @@ -0,0 +1,53 @@ +import React from 'react'; +import Movie from './Movie.js'; +import axios from 'axios'; +import './Library.css'; +import PropTypes from 'prop-types'; + +class Library extends React.Component { + static propTypes = { + updateStatusCallback: PropTypes.func, + selectMovieCallback: PropTypes.func + } + + constructor(props){ + super(props); + this.state = { + movies: [] + }; + } + componentDidMount(){ + axios.get('http://localhost:3000/movies') + .then((response) => { + this.props.updateStatusCallback(`Successfully loaded ${response.data.length} movies`, 'success'); + + this.setState({ movies: response.data}) + }) + .catch((error) => { + this.props.updateStatusCallback( error.message, 'error') + }); + } + + render() { + const movieData = this.state.movies.map((movie, index) => { + return + }) + + return( +
+ { movieData } +
+ ); + } +} + +export default Library; diff --git a/src/components/Movie.css b/src/components/Movie.css new file mode 100644 index 000000000..abf1edf88 --- /dev/null +++ b/src/components/Movie.css @@ -0,0 +1,30 @@ +.single-movie { + width: 350px; + height: 650px; + border: 3px solid rgb(59, 89, 152); + background-color: rgb(223,227,238); + margin: 10px; + text-align: center; + border-radius: 5%; +} + +button { + border: 3px solid rgb(59, 89, 152); + font-size: 1.2rem; + margin-top: 10px; + border-radius: 5%; +} + +.overview .overview-text { + visibility: hidden; + width: 300px; + background-color: rgb(59, 89, 152); + color: white; + text-align: center; + padding: 5px; + position: absolute; +} + +.overview:hover .overview-text { + visibility: visible; +} diff --git a/src/components/Movie.js b/src/components/Movie.js new file mode 100644 index 000000000..2deb7f532 --- /dev/null +++ b/src/components/Movie.js @@ -0,0 +1,57 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import './Movie.css'; + +class Movie extends React.Component{ + static propTypes = { + title: PropTypes.string, + image_url: PropTypes.string, + overview: PropTypes.string, + addMovieCallback: PropTypes.func, + selectMovieCallback: PropTypes.func, + external_id: PropTypes.number, + release_date: PropTypes.string, + buttonName: PropTypes.string + } + + createMovie = () => { + // console.log(this.props.title); + const movie = { + title: this.props.title, + external_id: this.props.external_id, + image_url: this.props.image_url, + inventory: 5, + release_date: this.props.release_date, + overview: this.props.overview + } + + this.props.addMovieCallback(movie) + } + + selectMovie = () => { + this.props.selectMovieCallback( this.props.title ) + } + + showButton = () => { + if (this.props.buttonName === "Maybe This One") { + return + } else if (this.props.buttonName === "Add to Library") { + return + } + } + + render(){ + + return( +
+
+

{ this.props.title }

+ movie-poster + { this.showButton() } +

Overview{this.props.overview }

+
+
+ ) + } +} +export default Movie; diff --git a/src/components/Rental.css b/src/components/Rental.css new file mode 100644 index 000000000..67419015a --- /dev/null +++ b/src/components/Rental.css @@ -0,0 +1,30 @@ + +.label { + font-size: 1.3rem; + font-weight: bold; + color: rgb(59, 89, 152); + border: 3px dotted rgb(139, 157, 195); + padding: 8px; + border-radius: 5%; +} + +.rental-container .names { + max-width: 200px; + color: rgb(59, 89, 152); +} + +.rental-container { + height: 70%; + margin: 30px 30px; + display: flex; + flex-direction: column; +} + +.movie-name, .customer-name { + margin: 20px 0px; +} + +.names { + margin-left: 20px; + font-size: 1.2rem; +} diff --git a/src/components/Rental.js b/src/components/Rental.js new file mode 100644 index 000000000..70414817b --- /dev/null +++ b/src/components/Rental.js @@ -0,0 +1,55 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import axios from 'axios'; + +import './Rental.css'; + +class Rental extends React.Component{ + + static propTypes = { + movieName: PropTypes.string, + customerName: PropTypes.string, + createRentalCallback: PropTypes.func, + updateStatusCallback: PropTypes.func, + customerID: PropTypes.number + } + + createRental = () => { + const urlBase = "http://localhost:3000/rentals/"; + const today = new Date(); + const due_date = new Date().setDate(today.getDate()+7); + const newRental = { + customer_id: this.props.customerID, + checkout_date: `${today}`, + due_date: `${new Date(due_date)}`, + returned: false + }; + + axios.post(`${urlBase}${this.props.movieName}/check-out`, newRental) + .then((response) => { + this.props.updateStatusCallback( + `Successfully checked out ${this.props.movieName} to ${this.props.customerName}`, 'success'); + }) + .catch((error) => { + this.props.updateStatusCallback( error.message, 'error') + }); + } + + render(){ + return ( +
+
+ Selected Movie + {this.props.movieName} +
+
+ Selected Customer + {this.props.customerName} +
+ +
+ ) + } + } + + export default Rental; diff --git a/src/components/Search.css b/src/components/Search.css new file mode 100644 index 000000000..caa30ddc7 --- /dev/null +++ b/src/components/Search.css @@ -0,0 +1,21 @@ +.search-bar { + background-color: rgb(59, 89, 152); + text-align: center; + margin: 50px 150px; +} + +.search-input { + text-align: center; + margin: 20px 50px; + font-size: 2.5rem; +} + +.search-input, .submit-search { + font-size: 1.5rem; + margin-bottom: 15px; + margin-right: 15px; +} + +.submit-search { + margin-left: 30px; +} diff --git a/src/components/Search.js b/src/components/Search.js new file mode 100644 index 000000000..aeb4cdc65 --- /dev/null +++ b/src/components/Search.js @@ -0,0 +1,81 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import axios from 'axios'; +import Movie from './Movie.js'; +import './Search.css'; + + +class Search extends React.Component{ + static propTypes = { + updateStatusCallback: PropTypes.func, + } + + state = { + query: '', + movies: [] + } + + handleInputChange = (event) => { + this.setState({ + query: event.target.value + }) + } + + listMovies(searchTerm) { + axios.get(`http://localhost:3000/movies/?query=${searchTerm}`) + .then((response) => { + this.props.updateStatusCallback(`Found ${response.data.length} results for ${ searchTerm }`, 'success'); + this.setState({ + movies: response.data + }) + }) + .catch((error) => { + this.props.updateStatusCallback( error.message, 'error') + }); + } + + onFormSubmit = (event) => { + event.preventDefault(); + + this.listMovies(this.state.query); + } + + addMovie = (movie) => { + axios.post('http://localhost:3000/movies', movie) + .then((response) => { + this.props.updateStatusCallback(`Successfully added movie ${ movie.title }`, 'success'); + }) + .catch((error) => { + this.props.updateStatusCallback( error.message, 'error') + }); + } + + render(){ + const movieList = this.state.movies.map((movie, index) => { + return + }) + + return( +
+
+ + +
+
+ { movieList } +
+
+ ) + } +} + +export default Search; diff --git a/src/components/Status.css b/src/components/Status.css new file mode 100644 index 000000000..4aa0c0496 --- /dev/null +++ b/src/components/Status.css @@ -0,0 +1,30 @@ +.false { + display: none; +} + +.status { + margin: 50px auto; + text-align: center; +} + +.success { + color: rgb(59, 89, 152); + font-size: 2rem; + font-weight: bold; +} + +.error { + color: red; +} + +.status button { + margin-left: 20px; + border-radius: 30%; +} + +.status { + border-top: 3px dashed rgb(139, 157, 195); + margin: 50px 150px; + padding-top: 50px; + +} diff --git a/src/components/Status.js b/src/components/Status.js new file mode 100644 index 000000000..6babac577 --- /dev/null +++ b/src/components/Status.js @@ -0,0 +1,29 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import './Status.css'; + +class Status extends React.Component { + state = { + display: true + } + + hideStatus = (event) => { + this.setState({display: false}) + } + + static propTypes = { + message: PropTypes.string.isRequired, + type: PropTypes.string + } + render() { + + return( +
+ { this.props.message } + +
+ ) + } +} + +export default Status; diff --git a/src/images/bunny.png b/src/images/bunny.png new file mode 100644 index 000000000..6b5a039f6 Binary files /dev/null and b/src/images/bunny.png differ diff --git a/src/images/customers.png b/src/images/customers.png new file mode 100644 index 000000000..e0e92af2c Binary files /dev/null and b/src/images/customers.png differ diff --git a/src/images/dog.png b/src/images/dog.png new file mode 100644 index 000000000..df3978d62 Binary files /dev/null and b/src/images/dog.png differ diff --git a/src/images/house.png b/src/images/house.png new file mode 100644 index 000000000..46f2857f4 Binary files /dev/null and b/src/images/house.png differ diff --git a/src/images/panda.png b/src/images/panda.png new file mode 100644 index 000000000..5edd19add Binary files /dev/null and b/src/images/panda.png differ diff --git a/src/images/penguin.png b/src/images/penguin.png new file mode 100644 index 000000000..c45164a0c Binary files /dev/null and b/src/images/penguin.png differ diff --git a/src/images/rabbit.png b/src/images/rabbit.png new file mode 100644 index 000000000..d95d9bdc1 Binary files /dev/null and b/src/images/rabbit.png differ diff --git a/src/images/robot.png b/src/images/robot.png new file mode 100644 index 000000000..d4fb3cfee Binary files /dev/null and b/src/images/robot.png differ diff --git a/src/images/search.png b/src/images/search.png new file mode 100644 index 000000000..7e454bc58 Binary files /dev/null and b/src/images/search.png differ diff --git a/src/images/shiba.png b/src/images/shiba.png new file mode 100644 index 000000000..916943977 Binary files /dev/null and b/src/images/shiba.png differ diff --git a/src/images/video.png b/src/images/video.png new file mode 100644 index 000000000..72ed3b869 Binary files /dev/null and b/src/images/video.png differ