Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
45 changes: 45 additions & 0 deletions client/src/components/Authentication/SignIn.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react';
import classnames from 'classnames';

class SignIn extends React.Component {
state = {
identifier: '',
password: '',
errors: {},
loading: false
}

handleChange(e) {
this.setState({ [e.target.name]: e.target.value });
}

handleSubmit(e) {
e.preventDefault();
}

render() {
return (
<div>
<h1>Sign In</h1>
<form className={classnames('ui', 'form', { loading: this.state.loading })} onSubmit={this.handleSubmit}>

<h4 class="ui dividing header">Fill with your credentials to log in</h4>

<div className="field">
<label for="identifier">Identifier</label>
<input className="ui input" onChange={this.handleChange} name="identifier" id="identifier" type="text" placeholder="Username or e-mail" />
</div>
<div className="field">
<label for="password">Password</label>
<input className="ui input" onChange={this.handleChange} name="password" id="password" type="password" placeholder="Password" />
</div>
<div className="field">
<button type="submit" className="ui primary button">Send</button>
</div>
</form>
</div>
);
}
}

export default SignIn;
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { connect } from 'react-redux';
import { Redirect, NavLink } from 'react-router-dom';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { addAlbum, updateAlbum } from '../../actions/albums';
import { addAlbum, updateAlbum } from '../../../actions/albums';

