Skip to content

Commit 10f2655

Browse files
authored
Merge pull request #22 from rpichioli/improvements
Client-side partially refactored, improved and reorganized
2 parents bee1219 + bd4f41a commit 10f2655

File tree

20 files changed

+403
-587
lines changed

20 files changed

+403
-587
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import React from 'react';
2+
import classnames from 'classnames';
3+
4+
class SignIn extends React.Component {
5+
state = {
6+
identifier: '',
7+
password: '',
8+
errors: {},
9+
loading: false
10+
}
11+
12+
handleChange(e) {
13+
this.setState({ [e.target.name]: e.target.value });
14+
}
15+
16+
handleSubmit(e) {
17+
e.preventDefault();
18+
}
19+
20+
render() {
21+
return (
22+
<div>
23+
<h1>Sign In</h1>
24+
<form className={classnames('ui', 'form', { loading: this.state.loading })} onSubmit={this.handleSubmit}>
25+
26+
<h4 class="ui dividing header">Fill with your credentials to log in</h4>
27+
28+
<div className="field">
29+
<label for="identifier">Identifier</label>
30+
<input className="ui input" onChange={this.handleChange} name="identifier" id="identifier" type="text" placeholder="Username or e-mail" />
31+
</div>
32+
<div className="field">
33+
<label for="password">Password</label>
34+
<input className="ui input" onChange={this.handleChange} name="password" id="password" type="password" placeholder="Password" />
35+
</div>
36+
<div className="field">
37+
<button type="submit" className="ui primary button">Send</button>
38+
</div>
39+
</form>
40+
</div>
41+
);
42+
}
43+
}
44+
45+
export default SignIn;
File renamed without changes.

client/src/components/bands/AlbumFormPage.js renamed to client/src/components/Band/Album/AlbumForm.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import { connect } from 'react-redux';
33
import { Redirect, NavLink } from 'react-router-dom';
44
import PropTypes from 'prop-types';
55
import classnames from 'classnames';
6-
import { addAlbum, updateAlbum } from '../../actions/albums';
6+
import { addAlbum, updateAlbum } from '../../../actions/albums';
77

