Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Water - Beatrice and Jessica #18

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3132b46
Ability to search for videos from external API and add them to library
Beatress Jan 20, 2021
6976cbd
added axios install
jwinchan Jan 20, 2021
fbf4192
created framework for React Router
jwinchan Jan 20, 2021
16d983f
changed nav links to video and customers
jwinchan Jan 20, 2021
934435a
created videocollection component; homepage can display list of videos
jwinchan Jan 20, 2021
fa252b3
created customer and customercollection components; can render on web…
jwinchan Jan 20, 2021
7d97ec9
Merge pull request #1 from jwinchan/homepage
jwinchan Jan 21, 2021
80c9755
Able to select customer/video, starting work on checkout function
Beatress Jan 21, 2021
2666c96
completed checkout functionality
jwinchan Jan 21, 2021
ebf87a6
added bootstrap and styling to navbar
jwinchan Jan 21, 2021
2c6898b
deleted navbar component
jwinchan Jan 21, 2021
fa236b4
updated styling
jwinchan Jan 21, 2021
d24d03d
Merge pull request #2 from jwinchan/styling
jwinchan Jan 21, 2021
84645eb
changed route endpoints
jwinchan Jan 21, 2021
2ebcd71
Rename errorMessage -> checkoutMessage, conditionally move checkout m…
Beatress Jan 21, 2021
0ec0d0a
Merge pull request #3 from jwinchan/move-navbar
jwinchan Jan 21, 2021
b1588f1
fixed nav bar links
jwinchan Jan 22, 2021
9b2782d
styled search bar
jwinchan Jan 22, 2021
c63c65d
styled checkoutmessage
jwinchan Jan 22, 2021
af51020
color styling
jwinchan Jan 22, 2021
b80ac7c
Merge pull request #4 from jwinchan/alerts
Beatress Jan 22, 2021
8a8fe07
Unified syntax of selectedVideo and selectedCustomer
Beatress Jan 22, 2021
5961413
Added PropTypes, removed unneeded comments, fixed ESlint warnings
Beatress Jan 22, 2021
5de0534
Merge pull request #5 from jwinchan/proptypes
jwinchan Jan 22, 2021
220771d
fixed styling for checkout message
jwinchan Jan 22, 2021
747411d
changed header font
jwinchan Jan 22, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@
"single",
{ "allowTemplateLiterals": true, "avoidEscape": true }
],
"camelcase": "error"
"camelcase": "warn"
}
}
}
41,864 changes: 34,283 additions & 7,581 deletions package-lock.json

Large diffs are not rendered by default.

11 changes: 8 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,20 @@
"version": "0.1.1",
"private": true,
"dependencies": {
"axios": "^0.21.1",
"bootstrap": "^4.6.0",
"react": "^17.0.1",
"react-bootstrap": "^1.4.3",
"react-dom": "^17.0.1",
"react-scripts": "4.0.1"
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.1",
"save": "^2.4.0"
},
"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",
Expand Down
32 changes: 31 additions & 1 deletion src/App.css
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
@import url('https://fonts.googleapis.com/css2?family=Bodoni+Moda:wght@700&display=swap');

body {
padding-top: 65px;
}

body, h1, h2, h3, h4, h5, h6 {
font-family: 'Bodoni Moda', serif;
}

.App {
text-align: center;
}

.App-logo {
animation: App-logo-spin infinite 20s linear;
height: 80px;
height: 700px;
}

.App-header {
max-width: 100%;
background-color: #222;
height: 150px;
padding: 20px;
Expand All @@ -22,6 +33,25 @@
font-size: large;
}

.checkout {
color:aliceblue;
padding: 8px;
}


span {
color:aliceblue;
padding: 20px;
}

ul {
list-style-type: none;
}

li {
list-style-type: none;
}

@keyframes App-logo-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
Expand Down
118 changes: 106 additions & 12 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,115 @@
import React, { Component } from 'react';
import React, {useState } from 'react';
import { BrowserRouter as Router, Link, Route, Switch } from 'react-router-dom';
import logo from './logo.svg';
import axios from 'axios';
import './App.css';
import AddMovieForm from './components/AddMovieForm';
import VideoCollection from './components/VideoCollection';
import CustomerCollection from './components/CustomerCollection';
import { Navbar, Nav, Button } from 'react-bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';


const API_URL_BASE = 'http://localhost:3000/';

const App = () => {
const [selectedVideo, setSelectedVideo] = useState('None');
const [selectedCustomer, setSelectedCustomer] = useState({
id: null,
name: 'None',
});
const [checkoutMessage, setcheckoutMessage] = useState(null);


const selectVideo = (title) => {
setcheckoutMessage('');
setSelectedVideo(title);
};

const selectCustomer = (id, name) => {
setcheckoutMessage('');
setSelectedCustomer({
id: id,
name: name,
})
};

const checkOut = () => {
const today = new Date();
today.setDate(today.getDate() + 7);

if (selectedCustomer.name === 'None' || selectedVideo === 'None') {
setcheckoutMessage('Need to select a customer and video')
return null
}

axios.post(API_URL_BASE + 'rentals/' + selectedVideo + '/check-out?customer_id=' + selectedCustomer.id + '&due_date=' + today.toString())
.then((response) => {
console.log(response);
setcheckoutMessage(`Checked out ${selectedVideo} successfully to ${selectedCustomer.name}!`);
setSelectedVideo('None');
setSelectedCustomer({
id: null,
name: 'None',
});
})
.catch((error) => {
console.log(error);
setcheckoutMessage(error.message);
});
};

const checkoutMessageNav = () => {
if (checkoutMessage) {
return (<div className='checkout'>{checkoutMessage}</div>);
} else {
return (
<div>
<span>Selected Video: {selectedVideo}</span>
<span>Selected Customer: {selectedCustomer.name}</span>
</div>)
}
};

class App extends Component {
render() {
return (

<Router>
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
<Navbar fixed='top' bg="dark" variant="dark">
<Nav className="mr-auto">
<Nav.Link as={Link}to='/'>Home</Nav.Link>
<Nav.Link as={Link} to='/library'>Library</Nav.Link>
<Nav.Link as={Link} to='/customers'>Customers</Nav.Link>
<Nav.Link as={Link} to='/search'>Search</Nav.Link>
</Nav>
{/* <Navbar.Header>Hello</Navbar.Header> */}
{checkoutMessageNav()}
<Button variant="outline-info" onClick={checkOut}>Check Out</Button>
</Navbar>

<Switch>
<Route path="/library">
<VideoCollection onSelectVideo={selectVideo} showButton='select'/>
</Route>
<Route path="/customers">
<CustomerCollection onSelectCustomer={selectCustomer}/>
</Route>
<Route path="/search">
<AddMovieForm showButton='add'/>
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</div>
);
}
</Router>
);
}

