Skip to content
This repository was archived by the owner on Feb 12, 2019. It is now read-only.
Open
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
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,4 @@ bower_components
.node_repl_history

# Compiled Files
dist/app.js
dist/style.css
dist
16 changes: 6 additions & 10 deletions dist/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,14 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" name="viewport" />
<title>Spotify</title>

<title>Spotify Exercise</title>
<!-- Description of site -->
<meta name="description" content="Spotify!.">

<meta name="description" content="Quickly start developing an react application.">
<!-- Stylesheets -->
<link rel="stylesheet" href="style.css" />
<link rel="stylesheet" href="bundle.css" />
</head>

<body ng-app="spotifyzier">
<section ui-view></section>

<script src="app.js"></script>
<body>
<script src="bundle.js"></script>
</body>
</html>
</html>
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"main": "index.js",
"scripts": {
"start": "node index.js",
"dev": "webpack-dev-server --open",
"doc": "gulp doc",
"build": "webpack",
"test": "echo \"Error: no test specified\" && exit 1"
Expand All @@ -25,6 +26,7 @@
"cors": "^2.8.1",
"express": "^4.15.2",
"helmet": "^3.5.0",
"material-design-icons": "^3.0.1",
"md5": "^2.2.1",
"mongoose": "^4.9.0",
"normalize.css": "^5.0.0",
Expand All @@ -34,7 +36,8 @@
"react-router": "^3.0.2",
"react-router-redux": "^4.0.8",
"redux": "^3.6.0",
"redux-saga": "^0.14.3"
"redux-saga": "^0.14.3",
"redux-thunk": "^2.2.0"
},
"devDependencies": {
"babel": "^6.23.0",
Expand Down
18 changes: 18 additions & 0 deletions src/API/searchApi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class SearchApi {
search(query) {
return new Promise((resolve, reject) => {
if(query.length > 3) {
fetch(`http://localhost:3000/search?q=${query}`)
.then(response => response.json())
.then((data) => {
resolve(data.albums.items || [])
}, reject)
}
else {
resolve([])
}
})
}
}

export default new SearchApi()
15 changes: 15 additions & 0 deletions src/Actions/Search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export const types = {
SEARCH_REQUESTED: 'SEARCH_REQUESTED',
SEARCH_RESOLVED: 'SEARCH_RESOLVED',
SEARCH_REJECTED: 'SEARCH_REJECTED'
}

export const searchRequested = (q) => ({
type: types.SEARCH_REQUESTED,
query: q
})

export const searchResolved = (data) => ({
type: types.SEARCH_RESOLVED,
data
})
23 changes: 23 additions & 0 deletions src/Components/ActionButton/ActionButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react'

import './ActionButton.scss'

class ActionButton extends React.Component {
static propTypes = {
customClass: React.PropTypes.string,
label: React.PropTypes.string.isRequired,
onButtonClick: React.PropTypes.func.isRequired
}

render() {
return(
<button
className={ `action-button ${this.props.customClass}` }
onClick={ this.props.onButtonClick }>
{ this.props.label }
</button>
)
}
}

export default ActionButton
10 changes: 10 additions & 0 deletions src/Components/ActionButton/ActionButton.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.action-button {
border-radius: 40px;
border: 1px solid #03bb4f;
background-color: rgba(30, 30, 30, 0.7);
color: #fff;
padding: 8px 20px;
font-size: 12px;
font-weight: 100;
cursor: pointer;
}
43 changes: 43 additions & 0 deletions src/Components/AlbumList/AlbumItem/AlbumItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react'

import ActionButton from '~/Components/ActionButton/ActionButton'
import './AlbumItem.scss'

class AlbumItem extends React.Component {
static propTypes = {
item: React.PropTypes.object.isRequired,
onCommentClick: React.PropTypes.func
}

constructor() {
super()
this.onItemClick = this.onItemClick.bind(this)
}

onItemClick() {
this.props.onCommentClick(this.props.item)
}

render() {
let url = `url(${this.props.item.images[0].url})`

return(
<div className="album-item" style={{ backgroundImage: url }}>
<span>
<div className="album-item-image">
<img src={ this.props.item.images[0].url } />
</div>
<div className="album-item-info">
<h2>{ this.props.item.artists[0].name }</h2>
<h1>{ this.props.item.name }</h1>
</div>
<div className="album-item-actions">
<ActionButton label="VIEW COMMENTS" onButtonClick={ this.onItemClick } />
</div>
</span>
</div>
)
}
}

export default AlbumItem
70 changes: 70 additions & 0 deletions src/Components/AlbumList/AlbumItem/AlbumItem.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
.album-item {
width: 100%;
height: 180px;
color: #fff;
border: 1px solid #181818;
margin-top: 14px;
background-size: cover;
background-position: center;

span {
background-image: linear-gradient(to left, rgba(40, 40, 40, 0.85), #282828);
width: 100%;
height: 100%;
display: block;
}

h1 {
width: 500px;
height: 60px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
font-size: 46px;
font-weight: normal;
margin: 0;
}

h2 {
font-size: 12px;
font-weight: normal;
color: #a0a097;
margin: 0;
}

.album-item-image {
float: left;
width: 180px;
height: 100%;
display: block;
img {
width: 180px;
}
}

.album-item-info {
float: left;
display: block;
margin-top: 23px;
padding-left: 30px;
}

.album-item-actions {
float: right;
height: 100%;
display: flex;
flex-direction: column-reverse;

.action-button {
margin: 20px 30px;
}
}

.album-item-backimg {
position: absolute;
top: 0;
left: 0;
z-index: -1;
}

}
42 changes: 42 additions & 0 deletions src/Components/AlbumList/AlbumList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react'
import { connect } from 'react-redux'
import AlbumItem from './AlbumItem/AlbumItem'

import './AlbumList.scss'

class AlbumList extends React.Component {
static propTypes = {
albums: React.PropTypes.array.isRequired
}

constructor() {
super()
this.gotoComment = this.gotoComment.bind(this)
}

gotoComment() {
//console.log(`Se clickeó el comment del item: ${ item.name }`)
}

render() {
const items = this.props.albums.map((albumItem) =>
<AlbumItem
key={ albumItem.id }
item={ albumItem }
onCommentClick={ this.gotoComment } />
)
return(
<div className="album-list">
{ items }
</div>
)
}
}

const mapStateToProps = (state) => ({
albums: state.Search.albums
})

const mapDispatchToProps = () => ({})

export default connect(mapStateToProps, mapDispatchToProps)(AlbumList)
10 changes: 10 additions & 0 deletions src/Components/AlbumList/AlbumList.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.album-list {
width: 66%;
margin: 10px auto;
display: flex;
flex-direction: column;
flex-wrap: nowrap;
justify-content: flex-start;
align-content: flex-start;
align-items: stretch;
}
17 changes: 17 additions & 0 deletions src/Components/Main/Main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react'
import Search from '../Search/Search'
import AlbumList from '../AlbumList/AlbumList'
import './main.scss'

export class Main extends React.Component {
render() {
return(
<div className="main">
<Search />
<AlbumList />
</div>
)
}
}

export default Main
4 changes: 4 additions & 0 deletions src/Components/Main/main.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.main {
width: 100%;
margin: 0 auto;
}
34 changes: 34 additions & 0 deletions src/Components/Search/Search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react'
import { connect } from 'react-redux'
import * as actions from '../../Actions/Search'
//import 'material-design-icons/iconfont/material-icons.css'
import './_search.scss'

class Search extends React.Component {
static propTypes = {
onSearch: React.PropTypes.func
}

render() {
return(
<div className="searchbox">
{ /*<i className="material-icons">search</i> */ }
<input
type="text"
className="search-input"
placeholder="Search"
onChange={ this.props.onSearch } />
</div>
)
}
}

const mapStateToProps = () => ({})

const mapDispatchToProps = (dispatch) => ({
onSearch(query) {
dispatch(actions.searchRequested(query.target.value))
}
})

export default connect(mapStateToProps, mapDispatchToProps)(Search)
16 changes: 16 additions & 0 deletions src/Components/Search/_search.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.searchbox {
font-size: 12px;
background-color: #282828;
padding: 12px;
.search-input {
color: #353535;
padding: 0 24px;
border: 1px solid #282828;
border-radius: 40px;
width: 68%;
height: 24px;
outline: none;
margin: 0 auto;
display: block;
}
}
16 changes: 16 additions & 0 deletions src/Reducers/Search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as actions from '../Actions/Search'

const searchReducer = (state = { albums: [] }, action) => {
switch(action.type) {
case actions.types.SEARCH_RESOLVED: return {
albums: action.data
}
case actions.types.SEARCH_REQUESTED: return {
...state,
query: action.query
}
default: return state
}
}

export default searchReducer
Loading