8-
class AlbumFormPage extends React.Component {
8+
class AlbumForm extends React.Component {
99
state = {
1010
id: this.props.album ? this.props.album.id : null,
1111
title: this.props.album ? this.props.album.title : '',
@@ -137,7 +137,7 @@ class AlbumFormPage extends React.Component {
137137
}
138138
}
139139

140-
AlbumFormPage.propTypes = {
140+
AlbumForm.propTypes = {
141141
updateAlbum: PropTypes.func.isRequired,
142142
addAlbum: PropTypes.func.isRequired
143143
};
@@ -152,4 +152,4 @@ function mapStateToProps(state, props) {
152152
return { album: null };
153153
}
154154

155-
export default connect(mapStateToProps, { addAlbum, updateAlbum })(AlbumFormPage);
155+
export default connect(mapStateToProps, { addAlbum, updateAlbum })(AlbumForm);
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import { connect } from 'react-redux';
4+
import { Redirect, NavLink } from 'react-router-dom';
5+
6+
import { fetchBandAlbums } from '../../../actions/bands';
7+
import AlbumCard from './AlbumCard';
8+
9+
class AlbumsList extends React.Component {
10+
11+
render() {
12+
const albumCards = (
13+
<div className="ui five cards">
14+
{
15+
!!this.props.band.albums && this.props.band.albums.map((album) => {
16+
return (<AlbumCard key={this.props.band.id} band={this.props.band} album={album} />);
17+
})
18+
}
19+
</div>
20+
);
21+
22+
const emptyMessage = (
23+
<div className="ui info message">There is no albums registered yet.</div>
24+
);
25+
26+
return (
27+
<div className="ui container">
28+
{
29+
!this.props.band ? //|| !this.props.band.albums ?
30+
<Redirect to="/bands" /> :
31+
<div>
32+
<h1>{this.props.band.title}</h1>
33+
<div>
34+
<NavLink exact to="/bands/" className="ui button">Back to bands list</NavLink>
35+
<NavLink exact to={`/band/${this.props.band.id}/album/new`} className="ui button primary">Add New</NavLink>
36+
<br /><br />
37+
{ !!this.props.band.albums && this.props.band.albums.length > 0 ? albumCards : emptyMessage }
38+
</div>
39+
</div>
40+
}
41+
</div>
42+
);
43+
}
44+
}
45+
46+
function mapStateToProps(state, props) {
47+
return { band: state.bands.filter(item => item.id == Number(props.match.params.id))[0] };
48+
}
49+
50+
export default connect(mapStateToProps, { fetchBandAlbums })(AlbumsList);
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import classnames from 'classnames';
4+
import { connect } from 'react-redux';
5+
import { Redirect, NavLink } from 'react-router-dom';
6+
import { fetchBand, updateBand, addBand } from '../../actions/bands';
7+
8+
/**
9+
* @description Band form used to add and edit. We bind state and events binding to the component by constructor this time.
10+
* @extends React
11+
*/
12+
class BandForm extends React.Component {
13+
constructor(props) {
14+
super(props);
15+
16+
this.state = {
17+
id: '',
18+
title: '',
19+
year: '',
20+
description: '',
21+
errors: {},
22+
loading: false,
23+
redirect: false
24+
}
25+
26+
this.handleChange = this.handleChange.bind(this);
27+
this.handleSubmit = this.handleSubmit.bind(this);
28+
}
29+
30+
componentDidMount() {
31+
if (this.props.match && this.props.match.params && typeof this.props.match.params.id !== "undefined") {
32+
this.props.fetchBand(this.props.match.params.id);
33+
}
34+
// this.setState({
35+
// id: (this.props.band) ? this.props.band.id : '',
36+
// title: (this.props.band) ? this.props.band.title : '',
37+
// year: (this.props.band) ? this.props.band.year : '',
38+
// description: (this.props.description) ? this.props.band.description : ''
39+
// });
40+
}
41+
42+
componentWillReceiveProps = (nextProps) => {
43+
this.setState({
44+
//id: nextProps.band.id,
45+
title: nextProps.band.title,
46+
year: nextProps.band.year,
47+
description: nextProps.band.description
48+
});
49+
}
50+
51+
handleChange(e) {
52+
this.setState({ [e.target.name]: e.target.value });
53+
}
54+
55+
handleSubmit(e) {
56+
e.preventDefault();
57+
let errors = {};
58+
59+
// Validation
60+
if (this.state.title === '') errors.title = "This field can't be empty";
61+
if (this.state.year === '') errors.year = "This field can't be empty";
62+
63+
// Fill the errors object state
64+
this.setState({ errors });
65+
66+
// Proceed if everything is OK
67+
if (Object.keys(errors).length === 0) {
68+
const { id, title, year, description } = this.state;
69+
this.setState({ loading: true });
70+
//this.props.saveBand({ id, title, year, description });
71+
72+
if (!id)
73+
this.props.addBand({ title, year, description }).then(() => this.setState({ redirect: true }));
74+
else
75+
this.props.updateBand({ id, title, year, description }).then(() => this.setState({ redirect: true }));
76+
}
77+
}
78+
79+
render() {
80+
return (
81+
<div>
82+
{
83+
// Redirect if some action has worked succesfully, render if not
84+
this.state.redirect ?
85+
<Redirect to="/bands" /> :
86+
<div className="ui container">
87+
<h1>Band Registration</h1>
88+
<NavLink exact to="/bands/" className="ui button">Back to bands list</NavLink>
89+
<br /><br />
90+
<form className={classnames("ui", "form", { loading: this.state.loading })} onSubmit={this.handleSubmit}>
91+
92+
<h4 className="ui dividing header">Fill the form below with the band information</h4>
93+
94+
{!!this.state.errors.global && <div className="ui negative message"><p>{this.state.errors.global}</p></div>}
95+
96+
<div className={classnames("field", { error: !!this.state.errors.title })}>
97+
<label htmlFor="title">Title</label>
98+
<input
99+
type="text" id="title" name="title"
100+
value={this.state.title}
101+
className="ui input"
102+
placeholder="The name of the band"
103+
onChange={this.handleChange}
104+
/>
105+
<span>{this.state.errors.title}</span>
106+
</div>
107+
108+
<div className={classnames("field", { error: !!this.state.errors.year })}>
109+
<label htmlFor="year">Year</label>
110+
<input
111+
type="text" id="year" name="year"
112+
value={this.state.year}
113+
className="ui input"
114+
placeholder="Foundation year"
115+
onChange={this.handleChange}
116+
/>
117+
<span>{this.state.errors.year}</span>
118+
</div>
119+
120+
<div className={classnames("field", { error: !!this.state.errors.description })}>
121+
<label htmlFor="description">Description</label>
122+
<textarea
123+
id="description" name="description"
124+
className="ui input"
125+
placeholder="The band summary"
126+
onChange={this.handleChange}
127+
value={this.state.description}
128+
style={{height: '115px'}}
129+
></textarea>
130+
<span>{this.state.errors.description}</span>
131+
</div>
132+
<div className="field">
133+
<button type="submit" className="ui primary button">Save</button>
134+
</div>
135+
</form>
136+
</div>
137+
}
138+
</div>
139+
);
140+
}
141+
}
142+
143+
BandForm.propTypes = {
144+
band: PropTypes.object,
145+
fetchBand: PropTypes.func.isRequired,
146+
updateBand: PropTypes.func.isRequired,
147+
addBand: PropTypes.func.isRequired
148+
};
149+
150+
function mapStateToProps(state, props) {
151+
if (props.match && props.match.params && props.match.params.id > 0) {
152+
return {
153+
band: state.bands.find(item => item.id == props.match.params.id)
154+
};
155+
}
156+
157+
return { band: null };
158+
}
159+
160+
export default connect(mapStateToProps, { fetchBand, updateBand, addBand })(BandForm);
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import classnames from 'classnames';
4+
import { connect } from 'react-redux';
5+
import { NavLink } from 'react-router-dom';
6+
7+
// Actions
8+
import { fetchBands } from '../../actions/bands';
9+
// Components
10+
//import BandsList from './BandList';
11+
12+
class BandsListPage extends React.Component {
13+
14+
/**
15+
* Fetch all bands on component load
16+
*/
17+
componentDidMount() {
18+
this.props.fetchBands();
19+
}
20+
21+
render() {
22+
let gridLines = "";
23+
24+
const bands = this.props.bands;
25+
26+
if (bands.length !== 0) {
27+
gridLines = bands.map((band, i) => {
28+
if (band) {
29+
return (
30+
<tr key={band.id}>
31+
<td>{band.id}</td>
32+
<td>{band.title}</td>
33+
<td style={{ textAlign: "center" }}>{band.year}</td>
34+
<td style={{ textAlign: "center" }}>
35+
<div className={classnames('ui','label','circular','small', {
36+
'green' : band.albums.length > 0,
37+
'yellow' : band.albums.length == 0
38+
})}>{band.albums.length}</div>
39+
</td>
40+
<td style={{ textAlign: "right" }}>
41+
<NavLink className="ui button compact icon small black" to={`/band/${band.id}/albums/`} title="More details">
42+
<i className="icon eye"></i>
43+
</NavLink>
44+
<NavLink className="ui button compact icon small blue" to={`/band/${band.id}`} title="Edit">
45+
<i className="icon edit"></i>
46+
</NavLink>
47+
<NavLink className="ui button compact icon small red disabled" to={`/band/delete/${band.id}`} title="Delete">
48+
<i className="icon trash"></i>
49+
</NavLink>
50+
</td>
51+
</tr>
52+
);
53+
}
54+
});
55+
}
56+
57+
const grid = (
58+
<table className="ui striped selectable compact table single line">
59+
<thead>
60+
<tr>
61+
<th width="7%">ID</th>
62+
<th>Title</th>
63+
<th width="7%" style={{ textAlign: "center" }}>Year</th>
64+
<th width="5%" style={{ textAlign: "center" }}>Albums</th>
65+
<th width="15%"></th>
66+
</tr>
67+
</thead>
68+
<tbody>
69+
{gridLines}
70+
</tbody>
71+
</table>
72+
);
73+
74+
const emptyMessage = (
75+
<div className="ui info message">There is no bands yet in your collection</div>
76+
);
77+
78+
return (
79+
<div className="ui container">
80+
<h1>Bands List</h1>
81+
<NavLink exact to="/bands/new" className="ui button primary">Add New</NavLink>
82+
{bands.length > 0 ? grid : emptyMessage }
83+
</div>
84+
);
85+
}
86+
}
87+
88+
BandsListPage.propTypes = {
89+
bands: PropTypes.array.isRequired,
90+
fetchBands: PropTypes.func.isRequired
91+
}
92+
93+
function mapStateToProps(state) {
94+
return {
95+
bands: state.bands
96+
}
97+
}
98+
99+
export default connect(mapStateToProps, { fetchBands })(BandsListPage);

0 commit comments

Comments
 (0)