class AlbumFormPage extends React.Component {
class AlbumForm extends React.Component {
state = {
id: this.props.album ? this.props.album.id : null,
title: this.props.album ? this.props.album.title : '',
Expand Down Expand Up @@ -137,7 +137,7 @@ class AlbumFormPage extends React.Component {
}
}

AlbumFormPage.propTypes = {
AlbumForm.propTypes = {
updateAlbum: PropTypes.func.isRequired,
addAlbum: PropTypes.func.isRequired
};
Expand All @@ -152,4 +152,4 @@ function mapStateToProps(state, props) {
return { album: null };
}

export default connect(mapStateToProps, { addAlbum, updateAlbum })(AlbumFormPage);
export default connect(mapStateToProps, { addAlbum, updateAlbum })(AlbumForm);
50 changes: 50 additions & 0 deletions client/src/components/Band/Album/AlbumList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Redirect, NavLink } from 'react-router-dom';

import { fetchBandAlbums } from '../../../actions/bands';
import AlbumCard from './AlbumCard';

class AlbumsList extends React.Component {

render() {
const albumCards = (
<div className="ui five cards">
{
!!this.props.band.albums && this.props.band.albums.map((album) => {
return (<AlbumCard key={this.props.band.id} band={this.props.band} album={album} />);
})
}
</div>
);

const emptyMessage = (
<div className="ui info message">There is no albums registered yet.</div>
);

return (
<div className="ui container">
{
!this.props.band ? //|| !this.props.band.albums ?
<Redirect to="/bands" /> :
<div>
<h1>{this.props.band.title}</h1>
<div>
<NavLink exact to="/bands/" className="ui button">Back to bands list</NavLink>
<NavLink exact to={`/band/${this.props.band.id}/album/new`} className="ui button primary">Add New</NavLink>
<br /><br />
{ !!this.props.band.albums && this.props.band.albums.length > 0 ? albumCards : emptyMessage }
</div>
</div>
}
</div>
);
}
}

function mapStateToProps(state, props) {
return { band: state.bands.filter(item => item.id == Number(props.match.params.id))[0] };
}

export default connect(mapStateToProps, { fetchBandAlbums })(AlbumsList);
160 changes: 160 additions & 0 deletions client/src/components/Band/BandForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { connect } from 'react-redux';
import { Redirect, NavLink } from 'react-router-dom';
import { fetchBand, updateBand, addBand } from '../../actions/bands';

/**
* @description Band form used to add and edit. We bind state and events binding to the component by constructor this time.
* @extends React
*/
class BandForm extends React.Component {
constructor(props) {
super(props);

this.state = {
id: '',
title: '',
year: '',
description: '',
errors: {},
loading: false,
redirect: false
}

this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}

componentDidMount() {
if (this.props.match && this.props.match.params && typeof this.props.match.params.id !== "undefined") {
this.props.fetchBand(this.props.match.params.id);
}
// this.setState({
// id: (this.props.band) ? this.props.band.id : '',
// title: (this.props.band) ? this.props.band.title : '',
// year: (this.props.band) ? this.props.band.year : '',
// description: (this.props.description) ? this.props.band.description : ''
// });
}

componentWillReceiveProps = (nextProps) => {
this.setState({
//id: nextProps.band.id,
title: nextProps.band.title,
year: nextProps.band.year,
description: nextProps.band.description
});
}

handleChange(e) {
this.setState({ [e.target.name]: e.target.value });
}

handleSubmit(e) {
e.preventDefault();
let errors = {};

// Validation
if (this.state.title === '') errors.title = "This field can't be empty";
if (this.state.year === '') errors.year = "This field can't be empty";

// Fill the errors object state
this.setState({ errors });

// Proceed if everything is OK
if (Object.keys(errors).length === 0) {
const { id, title, year, description } = this.state;
this.setState({ loading: true });
//this.props.saveBand({ id, title, year, description });

if (!id)
this.props.addBand({ title, year, description }).then(() => this.setState({ redirect: true }));
else
this.props.updateBand({ id, title, year, description }).then(() => this.setState({ redirect: true }));
}
}

render() {
return (
<div>
{
// Redirect if some action has worked succesfully, render if not
this.state.redirect ?
<Redirect to="/bands" /> :
<div className="ui container">
<h1>Band Registration</h1>
<NavLink exact to="/bands/" className="ui button">Back to bands list</NavLink>
<br /><br />
<form className={classnames("ui", "form", { loading: this.state.loading })} onSubmit={this.handleSubmit}>

<h4 className="ui dividing header">Fill the form below with the band information</h4>

{!!this.state.errors.global && <div className="ui negative message"><p>{this.state.errors.global}</p></div>}

<div className={classnames("field", { error: !!this.state.errors.title })}>
<label htmlFor="title">Title</label>
<input
type="text" id="title" name="title"
value={this.state.title}
className="ui input"
placeholder="The name of the band"
onChange={this.handleChange}
/>
<span>{this.state.errors.title}</span>
</div>

<div className={classnames("field", { error: !!this.state.errors.year })}>
<label htmlFor="year">Year</label>
<input
type="text" id="year" name="year"
value={this.state.year}
className="ui input"
placeholder="Foundation year"
onChange={this.handleChange}
/>
<span>{this.state.errors.year}</span>
</div>

<div className={classnames("field", { error: !!this.state.errors.description })}>
<label htmlFor="description">Description</label>
<textarea
id="description" name="description"
className="ui input"
placeholder="The band summary"
onChange={this.handleChange}
value={this.state.description}
style={{height: '115px'}}
></textarea>
<span>{this.state.errors.description}</span>
</div>
<div className="field">
<button type="submit" className="ui primary button">Save</button>
</div>
</form>
</div>
}
</div>
);
}
}

BandForm.propTypes = {
band: PropTypes.object,
fetchBand: PropTypes.func.isRequired,
updateBand: PropTypes.func.isRequired,
addBand: PropTypes.func.isRequired
};

function mapStateToProps(state, props) {
if (props.match && props.match.params && props.match.params.id > 0) {
return {
band: state.bands.find(item => item.id == props.match.params.id)
};
}

return { band: null };
}

export default connect(mapStateToProps, { fetchBand, updateBand, addBand })(BandForm);
99 changes: 99 additions & 0 deletions client/src/components/Band/BandList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { connect } from 'react-redux';
import { NavLink } from 'react-router-dom';

// Actions
import { fetchBands } from '../../actions/bands';
// Components
//import BandsList from './BandList';

class BandsListPage extends React.Component {

/**
* Fetch all bands on component load
*/
componentDidMount() {
this.props.fetchBands();
}

render() {
let gridLines = "";

const bands = this.props.bands;

if (bands.length !== 0) {
gridLines = bands.map((band, i) => {
if (band) {
return (
<tr key={band.id}>
<td>{band.id}</td>
<td>{band.title}</td>
<td style={{ textAlign: "center" }}>{band.year}</td>
<td style={{ textAlign: "center" }}>
<div className={classnames('ui','label','circular','small', {
'green' : band.albums.length > 0,
'yellow' : band.albums.length == 0
})}>{band.albums.length}</div>
</td>
<td style={{ textAlign: "right" }}>
<NavLink className="ui button compact icon small black" to={`/band/${band.id}/albums/`} title="More details">
<i className="icon eye"></i>
</NavLink>
<NavLink className="ui button compact icon small blue" to={`/band/${band.id}`} title="Edit">
<i className="icon edit"></i>
</NavLink>
<NavLink className="ui button compact icon small red disabled" to={`/band/delete/${band.id}`} title="Delete">
<i className="icon trash"></i>
</NavLink>
</td>
</tr>
);
}
});
}

const grid = (
<table className="ui striped selectable compact table single line">
<thead>
<tr>
<th width="7%">ID</th>
<th>Title</th>
<th width="7%" style={{ textAlign: "center" }}>Year</th>
<th width="5%" style={{ textAlign: "center" }}>Albums</th>
<th width="15%"></th>
</tr>
</thead>
<tbody>
{gridLines}
</tbody>
</table>
);

const emptyMessage = (
<div className="ui info message">There is no bands yet in your collection</div>
);

return (
<div className="ui container">
<h1>Bands List</h1>
<NavLink exact to="/bands/new" className="ui button primary">Add New</NavLink>
{bands.length > 0 ? grid : emptyMessage }
</div>
);
}
}

BandsListPage.propTypes = {
bands: PropTypes.array.isRequired,
fetchBands: PropTypes.func.isRequired
}

function mapStateToProps(state) {
return {
bands: state.bands
}
}

export default connect(mapStateToProps, { fetchBands })(BandsListPage);
Loading