WIP Display collection

This commit is contained in:
Andrew Nicolaou 2019-09-08 18:54:49 +02:00
parent 1c97152533
commit 2df3670dc4
9 changed files with 117 additions and 70 deletions

View file

@ -187,7 +187,7 @@ class CollectionListRowBase extends React.Component {
key={collection.id} key={collection.id}
> >
<th scope="row"> <th scope="row">
<Link to={`/${username}/collections/${collection.id}`}> <Link to={{ pathname: `/${username}/collections/${collection.id}`, state: { skipSavingPath: true } }}>
{renameOpen ? '' : collection.name} {renameOpen ? '' : collection.name}
</Link> </Link>
{renameOpen {renameOpen

View file

@ -7,15 +7,14 @@ import { connect } from 'react-redux';
import { Link } from 'react-router'; import { Link } from 'react-router';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import classNames from 'classnames'; import classNames from 'classnames';
import * as ProjectActions from '../actions/project'; import * as ProjectActions from '../../IDE/actions/project';
import * as ProjectsActions from '../actions/projects'; import * as ProjectsActions from '../../IDE/actions/projects';
import * as CollectionsActions from '../actions/collections'; import * as CollectionsActions from '../../IDE/actions/collections';
import * as ToastActions from '../actions/toast'; import * as ToastActions from '../../IDE/actions/toast';
import * as SortingActions from '../actions/sorting'; import * as SortingActions from '../../IDE/actions/sorting';
import * as IdeActions from '../actions/ide'; import * as IdeActions from '../../IDE/actions/ide';
import { getCollection } from '../selectors/collections'; import { getCollection } from '../../IDE/selectors/collections';
import Loader from '../../App/components/loader'; import Loader from '../../App/components/loader';
import Overlay from '../../App/components/Overlay';
const arrowUp = require('../../../images/sort-arrow-up.svg'); const arrowUp = require('../../../images/sort-arrow-up.svg');
const arrowDown = require('../../../images/sort-arrow-down.svg'); const arrowDown = require('../../../images/sort-arrow-down.svg');
@ -259,9 +258,13 @@ class Collection extends React.Component {
} }
_renderCollectionMetadata() { _renderCollectionMetadata() {
const { name, description, items, owner } = this.props.collection;
return ( return (
<div className="collections-metadata"> <div className="collection-metadata">
<p>{this.props.collection.description}</p> <h2 className="collection-metadata__name">{name}</h2>
<p className="collection-metadata__user">{items.length} sketches collected by {owner.username}</p>
<p className="collection-metadata__description">{description}</p>
</div> </div>
); );
} }
@ -299,15 +302,11 @@ class Collection extends React.Component {
const title = this.hasCollection() ? this.getCollectionName() : null; const title = this.hasCollection() ? this.getCollectionName() : null;
return ( return (
<Overlay <section className="collection-container">
ariaLabel="collection" <Helmet>
title={title} <title>{this.getTitle()}</title>
previousPath={this.props.previousPath} </Helmet>
> <div className="">
<div className="sketches-table-container">
<Helmet>
<title>{this.getTitle()}</title>
</Helmet>
{this._renderLoader()} {this._renderLoader()}
{this.hasCollection() && this._renderCollectionMetadata()} {this.hasCollection() && this._renderCollectionMetadata()}
{this._renderEmptyTable()} {this._renderEmptyTable()}
@ -332,7 +331,7 @@ class Collection extends React.Component {
</tbody> </tbody>
</table>} </table>}
</div> </div>
</Overlay> </section>
); );
} }
} }
@ -343,7 +342,10 @@ Collection.propTypes = {
authenticated: PropTypes.bool.isRequired authenticated: PropTypes.bool.isRequired
}).isRequired, }).isRequired,
getCollections: PropTypes.func.isRequired, getCollections: PropTypes.func.isRequired,
collection: PropTypes.shape({}).isRequired, // TODO collection: PropTypes.shape({
name: PropTypes.string.isRequired,
description: PropTypes.string,
}).isRequired,
username: PropTypes.string, username: PropTypes.string,
loading: PropTypes.bool.isRequired, loading: PropTypes.bool.isRequired,
toggleDirectionForField: PropTypes.func.isRequired, toggleDirectionForField: PropTypes.func.isRequired,

