diff --git a/package.json b/package.json index 9cafb5214..f8fc0eb8c 100644 --- a/package.json +++ b/package.json @@ -3,15 +3,17 @@ "version": "0.1.1", "private": true, "dependencies": { + "axios": "^0.21.1", "react": "^17.0.1", "react-dom": "^17.0.1", + "react-router-dom": "^5.2.0", "react-scripts": "4.0.1" }, "devDependencies": { - "gh-pages": "^3.1.0", "@testing-library/jest-dom": "^5.11.9", "@testing-library/react": "^11.2.3", - "@testing-library/user-event": "^12.6.0" + "@testing-library/user-event": "^12.6.0", + "gh-pages": "^3.1.0" }, "scripts": { "start": "PORT=3001 react-scripts start", diff --git a/src/ApiSupport.js b/src/ApiSupport.js new file mode 100644 index 000000000..906e3d5c1 --- /dev/null +++ b/src/ApiSupport.js @@ -0,0 +1,9 @@ +import axios from 'axios' + +const BASE_URL = 'http://localhost:3000'; + +const API = axios.create({ + baseURL: BASE_URL, +}); + +export default API; \ No newline at end of file diff --git a/src/App.css b/src/App.css index c5c6e8a68..bf07687e9 100644 --- a/src/App.css +++ b/src/App.css @@ -22,6 +22,37 @@ font-size: large; } +.App-cart { + float: right; + width: 25%; + border: 1px solid black; + border-radius: 5px; + margin-bottom: 12px; + padding: 20px; +} + +.Nav-bar { + list-style-type: none; + margin: 0; + overflow: hidden; + background-color: 000000; + display: block; + color: white; + text-align: center; + padding: 14px 16px; + text-decoration: none; +} + +li { + float: left; + display: inline; + float: left; + width: 25.0%; + color: white; + font-size: large; + +} + @keyframes App-logo-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } diff --git a/src/App.js b/src/App.js index 203067e4d..99ac2ad2b 100644 --- a/src/App.js +++ b/src/App.js @@ -1,21 +1,112 @@ -import React, { Component } from 'react'; -import logo from './logo.svg'; +import React, { Component, useState, useEffect } from 'react'; import './App.css'; +import { + BrowserRouter as Router, + Switch, + Route, + Link +} from 'react-router-dom'; +import Home from './components/Home'; +import Library from './components/Library'; +import Search from './components/Search'; +import CustomerList from './components/CustomerList'; +import Customer from './components/Customer'; +import API from './ApiSupport' +import './components/Library.css'; -class App extends Component { - render() { - return ( -
-
- logo -

Welcome to React

-
-

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

-
- ); + +const App = () => { + const [selectedCustomer, setSelectedCustomer] = useState(null); + const [selectedVideo, setSelectedVideo] = useState(null); + + const [errorMessage, setErrorMessage] = useState(null); + + + const handleCheckout = () => { + let todaysDate = new Date(); + todaysDate.setDate(todaysDate.getDate() + 7); + + API.post(`rentals/${selectedVideo.title}/check-out`, { + // eslint-disable-next-line camelcase + customer_id: selectedCustomer.id, + // eslint-disable-next-line camelcase + due_date: todaysDate, + + }) + .then((response) => { + alert(`${selectedVideo.title} successfully checked out to ${selectedCustomer.name}.`); + setSelectedVideo(null); + setSelectedCustomer(null); + }) + .catch((error) => { + alert(`Error: ${selectedVideo.title} can't be checked out to ${selectedCustomer.name}.`); + }); } +// this could be moved to the search item level if we wanted + const addVideo = (video) => { + API.post(`videos`, video) + .then((response) => { + // next time we visit the videos page retrieves videos again, so we don't need to update + // const updatedLibrary = [...videoList, response.data] + alert('Video successfully added'); + // setVideoList(updatedLibrary); + setErrorMessage(''); + }) + .catch((error) => { + alert('Video could not be added'); + }); + } + + return ( + +
+ + + + {selectedCustomer && selectedVideo ? : null} + {/* */} + {selectedCustomer !== null ? selectedCustomer.name : 'Select a customer' } + {selectedVideo !== null ? selectedVideo.title : ` \n Select a video` } + + + {/* A looks through its children s and + renders the first one that matches the current URL. */} + + + + + + + + + + + + + + + + + +
+
+ ); } + export default App; diff --git a/src/components/Customer.js b/src/components/Customer.js new file mode 100644 index 000000000..d4007da9e --- /dev/null +++ b/src/components/Customer.js @@ -0,0 +1,9 @@ +import React from 'react'; + +const Customer = (props) => { + return ( +
{props.name}
+ ) +} + +export default Customer; \ No newline at end of file diff --git a/src/components/CustomerList.js b/src/components/CustomerList.js new file mode 100644 index 000000000..5c83c0954 --- /dev/null +++ b/src/components/CustomerList.js @@ -0,0 +1,58 @@ +import React, { useState, useEffect } from 'react'; +// import axios from 'axios'; +import API from '../ApiSupport' +import Customer from './Customer'; +import PropTypes from 'prop-types'; +import { Link } from 'react-router-dom'; + +const CustomerList = (props) => { + // initially empty but changes during component's life cycle when API is called + const [customerList, setCustomerList] = useState([]); + const [errorMessage, setErrorMessage] = useState(null); + + useEffect(() => { + API.get(`customers`) + .then((response) => { + const apiCustomerList = response.data; + setCustomerList(apiCustomerList); + }) + .catch((error) => { + setErrorMessage('Could not retrieve customers'); + }); + }, []); + + const customerComponents = customerList.map((customer) => { + return ( + errorMessage == null ? + props.onCustomerSelected(customer)}> + + + : errorMessage + ) + }) + + return ( +
+ {customerComponents} +
+ ) +} + + +CustomerList.propTypes = { + onClickCustomer: PropTypes.func.isRequired, + name: PropTypes.string.isRequired, +} + +export default CustomerList; \ No newline at end of file diff --git a/src/components/Home.js b/src/components/Home.js new file mode 100644 index 000000000..ad439c9e7 --- /dev/null +++ b/src/components/Home.js @@ -0,0 +1,8 @@ +import React from 'react'; + +const Home = () => { + return
+
+} + +export default Home; \ No newline at end of file diff --git a/src/components/Library.css b/src/components/Library.css new file mode 100644 index 000000000..80e7c1c65 --- /dev/null +++ b/src/components/Library.css @@ -0,0 +1,7 @@ +.Video-box { + width: 25%; + border: 1px solid black; + border-radius: 5px; + margin-bottom: 12px; + padding: 20px; +} \ No newline at end of file diff --git a/src/components/Library.js b/src/components/Library.js new file mode 100644 index 000000000..273b26b1e --- /dev/null +++ b/src/components/Library.js @@ -0,0 +1,48 @@ +import React, { useEffect, useState} from 'react'; +import Video from './Video'; +import API from '../ApiSupport' +import { Link } from 'react-router-dom'; + +const Library = (props) => { + const [videoList, setVideoList] = useState([]); + const [errorMessage, setErrorMessage] = useState(null); + + useEffect(() => { + API.get(`videos`) + .then((response) => { + const apiVideoList = response.data; + setVideoList(apiVideoList); + }) + .catch((error) => { + setErrorMessage('Could not retrieve videos from library'); + console.error(error); + }); + }, []); + + + + const videoComponents = videoList.map((video) => { + return ( + errorMessage == null ? + props.onVideoSelected(video)}> +