Skip to content

Commit b02fe93

Browse files
alysivjiJoeJasinski
authored andcommitted
Update UX and Create Deterministic Build (#11)
* Clean up UI * Change yarn build step to copy file names
1 parent 467a85e commit b02fe93

File tree

12 files changed

+268
-91
lines changed

12 files changed

+268
-91
lines changed

frontend/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@
66
"bootstrap": "^4.1.0",
77
"react": "^16.3.2",
88
"react-dom": "^16.3.2",
9+
"react-router-dom": "^4.2.2",
910
"react-scripts": "1.1.1",
1011
"reactstrap": "^5.0.0-beta.3"
1112
},
1213
"scripts": {
1314
"start": "react-scripts start",
1415
"build": "react-scripts build",
15-
"postbuild": "rm -rf ../talkvoter/talkvoter/static && mv build/static ../talkvoter/talkvoter && mv build/index.html ../talkvoter/talkvoter/templates",
16+
"postbuild": "cp -v build/static/js/main.*.js ../talkvoter/talkvoter/static/js/main.js && cp -v build/static/css/main.*.css ../talkvoter/talkvoter/static/css/main.css",
1617
"test": "react-scripts test --env=jsdom",
1718
"eject": "react-scripts eject"
1819
}

frontend/public/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
-->
1111
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
1212
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
13+
<link rel="stylesheet" type="text/css" href="//fonts.googleapis.com/css?family=Pacifico" />
1314

1415
<title>Talk Recommender</title>
1516
</head>

frontend/src/App.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.navbar-title {
2+
font-family: Pacifico, Helvetica, sans-serif;
3+
font-size: 24px;
4+
}

frontend/src/App.js

