diff --git a/package-lock.json b/package-lock.json index 79f52b34f..a98ee276c 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", @@ -1979,9 +1988,9 @@ } }, "colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.0.tgz", + "integrity": "sha512-EDpX3a7wHMWFA7PUHWPHNWqOxIIRSJetuwl0AS5Oi/5FMV8kWm69RTlgm00GKjBO1xFHMtBbL49yRtMMdticBw==" }, "combined-stream": { "version": "1.0.6", @@ -4657,6 +4666,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 +4698,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 +8845,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 +9294,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", @@ -9970,6 +10038,13 @@ "mkdirp": "0.5.1", "sax": "1.2.4", "whet.extend": "0.9.9" + }, + "dependencies": { + "colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=" + } } }, "sw-precache": { @@ -10564,6 +10639,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 +10680,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..6fb28c433 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,11 @@ "version": "0.1.0", "private": true, "dependencies": { + "axios": "^0.18.0", + "colors": "^1.3.0", "react": "^16.4.1", "react-dom": "^16.4.1", + "react-router-dom": "^4.3.1", "react-scripts": "1.1.4" }, "scripts": { @@ -13,4 +16,4 @@ "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" } -} \ No newline at end of file +} diff --git a/public/favicon.ico b/public/favicon.ico index a11777cc4..41e7e82d5 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/src/App.css b/src/App.css index c5c6e8a68..eed00d195 100644 --- a/src/App.css +++ b/src/App.css @@ -1,28 +1,93 @@ .App { text-align: center; -} - -.App-logo { - animation: App-logo-spin infinite 20s linear; - height: 80px; + position: absolute; + background-image: url(https://images.unsplash.com/photo-1505775561242-727b7fba20f0?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=c246da92e7f2654da6c3326e1b35b287&auto=format&fit=crop&w=2850&q=80); + height: 100%; + width: 100%; + color: white; + background-position: fixed; + background-repeat: no-repeat; + background-size: cover; + background-attachment: fixed; + background-size: cover; } .App-header { - background-color: #222; - height: 150px; + height: 20%; padding: 20px; color: white; + display: grid; + grid-template-columns: 1fr 2.7fr 3fr 1fr; + align-items: center; } .App-title { - font-size: 1.5em; + font-size: 2em; + display: inline; + border-right: white 2px solid; + padding-right: 20px; + text-decoration: none; + font-weight: bold; + color: #e31222; } .App-intro { font-size: large; } -@keyframes App-logo-spin { - from { transform: rotate(0deg); } - to { transform: rotate(360deg); } +.links { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + align-items: center; +} + +.selected-items { + display: grid; + grid-template-columns: 1fr 1fr; + align-items: center; + border-left: white 2px solid; + padding: 12px 0; +} + +main { + height: 67vh; + background-color: transparent; + overflow: scroll; +} + +button { + width: 120px; + height: 50px; + background-color: transparent; + /* border: solid transparent 2px; */ + padding: 5px; + margin: 10px; + color: white; + border: solid 2px; + border-radius: 3px; +} + +button:hover { + background-color: grey; +} + +.page-link { + text-decoration: none; + color: white; + padding: 0px 30px 0px 30px; +} + +.page-link:hover { + color: grey; } + +::-webkit-scrollbar { + -webkit-appearance: none; + width: 7px; +} + +::-webkit-scrollbar-thumb { + border-radius: 4px; + background-color: rgba(0, 0, 0, .5); + -webkit-box-shadow: 0 0 1px rgba(255, 255, 255, .5); +} \ No newline at end of file diff --git a/src/App.js b/src/App.js index 203067e4d..feee4256a 100644 --- a/src/App.js +++ b/src/App.js @@ -1,21 +1,142 @@ import React, { Component } from 'react'; -import logo from './logo.svg'; +import {BrowserRouter as Router, Route, Link } from 'react-router-dom'; +import MovieLibrary from './components/MovieLibrary'; +import CustomerList from './components/CustomerList'; +import Search from './components/Search'; +import axios from 'axios'; +import Status from './components/Status'; + import './App.css'; +const BASE_URL = "http://localhost:3300/rentals/" + class App extends Component { + + constructor() { + super(); + + this.state = { + title: "", + customer_name: "", + customer_id: 0, + status: { + message: null + } + } + } + + updateStatus = (message) => { + this.setState({ + status: { + message: message + } + }) + } + + clearStatus = () => { + this.setState({ + status: { + message: null + } + }) + } + + createRental = () => { + let RENTAL_URL = BASE_URL + `${this.state.title}` + '/check-out' + + let date = new Date(); + date.setDate(date.getDate() + 7); + + let params = {title: this.state.title, customer_id: this.state.customer_id, due_date: date}; + axios.post(RENTAL_URL, params) + .then((response)=>{ + console.log(`%c Successfully submitted a rental!`, 'font-weight: bold; font-size: 50px;color: red; text-shadow: 3px 3px 0 rgb(217,31,38) , 6px 6px 0 rgb(226,91,14) , 9px 9px 0 rgb(245,221,8) , 12px 12px 0 rgb(5,148,68) , 15px 15px 0 rgb(2,135,206) , 18px 18px 0 rgb(4,77,145) , 21px 21px 0 rgb(42,21,113)'); + this.updateStatus(`Successfully rented ${this.state.title} to ${this.state.customer_name}`) + }) + .catch((error)=>{ + console.log(`There was an error: ${error}`); + this.updateStatus(`You must select a movie and customer to create a rental`) + }); + } + + // function that changes state + updateMovie = (movie) => { + let updatedState = this.state; + updatedState["title"] = movie.title; + this.setState(updatedState); + this.updateStatus(`Successfully added ${this.state.title} to your rental!`); + } + + updateCustomer = (cust_name, cust_id) => { + let updatedState = this.state; + updatedState["customer_name"] = cust_name; + updatedState["customer_id"] = cust_id; + this.setState(updatedState); + this.updateStatus(`Successfully added ${this.state.customer_name} to your rental!`); + } + render() { + return ( -
-
- logo -

Welcome to React

-
-

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

-
- ); - } -} -export default App; + + +
+ VIDEO STORE + +
+ Movies + Customers + Search +
+ +
+