View file

@ -41,7 +41,10 @@ class CollectionCreate extends React.Component {
this.props.createCollection(this.state.collection) this.props.createCollection(this.state.collection)
.then(({ id, owner }) => { .then(({ id, owner }) => {
browserHistory.replace(`/${owner.username}/collections/${id}`); const pathname = `/${owner.username}/collections/${id}`;
const location = { pathname, state: { skipSavingPath: true } };
browserHistory.replace(location);
}) })
.catch((error) => { .catch((error) => {
console.error('Error creating collection', error); console.error('Error creating collection', error);
@ -58,42 +61,49 @@ class CollectionCreate extends React.Component {
const invalid = name === '' || name == null; const invalid = name === '' || name == null;
return ( return (
<div className="sketches-table-container"> <section className="dashboard-header">
<Helmet> <Helmet>
<title>{this.getTitle()}</title> <title>{this.getTitle()}</title>
</Helmet> </Helmet>
<form className="form" onSubmit={this.handleCreateCollection}> <div className="dashboard-header__header">
{creationError && <span className="form-error">Couldn&apos;t create collection</span>} <h2 className="dashboard-header__header__title">Create collection</h2>
<p className="form__field"> </div>
<label htmlFor="name" className="form__label">Collection name</label> <div className="dashboard-content">
<input <div className="sketches-table-container">
className="form__input" <form className="form" onSubmit={this.handleCreateCollection}>
aria-label="name" {creationError && <span className="form-error">Couldn&apos;t create collection</span>}
type="text" <p className="form__field">
id="name" <label htmlFor="name" className="form__label">Collection name</label>
value={name} <input
placeholder={generatedCollectionName} className="form__input"
onChange={this.handleTextChange('name')} aria-label="name"
/> type="text"
{invalid && <span className="form-error">Collection name is required</span>} id="name"
</p> value={name}
<p className="form__field"> placeholder={generatedCollectionName}
<label htmlFor="description" className="form__label">Description (optional)</label> onChange={this.handleTextChange('name')}
<textarea />
className="form__input form__input-flexible-height" {invalid && <span className="form-error">Collection name is required</span>}
aria-label="description" </p>
type="text" <p className="form__field">
id="description" <label htmlFor="description" className="form__label">Description (optional)</label>
value={description} <textarea
onChange={this.handleTextChange('description')} className="form__input form__input-flexible-height"
placeholder="My fave sketches" aria-label="description"
rows="4" type="text"
/> id="description"
</p> value={description}
<input type="submit" disabled={invalid} value="Create collection" aria-label="create collection" /> onChange={this.handleTextChange('description')}
</form> placeholder="My fave sketches"
</div> rows="4"
/>
</p>
<input type="submit" disabled={invalid} value="Create collection" aria-label="create collection" />
</form>
</div>
</div>
</section>
); );
} }
} }

View file

@ -7,6 +7,7 @@ import { updateSettings, initiateVerification, createApiKey, removeApiKey } from
import NavBasic from '../../../components/NavBasic'; import NavBasic from '../../../components/NavBasic';
import CollectionCreate from '../components/CollectionCreate'; import CollectionCreate from '../components/CollectionCreate';
import Collection from '../components/Collection';
class CollectionView extends React.Component { class CollectionView extends React.Component {
static defaultProps = { static defaultProps = {
@ -61,7 +62,12 @@ class CollectionView extends React.Component {
return <CollectionCreate />; return <CollectionCreate />;
} }
return 'collection page'; return (
<Collection
collectionId={this.props.params.collection_id}
username={this.props.params.username}
/>
);
} }
render() { render() {
@ -69,15 +75,7 @@ class CollectionView extends React.Component {
<div className="dashboard"> <div className="dashboard">
<NavBasic onBack={this.closeAccountPage} /> <NavBasic onBack={this.closeAccountPage} />
<section className="dashboard-header"> {this.renderContent()}
<div className="dashboard-header__header">
<h2 className="dashboard-header__header__title">{this.pageTitle()}</h2>
</div>
<div className="dashboard-content">
{this.renderContent()}
</div>
</section>
</div> </div>
); );
} }
@ -102,6 +100,7 @@ CollectionView.propTypes = {
pathname: PropTypes.string.isRequired, pathname: PropTypes.string.isRequired,
}).isRequired, }).isRequired,
params: PropTypes.shape({ params: PropTypes.shape({
collection_id: PropTypes.string.isRequired,
username: PropTypes.string.isRequired, username: PropTypes.string.isRequired,
}).isRequired, }).isRequired,
previousPath: PropTypes.string.isRequired, previousPath: PropTypes.string.isRequired,