Lines changed: 58 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,71 @@
1-
import React from 'react';
2-
import { Card, CardBody, CardFooter, CardTitle, CardSubtitle, CardText} from 'reactstrap';
3-
import { Container, Row, Col } from 'reactstrap';
4-
import { Button } from 'reactstrap';
1+
import React, { Component } from 'react';
2+
import { BrowserRouter as Router, Route, Link, Switch } from 'react-router-dom';
3+
import {
4+
Container, Row, Col,
5+
Navbar, NavbarBrand, Nav,
6+
UncontrolledDropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap';
7+
import VoteCard from './components/VoteCard';
58

6-
class App extends React.Component {
7-
constructor(props) {
8-
super(props);
9-
this.state = {
10-
result: {},
11-
talk_id: null,
12-
};
13-
this.fetchRandomTalk()
14-
}
9+
import './App.css';
1510

16-
fetchRandomTalk() {
17-
/*
18-
Hit Flask endpoint to get talk from last year that user has not voted on
19-
*/
20-
fetch('/api/v1/talks/random/', {
21-
method: 'GET',
22-
headers: {'Content-Type': 'application/json'},
23-
credentials: "same-origin",
24-
}).then(response => response.json())
25-
.then(data => this.setState({
26-
result: data,
27-
talk_id: data['id'],
28-
}));
11+
class Foo extends Component {
12+
render() {
13+
return (
14+
<div>
15+
<h2>Page Foo</h2>
16+
</div>
17+
);
2918
}
19+
}
3020

31-
vote(event, vote_type) {
32-
/*
33-
Process vote user vote and load next talk
34-
*/
35-
event.preventDefault();
36-
37-
let body_ = {
38-
"vote": null
39-
};
21+
const NavigationBar = () => (
22+
<Navbar color="dark" dark expand="md">
23+
<NavbarBrand className="mx-auto" href="/">
24+
<div className="navbar-title">Talk Recommender</div>
25+
</NavbarBrand>
4026

41-
if (vote_type === 'yes') {
42-
body_['vote'] = 'in_person'
43-
} else {
44-
body_['vote'] = 'watch_later'
45-
}
27+
<Nav navbar>
28+
<UncontrolledDropdown nav inNavbar>
29+
<DropdownToggle nav caret></DropdownToggle>
30+
<DropdownMenu right>
31+
<DropdownItem tag={Link} to="/">Vote</DropdownItem>
32+
<DropdownItem tag={Link} to="/predict">Predict</DropdownItem>
33+
<DropdownItem divider />
34+
<DropdownItem href="/login">Login</DropdownItem>
35+
<DropdownItem href="/logout">Logout</DropdownItem>
36+
</DropdownMenu>
37+
</UncontrolledDropdown>
38+
</Nav>
39+
</Navbar>
40+
)
4641

47-
let that = this; // this trick never fails :D
42+
const Content = () => (
43+
<Container>
44+
<Row>
45+
<Col>
46+
<Switch>
47+
<Route exact path="/" component={VoteCard} />
48+
<Route path="/predict" component={Foo} />
49+
</Switch>
50+
</Col>
51+
</Row>
52+
</Container>
53+
)
4854

49-
fetch('/api/v1/talks/' + this.state.talk_id + '/vote/', {
50-
method: 'POST',
51-
body: JSON.stringify(body_),
52-
headers: {'Content-Type': 'application/json'},
53-
credentials: "same-origin",
54-
}).then(function(response) {
55-
if (response.status === 200) {
56-
that.fetchRandomTalk();
57-
} else if (response.status === 404) {
58-
// TODO: voted on all talks, go to predict
59-
console.log('Voted on all talks, time to predict')
60-
}
61-
});
62-
}
55+
const Layout = () => (
56+
<Router>
57+
<div>
58+
<NavigationBar />
59+
<br />
60+
<Content />
61+
</div>
62+
</Router>
63+
)
6364

65+
class App extends Component {
6466
render() {
6567
return (
66-
<Container>
67-
<Row>
68-
<Col>
69-
<Card>
70-
<CardBody>
71-
<CardTitle tag="h3" className="text-center">{this.state.result['title']}</CardTitle>
72-
<CardSubtitle className="text-center"><small>Presented by: {this.state.result['presenters']}</small></CardSubtitle>
73-
<br />
74-
<CardText>{this.state.result['description']}</CardText>
75-
</CardBody>
76-
<CardFooter>
77-
<Button color="danger" className="float-left" onClick={(e) => this.vote(e, 'no')}>Watch Later</Button>
78-
<Button color="success" className="float-right" onClick={(e) => this.vote(e, 'yes')}>In Person</Button>
79-
</CardFooter>
80-
</Card>
81-
</Col>
82-
</Row>
83-
</Container>
68+
<Layout />
8469
);
8570
}
8671
}

frontend/src/components/VoteCard.js

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import React, { Component } from 'react';
2+
import {
3+
Card, CardBody, CardFooter, CardTitle, CardSubtitle, CardText,
4+
Button } from 'reactstrap';
5+
6+
class VoteCard extends Component {
7+
constructor(props) {
8+
super(props);
9+
this.state = {
10+
result: {},
11+
talk_id: null,
12+
};
13+
this.fetchRandomTalk()
14+
}
15+
16+
fetchRandomTalk() {
17+
/*
18+
Hit Flask endpoint to get talk from last year that user has not voted on
19+
*/
20+
fetch('/api/v1/talks/random/', {
21+
method: 'GET',
22+
headers: {'Content-Type': 'application/json'},
23+
credentials: "same-origin",
24+
}).then(response => response.json())
25+
.then(data => this.setState({
26+
result: data,
27+
talk_id: data['id'],
28+
}));
29+
}
30+
31+
vote(event, vote_type) {
32+
/*
33+
Process vote user vote and load next talk
34+
*/
35+
event.preventDefault();
36+
37+
let body_ = {
38+
"vote": null
39+
};
40+
41+
if (vote_type === 'yes') {
42+
body_['vote'] = 'in_person'
43+
} else {
44+
body_['vote'] = 'watch_later'
45+
}
46+
47+
let that = this; // this trick never fails :D
48+
49+
fetch('/api/v1/talks/' + this.state.talk_id + '/vote/', {
50+
method: 'POST',
51+
body: JSON.stringify(body_),
52+
headers: {'Content-Type': 'application/json'},
53+
credentials: "same-origin",
54+
}).then(function(response) {
55+
if (response.status === 200) {
56+
that.fetchRandomTalk();
57+
} else if (response.status === 404) {
58+
// TODO: voted on all talks, go to predict
59+
console.log('Voted on all talks, time to predict')
60+
}
61+
});
62+
}
63+
64+
render() {
65+
return (
66+
<Card>
67+
<CardBody>
68+
<CardTitle tag="h3" className="text-center">{this.state.result['title']}</CardTitle>
69+
<CardSubtitle className="text-center"><small>Presented by: {this.state.result['presenters']}</small></CardSubtitle>
70+
<br />
71+
<CardText>{this.state.result['description']}</CardText>
72+
</CardBody>
73+
<CardFooter>
74+
<Button color="danger" className="float-left" onClick={(e) => this.vote(e, 'no')}>Watch Later</Button>
75+
<Button color="success" className="float-right" onClick={(e) => this.vote(e, 'yes')}>In Person</Button>
76+
</CardFooter>
77+
</Card>
78+
);
79+
}
80+
}
81+
82+
export default VoteCard;

0 commit comments

Comments
 (0)