Selected Movie: {this.state.title}

+

Selected Customer: {this.state.customer_name}

+
+ + +
+ + + +
+ + { + return () + }} /> + + { + return () + }} + /> + + { + return ( + ) + }} + /> +
+ + +
+ + + + ); + } + } + + export default App; diff --git a/src/components/Customer.css b/src/components/Customer.css new file mode 100644 index 000000000..10dc9630c --- /dev/null +++ b/src/components/Customer.css @@ -0,0 +1,40 @@ +.customer { + padding: 1em 0; + margin: 0.5rem; + min-height: 150px; + min-width: 225px; + flex-basis: 18%; + border-radius: 5px; + display: flex; + flex-direction: column; + grid-template-columns: 1fr; + align-items: center; + justify-content: center; +} + +.customer h3 { + font-weight: bold; + font-size: 1.5em; + padding: 0.25em; +} + +.customer button { + width: 50%; + margin: 0; + text-align: center; + padding: 0.5em; +} + +.customer h3 { + width: 80%; +} + +.customer_content { + grid-column-start: 1; + display: flex; + flex-direction: column; + justify-content: space-around; + text-align: center; + font-weight: 100; + overflow: hidden; +} \ No newline at end of file diff --git a/src/components/Customer.js b/src/components/Customer.js new file mode 100644 index 000000000..ca48fb28a --- /dev/null +++ b/src/components/Customer.js @@ -0,0 +1,31 @@ +import React, { Component } from 'react'; +import './Customer.css'; +import PropTypes from 'prop-types'; + +class Customer extends Component { + buttonClick = () => { + let cust_name = this.props.customerName; + let cust_id = this.props.id; + this.props.callBack(cust_name, cust_id); + } + render(){ + return( +
+

{this.props.customerName}

+ +
+ ); + } +} + +Customer.propTypes = { + customerName: PropTypes.string.isRequired, + id: PropTypes.number.isRequired, + callBack: PropTypes.func.isRequired + +}; + +export default Customer; diff --git a/src/components/CustomerList.css b/src/components/CustomerList.css new file mode 100644 index 000000000..2b1633118 --- /dev/null +++ b/src/components/CustomerList.css @@ -0,0 +1,4 @@ +.customer-list { + display: flex; + flex-wrap: wrap; +} \ No newline at end of file diff --git a/src/components/CustomerList.js b/src/components/CustomerList.js new file mode 100644 index 000000000..3913837ed --- /dev/null +++ b/src/components/CustomerList.js @@ -0,0 +1,53 @@ +import React, { Component } from 'react'; +import Customer from './Customer'; +import axios from 'axios'; +import './CustomerList.css' +import PropTypes from 'prop-types'; + +const CUST_URL = "http://localhost:3300/customers"; + +class CustomerList extends Component { + constructor(){ + super(); + + this.state = { + customers: [] + }; + } + + componentDidMount(){ + axios.get(CUST_URL) + .then((response)=>{ + this.setState({customers: response.data}); + this.props.updateStatusCallback(`Successfully loaded ${response.data.length} customers!`); + console.log(`%c Successfully loaded customers!`, 'font-weight: bold; font-size: 50px;color: red; text-shadow: 3px 3px 0 rgb(217,31,38) , 6px 6px 0 rgb(226,91,14) , 9px 9px 0 rgb(245,221,8) , 12px 12px 0 rgb(5,148,68) , 15px 15px 0 rgb(2,135,206) , 18px 18px 0 rgb(4,77,145) , 21px 21px 0 rgb(42,21,113)'); + }) + .catch((error)=>{ + console.log(`There was an error: ${error}`); + }); + } + + render(){ +// map response to customer list +const customers = this.state.customers.map((customer, index)=>{ + return +}); + + return( +
+ { customers } +
+ ); + } +} + +CustomerList.propTypes = { + callBack: PropTypes.func.isRequired, + updateStatusCallback: PropTypes.func +}; + +export default CustomerList; diff --git a/src/components/Movie.css b/src/components/Movie.css new file mode 100644 index 000000000..77ac7051b --- /dev/null +++ b/src/components/Movie.css @@ -0,0 +1,48 @@ +.movie { + padding: 1em 0; + margin: 0.5rem; + min-height: 250px; + min-width: 225px; + flex-basis: 18%; + border-radius: 5px; + display: grid; + grid-template-columns: 1fr; + align-items: center; + justify-items: center; +} + +.movie h3 { + font-weight: bold; + font-size: 1.5em; + margin: 0.1em; +} + +.movie button { + width: 73%; + margin: 0; + position: center; + padding: 0 15%; +} + +.movie h3 { + width: 80%; +} + +.movie__content { + grid-column-start: 1; + display: flex; + flex-direction: column; + justify-content: space-around; + text-align: center; + font-weight: 100; + overflow: hidden; +} + +img.movie:hover { + width: 100%; + /* min-width: 236px; */ +} + +* { + /* border: yellow dotted 1px; */ +} \ No newline at end of file diff --git a/src/components/Movie.js b/src/components/Movie.js new file mode 100644 index 000000000..ff90cd611 --- /dev/null +++ b/src/components/Movie.js @@ -0,0 +1,45 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; + import './Movie.css'; + +class Movie extends Component { +// this is a change + buttonClick = () => { + let movie = { + title: this.props.movieTitle, + overview: this.props.overview, + release_date: this.props.releaseDate, + image_url: this.props.image, + external_id: this.props.extID, + inventory: 10 + } + + this.props.callBack(movie); + } + render(){ + + return( +
+ {`${this.props.movieTitle}'s +

{this.props.movieTitle}

+ +
+ + ) + } +} + +Movie.propTypes = { + movieTitle: PropTypes.string.isRequired, + overview: PropTypes.string, + releaseDate: PropTypes.string, + image: PropTypes.string, + extID: PropTypes.number, + inventory: PropTypes.number, + buttonText: PropTypes.string, + callBack: PropTypes.func.isRequired +}; + +export default Movie; diff --git a/src/components/MovieLibrary.css b/src/components/MovieLibrary.css new file mode 100644 index 000000000..41177188d --- /dev/null +++ b/src/components/MovieLibrary.css @@ -0,0 +1,4 @@ +.movielibrary { + display: flex; + flex-wrap: wrap; +} \ No newline at end of file diff --git a/src/components/MovieLibrary.js b/src/components/MovieLibrary.js new file mode 100644 index 000000000..1f92d1f91 --- /dev/null +++ b/src/components/MovieLibrary.js @@ -0,0 +1,57 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import axios from 'axios'; +import Movie from './Movie'; +import './MovieLibrary.css'; + +const MOVIES_URL = "http://localhost:3300/movies"; + +class MovieLibrary extends Component { + + constructor(){ + super(); + + this.state = { + movies: [] + }; + } + + componentDidMount(){ + axios.get(MOVIES_URL) + .then((response)=>{ + this.setState({movies: response.data}); + console.log(`%c Successfully loaded movies!`, 'font-weight: bold; font-size: 50px;color: red; text-shadow: 3px 3px 0 rgb(217,31,38) , 6px 6px 0 rgb(226,91,14) , 9px 9px 0 rgb(245,221,8) , 12px 12px 0 rgb(5,148,68) , 15px 15px 0 rgb(2,135,206) , 18px 18px 0 rgb(4,77,145) , 21px 21px 0 rgb(42,21,113)'); + this.props.updateStatusCallback(`Successfully loaded ${response.data.length} movies!`); + }) + .catch((error)=>{ + console.log(`There was an error: ${error}`); + }); + } + + render() { + + const movies = this.state.movies.map((movie,index)=>{ + return + }) + return( +
+ {movies} +
+ ); + } +} + +MovieLibrary.propTypes = { + callBack: PropTypes.func.isRequired, + updateStatusCallback: PropTypes.func +}; +export default MovieLibrary; diff --git a/src/components/RentalInfo.js b/src/components/RentalInfo.js new file mode 100644 index 000000000..5dba0a394 --- /dev/null +++ b/src/components/RentalInfo.js @@ -0,0 +1,19 @@ +import React, { Component } from 'react'; +import {BrowserRouter as Router, Route, Link } from 'react-router-dom'; +import axios from 'axios'; + +class RentalInfo extends Component { + + render(){ + console.log(this.state.movie); + return( +
+

selected movie: {this.state.movie.title}

+

selected customer: {this.state.customer.name}

+
+ ); + } +} + + +export default RentalInfo; diff --git a/src/components/Search.js b/src/components/Search.js new file mode 100644 index 000000000..a800a198f --- /dev/null +++ b/src/components/Search.js @@ -0,0 +1,86 @@ +import React, { Component } from 'react'; +import SearchBar from './SearchBar'; +import axios from 'axios'; +import Movie from './Movie'; +import './MovieLibrary.css'; +import PropTypes from 'prop-types'; + +const DB_URL = "http://localhost:3300/movies?query="; +const API_URL = "http://localhost:3300/movies"; + +class Search extends Component { + constructor(){ + super(); + + this.state = { + dbMovies: [] + }; + } + + addToLibrary = (movie) => { + // grab movie from state (that exists becuse of search) + // create movie object + // pass to axios with POST URL + movie object we just made + console.log(movie); + + axios.post(API_URL, movie) + .then((response)=>{ + console.log(API_URL); + console.log(`%c Successfully performed a search!`, 'font-weight: bold; font-size: 50px;color: red; text-shadow: 3px 3px 0 rgb(217,31,38) , 6px 6px 0 rgb(226,91,14) , 9px 9px 0 rgb(245,221,8) , 12px 12px 0 rgb(5,148,68) , 15px 15px 0 rgb(2,135,206) , 18px 18px 0 rgb(4,77,145) , 21px 21px 0 rgb(42,21,113)'); + this.props.updateStatusCallback(`Successfully added ${movie.title} to your Movie Library!`) + }) + .catch((error)=>{ + console.log(`there was an error: ${error.message}`); + this.props.updateStatusCallback(`Failed to add movie: ${error.message}`) + }); + } + + clickHandler = () => { + this.props.clearStatusCallback(); + } + + externalQuery = (query) => { + axios.get(DB_URL+query) + .then((response) => { + this.setState({ + dbMovies: response.data + }) + console.log(response.data); + this.props.updateStatusCallback(`Found ${response.data.length} movie titles related to your search!`); + }) + .catch((error) => { + console.log(`There was an error: ${error}`); + this.props.updateStatusCallback(`Failed to load movies: ${error.message}`); + }); + } + + render(){ + const dbMovies = this.state.dbMovies.map((movie, index)=>{ + return + }) + + return( +
+ +
+ {dbMovies} +
+
+ ) + } +} + +Search.propTypes = { + updateStatusCallback: PropTypes.func +}; + +export default Search; diff --git a/src/components/SearchBar.js b/src/components/SearchBar.js new file mode 100644 index 000000000..066fb574c --- /dev/null +++ b/src/components/SearchBar.js @@ -0,0 +1,53 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; + + +class SearchBar extends Component { + + constructor(){ + super(); + + this.state = { + query: "" + }; + } + + onInputChange = (event) => { + this.setState({query: event.target.value}) + } + + onFormSubmit = (event) => { + event.preventDefault(); + + this.props.externalDBQueryCallback(this.state.query); + + this.setState( { + query: "" + }) + } + + render() { + + return( +
+ + + +
+ + ); + } +} + +SearchBar.propTypes = { + externalDBQueryCallback: PropTypes.func.isRequired + +}; + +export default SearchBar; diff --git a/src/components/Status.js b/src/components/Status.js new file mode 100644 index 000000000..be143e5bc --- /dev/null +++ b/src/components/Status.js @@ -0,0 +1,18 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +class Status extends React.Component { + static propTypes = { + message: PropTypes.string + } + + render() { + return ( +
+ {this.props.message} +
+ ); + } +} + +export default Status; diff --git a/src/index.css b/src/index.css index b4cc7250b..6d5e91ee3 100644 --- a/src/index.css +++ b/src/index.css @@ -3,3 +3,7 @@ body { padding: 0; font-family: sans-serif; } + +ul { + list-style-type: none; +} \ No newline at end of file