editor workflow ui adjustments

This commit is contained in:
Cássio Zen 2016-09-09 17:15:58 -03:00
parent b6874152d9
commit c84d538eb6
6 changed files with 107 additions and 64 deletions

View File

@ -67,6 +67,7 @@
},
"dependencies": {
"bricks.js": "^1.7.0",
"dateformat": "^1.0.12",
"fuzzy": "^0.1.1",
"js-base64": "^2.1.9",
"json-loader": "^0.5.4",

View File

@ -103,24 +103,12 @@ export default class API {
}
retrieveMetadata(key) {
const cache = LocalForage.getItem(`gh.meta.${key}`);
return cache.then((cached) => {
if (cached && cached.expires > Date.now()) { return cached.data; }
return this.request(`${this.repoURL}/contents/${key}.json`, {
params: { ref: 'refs/meta/_netlify_cms' },
headers: { Accept: 'application/vnd.github.VERSION.raw' },
cache: 'no-store',
})
.then(response => JSON.parse(response))
.then((result) => {
LocalForage.setItem(`gh.meta.${key}`, {
expires: Date.now() + 300000, // In 5 minutes
data: result,
});
return result;
});
}).catch(error => null);
return this.request(`${this.repoURL}/contents/${key}.json`, {
params: { ref: 'refs/meta/_netlify_cms' },
headers: { Accept: 'application/vnd.github.VERSION.raw' },
cache: 'no-store',
})
.then(response => JSON.parse(response));
}
readFile(path, sha, branch = this.branch) {
@ -148,14 +136,26 @@ export default class API {
}
readUnpublishedBranchFile(contentKey) {
let metaData;
return this.retrieveMetadata(contentKey)
.then(data => {
metaData = data;
return this.readFile(data.objects.entry, null, data.branch);
})
.then(file => {
return { metaData, file };
const cache = LocalForage.getItem(`gh.unpublished.${contentKey}`);
return cache.then((cached) => {
if (cached && cached.expires > Date.now()) { return cached.data; }
let metaData;
return this.retrieveMetadata(contentKey)
.then(data => {
metaData = data;
return this.readFile(data.objects.entry, null, data.branch);
})
.then(file => {
return { metaData, file };
})
.then((result) => {
LocalForage.setItem(`gh.unpublished.${contentKey}`, {
expires: Date.now() + 300000, // In 5 minutes
data: result,
});
return result;
});
});
}
@ -191,19 +191,24 @@ export default class API {
if (options.mode && options.mode === EDITORIAL_WORKFLOW) {
const contentKey = options.collectionName ? `${options.collectionName}-${entry.slug}` : entry.slug;
const branchName = `cms/${contentKey}`;
return this.createBranch(branchName, response.sha)
.then(this.storeMetadata(contentKey, {
return this.user().then(user => {
return user.name ? user.name : user.login;
})
.then(username => this.storeMetadata(contentKey, {
type: 'PR',
status: status.DRAFT,
user: username,
status: status.first(),
branch: branchName,
collection: options.collectionName,
title: options.parsedData.title,
description: options.parsedData.description,
title: options.parsedData && options.parsedData.title,
description: options.parsedData && options.parsedData.description,
objects: {
entry: entry.path,
files: mediaFiles.map(file => file.path)
}
},
timeStamp: new Date().toISOString()
}))
.then(this.createBranch(branchName, response.sha))
.then(this.createPR(options.commitMessage, `cms/${contentKey}`));
} else {
return this.patchBranch(this.branch, response.sha);

View File

@ -0,0 +1,29 @@
.column {
position: relative;
display: inline-block;
vertical-align: top;
text-align: center;
width: 28%;
}
.column:not(:last-child) {
margin-right: 8%;
}
.card {
width: 100% !important;
margin: 7px 0;
& h1 {
font-size: 17px;
& small {
font-weight: normal;
}
}
& p {
color: #555;
font-size: 12px;
margin-top: 5px;
}
}

View File

@ -1,29 +1,41 @@
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import dateFormat from 'dateFormat';
import { Card } from './UI';
import { statusDescriptions } from '../constants/publishModes';
import styles from './UnpublishedListing.css';
export default class UnpublishedListing extends React.Component {
renderColumn(entries) {
renderColumns(entries, column) {
if (!entries) return;
return (
<div>
if (!column) {
return entries.entrySeq().map(([currColumn, currEntries]) => (
<div key={currColumn} className={styles.column}>
<h3>{statusDescriptions.get(currColumn)}</h3>
{this.renderColumns(currEntries, currColumn)}
</div>
));
} else {
return <div>
{entries.map(entry => {
return <Card key={entry.get('slug')}><h4>{entry.getIn(['data', 'title'])}</h4></Card>;
// Look for an "author" field. Fallback to username on backend implementation;
const author = entry.getIn(['data', 'author'], entry.getIn(['metaData', 'user']));
const timeStamp = dateFormat(Date.parse(entry.getIn(['metaData', 'timeStamp'])), 'longDate');
return (
<Card key={entry.get('slug')} className={styles.card}>
<h1>{entry.getIn(['data', 'title'])} <small>by {author}</small></h1>
<p>Last updated: {timeStamp} by {entry.getIn(['metaData', 'user'])}</p>
</Card>
);
}
)}
</div>
);
</div>;
}
}
render() {
const { entries } = this.props;
const columns = entries.entrySeq().map(([key, currEntries]) => (
<div key={key}>
<h3>{statusDescriptions.get(key)}</h3>
{this.renderColumn(currEntries)}
</div>
));
const columns = this.renderColumns(this.props.entries);
return (
<div>
@ -34,12 +46,5 @@ export default class UnpublishedListing extends React.Component {
}
UnpublishedListing.propTypes = {
entries: ImmutablePropTypes.map,
entries: ImmutablePropTypes.orderedMap,
};
<div>
<h3>Drafts</h3>
<card>Cool Recipe</card>
</div>

View File

@ -1,18 +1,18 @@
import { Map } from 'immutable';
import { Map, OrderedMap } from 'immutable';
// Create/edit workflow modes
export const SIMPLE = 'simple';
export const EDITORIAL_WORKFLOW = 'editorial_workflow';
// Available status
export const status = {
export const status = OrderedMap({
DRAFT: 'draft',
PENDING_REVIEW: 'pending_review',
PENDING_PUBLISH: 'pending_publish',
};
});
export const statusDescriptions = Map({
[status.DRAFT]: 'Draft',
[status.PENDING_REVIEW]: 'Waiting for Review',
[status.PENDING_PUBLISH]: 'Waiting to go live',
[status.get('DRAFT')]: 'Draft',
[status.get('PENDING_REVIEW')]: 'Waiting for Review',
[status.get('PENDING_PUBLISH')]: 'Waiting to go live',
});

View File

@ -1,12 +1,11 @@
import React, { PropTypes } from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { Map } from 'immutable';
import { OrderedMap } from 'immutable';
import { init, loadUnpublishedEntries } from '../actions/editorialWorkflow';
import { selectUnpublishedEntries } from '../reducers';
import { EDITORIAL_WORKFLOW, status } from '../constants/publishModes';
import UnpublishedListing from '../components/UnpublishedListing';
import { connect } from 'react-redux';
import _ from 'lodash';
export default function EditorialWorkflow(WrappedComponent) {
class EditorialWorkflow extends WrappedComponent {
@ -45,11 +44,15 @@ export default function EditorialWorkflow(WrappedComponent) {
const returnObj = { isEditorialWorkflow };
if (isEditorialWorkflow) {
returnObj.unpublishedEntries = _.reduce(status, (acc, currStatus) => {
/*
* Generates an ordered Map of the available status as keys.
* Each key containing a List of available unpubhlished entries
* Eg.: OrderedMap{'draft':List(), 'pending_review':List(), 'pending_publish':List()}
*/
returnObj.unpublishedEntries = status.reduce((acc, currStatus) => {
return acc.set(currStatus, selectUnpublishedEntries(state, currStatus));
}, Map());
}, OrderedMap());
}
return returnObj;
}