function Home() {
return (
<img src={logo} className="App-logo" alt="logo" />
);
}

export default App;
4 changes: 4 additions & 0 deletions src/components/AddMovieForm.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/* .movie-search-page {
display: flex;
justify-content: center;
} */
95 changes: 95 additions & 0 deletions src/components/AddMovieForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import axios from 'axios';
import Video from './Video';
import { Button, Form, FormControl } from 'react-bootstrap';
import './AddMovieForm.css';

const AddMovieForm = (props) => {
const BASE_URL = 'http://localhost:3000'

// Use promises to allow app to sleep between API calls
const { promisify } = require('util')
const sleep = promisify(setTimeout)

const [movieName, setMovieName] = useState('');
const [videoList, setVideoList] = useState([]);
const [errorMessage, setErrorMessage] = useState(null);

const search = (movieName) => {
sleep(2000).then(axios.get(BASE_URL + '/videos?query=' + movieName)
.then((response) => {
console.log(videoList);
setVideoList(response.data);
})
.catch((error) => {
setErrorMessage(error.message);
console.log(error.message);
}));
};

const onMovieChange = (event) => {
setMovieName(event.target.value);
};

const onSearch = (event) => {
setErrorMessage(null);
event.preventDefault();
if (movieName !== '') {
search(movieName);
}
setMovieName('');
};

const addToLibrary = (externalId) => {
console.log(externalId);
sleep(2000).then(axios.post(BASE_URL + '/videos/' + externalId + '/1')
.then((response) => {
// We return this to the video Component
console.log(response);
return response
})
.catch((error) => {
console.log(error.message);
return error.message
}));
};

const videoListDisplay = () => {
if (videoList === []) {
setErrorMessage('test')
}

return videoList.map((video, i) => {
return(
<li key={i}>
<Video video={video} onAddLibraryCallback={addToLibrary}
external_id={video.external_id}
showButton={props.showButton}/>
</li>
)});
}

return (
<div className="movie-search-page">
<div className="movie-search-form">
<Form onSubmit={onSearch}>
{ errorMessage ? <div><h2 className="error-msg">{errorMessage}</h2></div> : '' }
<div>
<FormControl type='text' placeholder='Search Movie' onChange={onMovieChange} value={movieName}/>
<Button type="submit" variant="outline-info">Search</Button>
</div>
</Form>
</div>
<div className="video-list">
{videoListDisplay()}
</div>
</div>
)
}

AddMovieForm.propTypes = {
showButton: PropTypes.string.isRequired,
};

export default AddMovieForm;
Empty file added src/components/Customer.css
Empty file.
27 changes: 27 additions & 0 deletions src/components/Customer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button } from 'react-bootstrap';

import './Customer.css'

const Customer = (props) => {
return (
<ul className="customer-row">
<div class="customer-text">
<li><h1>{props.customer.name}</h1></li>
<li><strong>Address: </strong>{props.customer.address}</li>
<li>{props.customer.city}, {props.customer.state} {props.customer.postal_code}</li>
<li><strong>Phone: </strong>{props.customer.phone}</li>
<li><strong>Account Credit: </strong>${props.customer.account_credit}</li>
<li><strong>Videos Checked Out: </strong>{props.customer.videos_checked_out_count}</li>
<li><Button variant="outline-info" onClick={() => props.onSelectCustomer(props.customer.id, props.customer.name)}>Select Customer</Button></li>
</div>
</ul>
);
}

Customer.propTypes = {
onSelectCustomer: PropTypes.func.isRequired,
};

export default Customer;
45 changes: 45 additions & 0 deletions src/components/CustomerCollection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React, { useState, useEffect} from 'react';
import PropTypes from 'prop-types';
import Customer from './Customer';
import axios from 'axios';

const API_URL_BASE = 'http://localhost:3000/customers';

const CustomerCollection = (props) => {

const [customerList, setCustomerList] = useState([]);
const [errorMessage, setErrorMessage] = useState(null);

useEffect(() => {
axios.get(API_URL_BASE)
.then((response) => {
setCustomerList(response.data);
})
.catch((error) => {
setErrorMessage(error.message);
});
}, []);

const customerComponents = customerList.map((customer, i) => {
return (
<div key={i}>
<Customer
customer={customer} onSelectCustomer={props.onSelectCustomer}
/>
</div>
);
});

return (
<div className = "CustomerCollection">
{errorMessage ? <div><h2 className="error-msg">{errorMessage}</h2></div> : ''}
{customerComponents}
</div>
);
};

CustomerCollection.propTypes = {
onSelectCustomer: PropTypes.func.isRequired,
};

export default CustomerCollection;
Loading