Skip to content
This repository has been archived by the owner on Apr 14, 2020. It is now read-only.

Commit

Permalink
Merge pull request #84 from casey-chow/casey/listings-ui
Browse files Browse the repository at this point in the history
Listings UI
  • Loading branch information
casey-chow authored Apr 19, 2017
2 parents 622e713 + 7391d18 commit eece7c8
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 61 deletions.
13 changes: 10 additions & 3 deletions client/src/components/ActionBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class ActionBar extends Component {
};

static pages = [
{ name: 'Listings', url: '/' },
{ name: 'Listings', url: '/listings' },
{ name: 'Seeks', url: '/seeks' },
{ name: 'Compose', url: '/compose' },
];
Expand All @@ -46,6 +46,8 @@ class ActionBar extends Component {
<LoginButton {...props} />
);

const currentMajorPath = this.props.location.pathname.split('/')[1];

return (
<Paper
style={{
Expand All @@ -63,9 +65,14 @@ class ActionBar extends Component {
<SearchBar style={{ flex: '2 2 0%' }} />
<RightElement style={{ flex: '1 1 0%' }} />
</AppBar>
<Tabs onChange={this.changeTab} value={this.props.location.pathname}>
<Tabs onChange={this.changeTab} value={currentMajorPath}>
{ActionBar.pages.map(page => (
<Tab label={page.name} value={page.url} containerElement={<Link to={page.url} />} />
<Tab
key={page.name}
label={page.name}
value={page.url.split('/')[1]}
containerElement={<Link to={page.url} />}
/>
))}
</Tabs>
</Paper>
Expand Down
84 changes: 48 additions & 36 deletions client/src/components/ListingCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,68 +3,80 @@ import PropTypes from 'prop-types';

import {
Card,
// CardActions,
CardActions,
CardHeader,
CardMedia,
// CardTitle,
CardTitle,
CardText,
} from 'material-ui/Card';
import FlatButton from 'material-ui/FlatButton';
import EmailIcon from 'material-ui/svg-icons/communication/email';
import FavoriteIcon from 'material-ui/svg-icons/action/favorite';

class ListingCard extends React.Component {
constructor(props) {
super(props);
this.state = {
expanded: false,
};
}

handleExpandChange = (expanded) => {
this.setState({ expanded });
static propTypes = {
expanded: PropTypes.bool.isRequired,
onExpandChange: PropTypes.func,
listing: PropTypes.shape({
keyId: PropTypes.number,
creationDate: PropTypes.string,
lastModificationDate: PropTypes.string,
title: PropTypes.string,
description: PropTypes.string,
userId: PropTypes.number,
price: PropTypes.number,
status: PropTypes.string,
expirationDate: PropTypes.number,
thumbnail: PropTypes.string,
}).isRequired,
};

handleToggle = (event, toggle) => {
this.setState({ expanded: toggle });
static defaultProps = {
expanded: false,
onExpandChange: () => {},
};

handleExpand = () => {
this.setState({ expanded: true });
};
state = {
expanded: false,
}

handleReduce = () => {
this.setState({ expanded: false });
};
handleExpandChange = (expanded) => {
this.props.onExpandChange(expanded, this.props.listing.keyId);
}

render() {
const { listing } = this.props;
const { listing, expanded } = this.props;

const cardStyles = expanded ? {
margin: '1.5em -3em',
} : {};

return (
<Card expanded={this.state.expanded} onExpandChange={this.handleExpandChange}>
<CardHeader title={listing.title} subtitle={`$${listing.price / 100}`}actAsExpander />
<Card expanded={expanded} style={cardStyles} onExpandChange={this.handleExpandChange}>
<CardHeader
title={listing.title}
subtitle={`$${listing.price / 100}`}
actAsExpander
/>

<CardMedia expandable>
<img alt={listing.title} src={listing.thumbnail} style={{ minWidth: undefined, maxHeight: '300px', width: 'auto' }} />
</CardMedia>

<CardTitle title={listing.title} expandable />

<CardText expandable>
{listing.description}
</CardText>

<CardActions expandable>
<FlatButton primary icon={<EmailIcon />} label="Contact Seller" />
<FlatButton secondary icon={<FavoriteIcon />} label="Save" />
</CardActions>
</Card>
);
}
}

ListingCard.propTypes = {
listing: PropTypes.shape({
keyId: PropTypes.number,
creationDate: PropTypes.string,
lastModificationDate: PropTypes.string,
title: PropTypes.string,
description: PropTypes.string,
userId: PropTypes.number,
price: PropTypes.number,
status: PropTypes.string,
expirationDate: PropTypes.number,
thumbnail: PropTypes.string,
}).isRequired,
};

export default ListingCard;
37 changes: 24 additions & 13 deletions client/src/components/Listings.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,39 @@ class Listings extends PureComponent {
static propTypes = {
listings: PropTypes.arrayOf(PropTypes.shape({
keyId: PropTypes.number,
creationDate: PropTypes.string,
lastModificationDate: PropTypes.string,
title: PropTypes.string,
description: PropTypes.string,
userId: PropTypes.number,
price: PropTypes.number,
status: PropTypes.string,
expirationDate: PropTypes.number,
thumbnail: PropTypes.string,
})).isRequired,
};

render() {
const listings = this.props.listings.map(listing => <ListingCard listing={listing} />);
state = {
openCardId: -1,
};

isExpanded = keyId => this.state.openCardId === keyId;

handleExpandChange = (expanded, keyId) => {
console.log('handleExpandChange', this);
if (!expanded) {
this.setState({ openCardId: -1 });
} else {
this.setState({ openCardId: keyId });
}
}

render() {
return (
<div>
<Container className="Listings">
<Row>
<Col xs={12}>
<Col xs={1} />
<Col xs={10}>
<div className="cardsContainer">
{listings}
{this.props.listings.map(listing =>
<ListingCard
key={listing.keyId}
expanded={this.isExpanded(listing.keyId)}
listing={listing}
onExpandChange={this.handleExpandChange}
/>)}
</div>
</Col>
</Row>
Expand Down
19 changes: 14 additions & 5 deletions client/src/components/SearchBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { withRouter } from 'react-router-dom';
import AutoComplete from 'material-ui/AutoComplete';
import Paper from 'material-ui/Paper';

import { searchListings, loadRecentListings } from './../actions/listings';
import { searchListings } from './../actions/listings';


import './SearchBar.css';
Expand All @@ -17,6 +17,9 @@ class SearchBar extends Component {
dispatch: PropTypes.func.isRequired,
style: PropTypes.object,
query: PropTypes.string,
location: PropTypes.shape({
pathname: PropTypes.string.isRequired,
}).isRequired,
history: PropTypes.shape({
push: PropTypes.func.isRequired,
}).isRequired,
Expand All @@ -31,6 +34,9 @@ class SearchBar extends Component {
dataSource: [],
open: false,
focus: false,
// submitIntent is true when the user last expressed some
// intent of submission, in this case a "request"
submitIntent: true,
}

handleUpdateInput = (value) => {
Expand All @@ -44,11 +50,13 @@ class SearchBar extends Component {
'clothes',
],
});
if (!value) {
this.props.dispatch(loadRecentListings());
} else {
this.props.dispatch(searchListings(value));

// if we are not in search URL
if (!/\/listings\/search\/.*/.test(this.props.location.pathname)) {
this.props.history.push('/listings/search/');
}

this.props.dispatch(searchListings(value));
}

handleTouchTap = (event) => {
Expand Down Expand Up @@ -103,6 +111,7 @@ class SearchBar extends Component {
hintText={<span className="hint-text">What do you want to buy?</span>}
dataSource={this.state.dataSource}
onUpdateInput={this.handleUpdateInput}
openOnFocus
onClose={this.handleRequestClose}
onFocus={this.handleOnFocus}
onBlur={this.handleOnBlur}
Expand Down
30 changes: 29 additions & 1 deletion client/src/containers/RecentListings.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,40 @@ class RecentListings extends Component {
dispatch: PropTypes.func.isRequired,
};

state = {
initialLoad: true,
}

componentWillMount() {
this.props.dispatch(loadRecentListings());
}

componentWillReceiveProps(nextProps) {
if (!nextProps.listingsLoading) {
this.setState({ initialLoad: false });
}
}

shouldComponentUpdate(nextProps, nextState) {
if (this.props.listingsLoading !== nextProps.listingsLoading) {
return true;
}

if (this.props.listings.length !== nextProps.listings.length) {
return true;
}

for (let i = 0; i < this.props.listings.length; i += 1) {
if (this.props.listings[i].keyId !== nextProps.listings[i].keyId) {
return true;
}
}

return false;
}

render() {
if (this.props.listingsLoading) {
if (this.props.listingsLoading && this.state.initialLoad) {
return (
<div style={{ alignItems: 'center', justifyContent: 'center', display: 'flex' }}>
<CircularProgress size={80} thickness={8} />
Expand Down
30 changes: 29 additions & 1 deletion client/src/containers/SearchListings.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,42 @@ class SearchListings extends Component {
}).isRequired,
};

state = {
initialLoad: true,
}

componentWillMount() {
const query = this.props.match.params.query;
this.props.dispatch(searchListings(query));
this.props.dispatch(setCurrentListingsQuery(query));
}

componentWillReceiveProps(nextProps) {
if (!nextProps.listingsLoading) {
this.setState({ initialLoad: false });
}
}

shouldComponentUpdate(nextProps, nextState) {
if (this.props.listingsLoading !== nextProps.listingsLoading) {
return true;
}

if (this.props.listings.length !== nextProps.listings.length) {
return true;
}

for (let i = 0; i < this.props.listings.length; i += 1) {
if (this.props.listings[i].keyId !== nextProps.listings[i].keyId) {
return true;
}
}

return false;
}

render() {
if (this.props.listingsLoading) {
if (this.props.listingsLoading && this.state.initialLoad) {
return (
<div style={{ alignItems: 'center', justifyContent: 'center', display: 'flex' }}>
<CircularProgress size={80} thickness={8} />
Expand Down
4 changes: 2 additions & 2 deletions client/src/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ const listingsLoading = (state = false, action) => {
const recentListings = (state = [], action) => {
switch (action.type) {
case 'RECENT_LISTINGS_FAILURE': // TODO: failure state
case 'RECENT_LISTINGS_REQUEST':
return [];
case 'RECENT_LISTINGS_SUCCESS':
return action.json;
case 'RECENT_LISTINGS_REQUEST':
default:
return state;
}
Expand All @@ -33,10 +33,10 @@ const recentListings = (state = [], action) => {
const searchListings = (state = [], action) => {
switch (action.type) {
case 'SEARCH_LISTINGS_FAILURE': // TODO: failure state
case 'SEARCH_LISTINGS_REQUEST':
return [];
case 'SEARCH_LISTINGS_SUCCESS':
return action.json;
case 'SEARCH_LISTINGS_REQUEST':
default:
return state;
}
Expand Down
Loading

0 comments on commit eece7c8

Please sign in to comment.