View file

@ -45,7 +45,7 @@ const routes = store => (
<Route path="/:username/sketches" component={IDEView} /> <Route path="/:username/sketches" component={IDEView} />
<Route path="/:username/collections" component={DashboardView} /> <Route path="/:username/collections" component={DashboardView} />
<Route path="/:username/collections/create" component={CollectionView} /> <Route path="/:username/collections/create" component={CollectionView} />
<Route path="/:username/collections/:collection_id" component={DashboardView} /> <Route path="/:username/collections/:collection_id" component={CollectionView} />
<Route path="/about" component={IDEView} /> <Route path="/about" component={IDEView} />
<Route path="/feedback" component={IDEView} /> <Route path="/feedback" component={IDEView} />
</Route> </Route>

View file

@ -0,0 +1,21 @@
.collection-container {
}
.collection-metadata {
margin: 0px #{56 / $base-font-size}rem;
padding: #{24 / $base-font-size}rem #{10 / $base-font-size}rem;
@include themify() {
background-color: getThemifyVariable('modal-background-color');
border-bottom: 1px dotted getThemifyVariable('modal-border-color');
}
}
.collection-metadata__user {
padding-top: #{12 / $base-font-size}rem;
}
.collection-metadata__description {
padding-top: #{24 / $base-font-size}rem;
}

View file

@ -2,6 +2,10 @@
padding: 24px 66px; padding: 24px 66px;
} }
.dashboard-header--no-vertical-padding {
padding: 0 66px;
}
.dashboard-header__tabs { .dashboard-header__tabs {
display: flex; display: flex;
padding-top: #{24 / $base-font-size}rem; padding-top: #{24 / $base-font-size}rem;

View file

@ -48,6 +48,7 @@
@import 'components/tabs'; @import 'components/tabs';
@import 'components/dashboard-header'; @import 'components/dashboard-header';
@import 'components/editable-input'; @import 'components/editable-input';
@import 'components/collection';
@import 'layout/dashboard'; @import 'layout/dashboard';
@import 'layout/ide'; @import 'layout/ide';

View file

@ -112,10 +112,14 @@ router.get('/:username/sketches', (req, res) => {
)); ));
}); });
router.get('/:username/collections', (req, res) => { router.get('/:username/collections/create', (req, res) => {
userExists(req.params.username, exists => ( userExists(req.params.username, (exists) => {
exists ? res.send(renderIndex()) : get404Sketch(html => res.send(html)) const isLoggedInUser = req.user && req.user.username === req.params.username;
)); const canAccess = exists && isLoggedInUser;
return canAccess ?
res.send(renderIndex()) :
get404Sketch(html => res.send(html));
});
}); });
router.get('/:username/collections/create', (req, res) => { router.get('/:username/collections/create', (req, res) => {
@ -136,4 +140,10 @@ router.get('/:username/collections/:id', (req, res) => {
)); ));
}); });
router.get('/:username/collections', (req, res) => {
userExists(req.params.username, exists => (
exists ? res.send(renderIndex()) : get404Sketch(html => res.send(html))
));
});
export default router; export default router;