mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-31 02:26:50 +10:00
ListingPage: fetch & redux
This commit is contained in:
parent
8f99ca324e
commit
ab7666968d
1 changed files with 127 additions and 77 deletions
|
|
@ -1,9 +1,13 @@
|
|||
import React from 'react';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { types } from 'sharetribe-sdk';
|
||||
import { NamedLink, PageLayout } from '../../components';
|
||||
import { showListings, getListingsById } from '../../ducks/sdk.duck';
|
||||
import css from './ListingPage.css';
|
||||
|
||||
// TODO this hard coded info needs to be removed when API supports these features
|
||||
const info = {
|
||||
title: 'Banyan Studios',
|
||||
//title: 'Banyan Studios',
|
||||
price: '55\u20AC / day',
|
||||
images: [
|
||||
{ id: 1, title: 'img1', imageUrl: 'http://placehold.it/750x470' },
|
||||
|
|
@ -12,18 +16,18 @@ const info = {
|
|||
{ id: 4, title: 'img4', imageUrl: 'http://placehold.it/750x470' },
|
||||
{ id: 5, title: 'img5', imageUrl: 'http://placehold.it/750x470' },
|
||||
],
|
||||
description: `
|
||||
<p>
|
||||
Organic Music Production in a Sustainable, Ethical and Professional Studio.
|
||||
</p>
|
||||
<p>
|
||||
Social Permaculture focuses on nourishing our environment with abundance instead of depleting it.
|
||||
</p>
|
||||
<p>
|
||||
Banyan Studios is a comfortable, conscious, inspiring and creative retreat for musicians trying to convey their message through state of the art Audio & Video.
|
||||
</p>
|
||||
<a href="https://vimeo.com/168106603" alt="video">https://vimeo.com/168106603</a>
|
||||
`,
|
||||
// description: `
|
||||
// <p>
|
||||
// Organic Music Production in a Sustainable, Ethical and Professional Studio.
|
||||
// </p>
|
||||
// <p>
|
||||
// Social Permaculture focuses on nourishing our environment with abundance instead of depleting it.
|
||||
// </p>
|
||||
// <p>
|
||||
// Banyan Studios is a comfortable, conscious, inspiring and creative retreat for musicians trying to convey their message through state of the art Audio & Video.
|
||||
// </p>
|
||||
// <a href="https://vimeo.com/168106603" alt="video">https://vimeo.com/168106603</a>
|
||||
// `,
|
||||
reviews: [
|
||||
{
|
||||
id: 1,
|
||||
|
|
@ -39,67 +43,113 @@ const info = {
|
|||
};
|
||||
|
||||
// N.B. All the presentational content needs to be extracted to their own components
|
||||
const ListingPage = () => (
|
||||
<PageLayout title={`${info.title} ${info.price}`}>
|
||||
<div className={css.imageContainer}>
|
||||
<img className={css.mainImage} alt={info.images[0].title} src={info.images[0].imageUrl} />
|
||||
<div className={css.thumbnailContainer}>
|
||||
{info.images.slice(1).map(image => (
|
||||
<div key={image.id} className={css.squareWrapper}>
|
||||
<div className={css.aspectWrapper}>
|
||||
<img className={css.thumbnail} alt={image.title} src={image.imageUrl} />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{/* eslint-disable react/no-danger */}
|
||||
<div className={css.description} dangerouslySetInnerHTML={{ __html: info.description }} />
|
||||
{/* eslint-enable react/no-danger */}
|
||||
<div className={css.filterSection}>
|
||||
<h1>Here will be filters (or dragons)</h1>
|
||||
<h2>Studio type</h2>
|
||||
<h2>Amenities</h2>
|
||||
<h2>Additional Services Available</h2>
|
||||
<p><strong>Studio hours:</strong> 10am - 6pm</p>
|
||||
</div>
|
||||
<a className={css.contact} href="mailto:studio.dude@mystudio.com">
|
||||
<h2>Contact studio</h2>
|
||||
</a>
|
||||
<div className={css.reviewSection}>
|
||||
<h2>Studio reviews (1)</h2>
|
||||
<div className={css.reviews}>
|
||||
{info.reviews.map(review => (
|
||||
<div key={review.id} className={css.review}>
|
||||
<p>{review.review}</p>
|
||||
<div className={css.reviewDetails}>
|
||||
<div className={css.avatarWrapper}>
|
||||
<img
|
||||
className={css.avatar}
|
||||
src={review.reviewer.avatar}
|
||||
alt={review.reviewer.name}
|
||||
/>
|
||||
</div>
|
||||
<div className={css.reviewDetails}>
|
||||
<span className={css.authorName}>{review.reviewer.name}</span><span
|
||||
className={css.date}
|
||||
>
|
||||
{review.reviewer.date}
|
||||
</span>
|
||||
</div>
|
||||
<div className={css.rating}>
|
||||
review: <span>{review.rating}</span><span>/5</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className={css.map}>Map</div>
|
||||
<NamedLink className={css.buttonLink} name="OrderDetailsPage" params={{ id: 12345 }}>
|
||||
{`Book ${info.title}`}
|
||||
</NamedLink>
|
||||
</PageLayout>
|
||||
);
|
||||
export class ListingPageComponent extends Component {
|
||||
|
||||
export default ListingPage;
|
||||
componentDidMount() {
|
||||
ListingPageComponent.loadData(this.props.params.id, this.props.onLoadListing);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { entitiesData, params } = this.props;
|
||||
const id = new types.UUID(params.id);
|
||||
const currentListing = entitiesData.entities.listing ? getListingsById(entitiesData, [id])[0] : null;
|
||||
|
||||
const title = currentListing ? currentListing.attributes.title : '';
|
||||
const description = currentListing ? currentListing.attributes.description : '';
|
||||
|
||||
// TODO render "loading" or blank page, if currentListing is null.
|
||||
return (
|
||||
<PageLayout title={`${title} ${info.price}`}>
|
||||
<div className={css.imageContainer}>
|
||||
<img className={css.mainImage} alt={info.images[0].title} src={info.images[0].imageUrl} />
|
||||
<div className={css.thumbnailContainer}>
|
||||
{info.images.slice(1).map(image => (
|
||||
<div key={image.id} className={css.squareWrapper}>
|
||||
<div className={css.aspectWrapper}>
|
||||
<img className={css.thumbnail} alt={image.title} src={image.imageUrl} />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{/* eslint-disable react/no-danger */}
|
||||
<div className={css.description} dangerouslySetInnerHTML={{ __html: description }} />
|
||||
{/* eslint-enable react/no-danger */}
|
||||
<div className={css.filterSection}>
|
||||
<h1>Here will be filters (or dragons)</h1>
|
||||
<h2>Studio type</h2>
|
||||
<h2>Amenities</h2>
|
||||
<h2>Additional Services Available</h2>
|
||||
<p><strong>Studio hours:</strong> 10am - 6pm</p>
|
||||
</div>
|
||||
<a className={css.contact} href="mailto:studio.dude@mystudio.com">
|
||||
<h2>Contact studio</h2>
|
||||
</a>
|
||||
<div className={css.reviewSection}>
|
||||
<h2>Studio reviews (1)</h2>
|
||||
<div className={css.reviews}>
|
||||
{info.reviews.map(review => (
|
||||
<div key={review.id} className={css.review}>
|
||||
<p>{review.review}</p>
|
||||
<div className={css.reviewDetails}>
|
||||
<div className={css.avatarWrapper}>
|
||||
<img
|
||||
className={css.avatar}
|
||||
src={review.reviewer.avatar}
|
||||
alt={review.reviewer.name}
|
||||
/>
|
||||
</div>
|
||||
<div className={css.reviewDetails}>
|
||||
<span className={css.authorName}>{review.reviewer.name}</span><span
|
||||
className={css.date}
|
||||
>
|
||||
{review.reviewer.date}
|
||||
</span>
|
||||
</div>
|
||||
<div className={css.rating}>
|
||||
review: <span>{review.rating}</span><span>/5</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className={css.map}>Map</div>
|
||||
<NamedLink className={css.buttonLink} name="OrderDetailsPage" params={{ id: 12345 }}>
|
||||
{`Book ${title}`}
|
||||
</NamedLink>
|
||||
</PageLayout>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
ListingPageComponent.loadData = (id, onLoadListing) => {
|
||||
onLoadListing(id);
|
||||
};
|
||||
|
||||
ListingPageComponent.defaultProps = { listing: null };
|
||||
|
||||
const { func, object, shape, string } = PropTypes;
|
||||
|
||||
ListingPageComponent.propTypes = {
|
||||
entitiesData: object.isRequired,
|
||||
onLoadListing: func.isRequired,
|
||||
params: shape({
|
||||
id: string.isRequired,
|
||||
slug: string.isRequired,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const { data, ListingPage } = state;
|
||||
const entitiesData = data ? data : {};
|
||||
return { ListingPage, entitiesData };
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
onLoadListing: (id) => dispatch(showListings({ id, include: ['author'] })),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ListingPageComponent);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue