Commit c42d4eeaceb50a4195d854a48fe6122f086724e5
1 parent
23c8a4c3c3
Exists in
master
handful of changes
- Remove dependency on faker NPM package. - Consolidate insert and update methods into single upsert method. - Add /edit and /new pages for editing and creating documents. - Move all exports to default exports (linter thing). - Add a module for editing and creating documents. - Update method tests for documents collection to use new upsert method. - Oh, I'm sure there's something else...
Showing
25 changed files
with
294 additions
and
76 deletions
Show diff stats
.meteor/versions
... | ... | @@ -4,13 +4,13 @@ alanning:roles@1.2.15 |
4 | 4 | aldeed:collection2@2.10.0 |
5 | 5 | aldeed:collection2-core@1.2.0 |
6 | 6 | aldeed:schema-deny@1.1.0 |
7 | -aldeed:schema-index@1.1.0 | |
7 | +aldeed:schema-index@1.1.1 | |
8 | 8 | aldeed:simple-schema@1.5.3 |
9 | 9 | allow-deny@1.0.5 |
10 | 10 | audit-argument-checks@1.0.7 |
11 | 11 | autoupdate@1.3.12 |
12 | 12 | babel-compiler@6.13.0 |
13 | -babel-runtime@0.1.12 | |
13 | +babel-runtime@0.1.13 | |
14 | 14 | base64@1.0.10 |
15 | 15 | binary-heap@1.0.10 |
16 | 16 | blaze@2.1.9 |
... | ... | @@ -40,7 +40,7 @@ email@1.1.18 |
40 | 40 | es5-shim@4.6.15 |
41 | 41 | fastclick@1.0.13 |
42 | 42 | fortawesome:fontawesome@4.6.3 |
43 | -fourseven:scss@3.10.0 | |
43 | +fourseven:scss@3.10.1 | |
44 | 44 | geojson-utils@1.0.10 |
45 | 45 | hot-code-push@1.0.4 |
46 | 46 | html-tools@1.0.11 | ... | ... |
imports/api/documents/documents.js
1 | -if (Meteor.isClient) import faker from 'faker'; | |
2 | 1 | import { Mongo } from 'meteor/mongo'; |
3 | 2 | import { SimpleSchema } from 'meteor/aldeed:simple-schema'; |
4 | 3 | import { Factory } from 'meteor/dburles:factory'; |
5 | 4 | |
6 | -export const Documents = new Mongo.Collection('Documents'); | |
5 | +const Documents = new Mongo.Collection('Documents'); | |
6 | +export default Documents; | |
7 | 7 | |
8 | 8 | Documents.allow({ |
9 | 9 | insert: () => false, |
... | ... | @@ -22,10 +22,15 @@ Documents.schema = new SimpleSchema({ |
22 | 22 | type: String, |
23 | 23 | label: 'The title of the document.', |
24 | 24 | }, |
25 | + body: { | |
26 | + type: String, | |
27 | + label: 'The body of the document.', | |
28 | + }, | |
25 | 29 | }); |
26 | 30 | |
27 | 31 | Documents.attachSchema(Documents.schema); |
28 | 32 | |
29 | 33 | Factory.define('document', Documents, { |
30 | - title: () => faker.hacker.phrase(), | |
34 | + title: () => 'Factory Title', | |
35 | + body: () => 'Factory Body', | |
31 | 36 | }); | ... | ... |
imports/api/documents/documents.tests.js
... | ... | @@ -2,7 +2,7 @@ |
2 | 2 | /* eslint-disable func-names, prefer-arrow-callback */ |
3 | 3 | |
4 | 4 | import { assert } from 'meteor/practicalmeteor:chai'; |
5 | -import { Documents } from './documents.js'; | |
5 | +import Documents from './documents.js'; | |
6 | 6 | |
7 | 7 | describe('Documents collection', function () { |
8 | 8 | it('registers the collection with Mongo properly', function () { | ... | ... |
imports/api/documents/methods.js
1 | 1 | import { SimpleSchema } from 'meteor/aldeed:simple-schema'; |
2 | 2 | import { ValidatedMethod } from 'meteor/mdg:validated-method'; |
3 | -import { Documents } from './documents'; | |
3 | +import Documents from './documents'; | |
4 | 4 | import { rateLimit } from '../../modules/rate-limit.js'; |
5 | 5 | |
6 | -export const insertDocument = new ValidatedMethod({ | |
7 | - name: 'documents.insert', | |
6 | +export const upsertDocument = new ValidatedMethod({ | |
7 | + name: 'documents.upsert', | |
8 | 8 | validate: new SimpleSchema({ |
9 | - title: { type: String }, | |
9 | + _id: { type: String, optional: true }, | |
10 | + title: { type: String, optional: true }, | |
11 | + body: { type: String, optional: true }, | |
10 | 12 | }).validator(), |
11 | 13 | run(document) { |
12 | - Documents.insert(document); | |
13 | - }, | |
14 | -}); | |
15 | - | |
16 | -export const updateDocument = new ValidatedMethod({ | |
17 | - name: 'documents.update', | |
18 | - validate: new SimpleSchema({ | |
19 | - _id: { type: String }, | |
20 | - 'update.title': { type: String, optional: true }, | |
21 | - }).validator(), | |
22 | - run({ _id, update }) { | |
23 | - Documents.update(_id, { $set: update }); | |
14 | + return Documents.upsert({ _id: document._id }, { $set: document }); | |
24 | 15 | }, |
25 | 16 | }); |
26 | 17 | |
... | ... | @@ -36,8 +27,7 @@ export const removeDocument = new ValidatedMethod({ |
36 | 27 | |
37 | 28 | rateLimit({ |
38 | 29 | methods: [ |
39 | - insertDocument, | |
40 | - updateDocument, | |
30 | + upsertDocument, | |
41 | 31 | removeDocument, |
42 | 32 | ], |
43 | 33 | limit: 5, | ... | ... |
imports/api/documents/methods.tests.js
... | ... | @@ -5,8 +5,8 @@ import { Meteor } from 'meteor/meteor'; |
5 | 5 | import { assert } from 'meteor/practicalmeteor:chai'; |
6 | 6 | import { resetDatabase } from 'meteor/xolvio:cleaner'; |
7 | 7 | import { Factory } from 'meteor/dburles:factory'; |
8 | -import { Documents } from './documents.js'; | |
9 | -import { insertDocument, updateDocument, removeDocument } from './methods.js'; | |
8 | +import Documents from './documents.js'; | |
9 | +import { upsertDocument, removeDocument } from './methods.js'; | |
10 | 10 | |
11 | 11 | describe('Documents methods', function () { |
12 | 12 | beforeEach(function () { |
... | ... | @@ -16,19 +16,22 @@ describe('Documents methods', function () { |
16 | 16 | }); |
17 | 17 | |
18 | 18 | it('inserts a document into the Documents collection', function () { |
19 | - insertDocument.call({ title: 'You can\'t arrest me, I\'m the Cake Boss!' }); | |
19 | + upsertDocument.call({ | |
20 | + title: 'You can\'t arrest me, I\'m the Cake Boss!', | |
21 | + body: 'They went nuts!', | |
22 | + }); | |
23 | + | |
20 | 24 | const getDocument = Documents.findOne({ title: 'You can\'t arrest me, I\'m the Cake Boss!' }); |
21 | - assert.equal(getDocument.title, 'You can\'t arrest me, I\'m the Cake Boss!'); | |
25 | + assert.equal(getDocument.body, 'They went nuts!'); | |
22 | 26 | }); |
23 | 27 | |
24 | 28 | it('updates a document in the Documents collection', function () { |
25 | 29 | const { _id } = Factory.create('document'); |
26 | 30 | |
27 | - updateDocument.call({ | |
31 | + upsertDocument.call({ | |
28 | 32 | _id, |
29 | - update: { | |
30 | - title: 'You can\'t arrest me, I\'m the Cake Boss!', | |
31 | - }, | |
33 | + title: 'You can\'t arrest me, I\'m the Cake Boss!', | |
34 | + body: 'They went nuts!', | |
32 | 35 | }); |
33 | 36 | |
34 | 37 | const getDocument = Documents.findOne(_id); | ... | ... |
imports/api/documents/server/publications.js
1 | 1 | import { Meteor } from 'meteor/meteor'; |
2 | -import { Documents } from '../documents'; | |
2 | +import { check } from 'meteor/check'; | |
3 | +import Documents from '../documents'; | |
3 | 4 | |
4 | -Meteor.publish('documents', () => Documents.find()); | |
5 | +Meteor.publish('documents.list', () => Documents.find()); | |
6 | + | |
7 | +Meteor.publish('documents.view', (_id) => { | |
8 | + check(_id, String); | |
9 | + return Documents.find(_id); | |
10 | +}); | |
11 | + | |
12 | +Meteor.publish('documents.edit', (_id) => { | |
13 | + check(_id, String); | |
14 | + return Documents.find(_id); | |
15 | +}); | ... | ... |
imports/modules/document-editor.js
... | ... | @@ -0,0 +1,55 @@ |
1 | +import $ from 'jquery'; | |
2 | +import 'jquery-validation'; | |
3 | +import { browserHistory } from 'react-router'; | |
4 | +import { Bert } from 'meteor/themeteorchef:bert'; | |
5 | +import { upsertDocument } from '../api/documents/methods.js'; | |
6 | + | |
7 | +let component; | |
8 | + | |
9 | +const handleUpsert = () => { | |
10 | + const { doc } = component.props; | |
11 | + const confirmation = doc && doc._id ? 'Document updated!' : 'Document added!'; | |
12 | + const upsert = { | |
13 | + title: document.querySelector('[name="title"]').value.trim(), | |
14 | + body: document.querySelector('[name="body"]').value.trim(), | |
15 | + }; | |
16 | + | |
17 | + if (doc && doc._id) upsert._id = doc._id; | |
18 | + | |
19 | + upsertDocument.call(upsert, (error, { insertedId }) => { | |
20 | + if (error) { | |
21 | + Bert.alert(error.reason, 'danger'); | |
22 | + } else { | |
23 | + component.form.reset(); | |
24 | + Bert.alert(confirmation, 'success'); | |
25 | + browserHistory.push(`/documents/${insertedId || doc._id}`); | |
26 | + } | |
27 | + }); | |
28 | +}; | |
29 | + | |
30 | +const validate = () => { | |
31 | + $(component.form).validate({ | |
32 | + rules: { | |
33 | + title: { | |
34 | + required: true, | |
35 | + }, | |
36 | + body: { | |
37 | + required: true, | |
38 | + }, | |
39 | + }, | |
40 | + messages: { | |
41 | + title: { | |
42 | + required: 'Need a title in here, Seuss.', | |
43 | + }, | |
44 | + body: { | |
45 | + required: 'This thneeds a body, please.', | |
46 | + }, | |
47 | + }, | |
48 | + submitHandler() { handleUpsert(); }, | |
49 | + }); | |
50 | +}; | |
51 | + | |
52 | +export default function handleLogin(options) { | |
53 | + component = options.component; | |
54 | + validate(); | |
55 | +} | ... | ... |
imports/modules/login.js
imports/startup/client/routes.js
1 | +/* eslint-disable max-len */ | |
2 | + | |
1 | 3 | import React from 'react'; |
2 | 4 | import { render } from 'react-dom'; |
3 | 5 | import { Router, Route, IndexRoute, browserHistory } from 'react-router'; |
4 | 6 | import { Meteor } from 'meteor/meteor'; |
5 | -import { App } from '../../ui/layouts/App.js'; | |
6 | -import { Documents } from '../../ui/pages/Documents.js'; | |
7 | -import { Index } from '../../ui/pages/Index.js'; | |
8 | -import { Login } from '../../ui/pages/Login.js'; | |
9 | -import { NotFound } from '../../ui/pages/NotFound.js'; | |
10 | -import { RecoverPassword } from '../../ui/pages/RecoverPassword.js'; | |
11 | -import { ResetPassword } from '../../ui/pages/ResetPassword.js'; | |
12 | -import { Signup } from '../../ui/pages/Signup.js'; | |
7 | +import App from '../../ui/layouts/App.js'; | |
8 | +import Documents from '../../ui/pages/Documents.js'; | |
9 | +import NewDocument from '../../ui/pages/NewDocument.js'; | |
10 | +import EditDocument from '../../ui/containers/EditDocument.js'; | |
11 | +import ViewDocument from '../../ui/containers/ViewDocument.js'; | |
12 | +import Index from '../../ui/pages/Index.js'; | |
13 | +import Login from '../../ui/pages/Login.js'; | |
14 | +import NotFound from '../../ui/pages/NotFound.js'; | |
15 | +import RecoverPassword from '../../ui/pages/RecoverPassword.js'; | |
16 | +import ResetPassword from '../../ui/pages/ResetPassword.js'; | |
17 | +import Signup from '../../ui/pages/Signup.js'; | |
13 | 18 | |
14 | 19 | const requireAuth = (nextState, replace) => { |
15 | 20 | if (!Meteor.loggingIn() && !Meteor.userId()) { |
... | ... | @@ -26,6 +31,9 @@ Meteor.startup(() => { |
26 | 31 | <Route path="/" component={ App }> |
27 | 32 | <IndexRoute name="index" component={ Index } onEnter={ requireAuth } /> |
28 | 33 | <Route name="documents" path="/documents" component={ Documents } onEnter={ requireAuth } /> |
34 | + <Route name="newDocument" path="/documents/new" component={ NewDocument } onEnter={ requireAuth } /> | |
35 | + <Route name="editDocument" path="/documents/:_id/edit" component={ EditDocument } onEnter={ requireAuth } /> | |
36 | + <Route name="editDocument" path="/documents/:_id" component={ ViewDocument } onEnter={ requireAuth } /> | |
29 | 37 | <Route name="login" path="/login" component={ Login } /> |
30 | 38 | <Route name="recover-password" path="/recover-password" component={ RecoverPassword } /> |
31 | 39 | <Route name="reset-password" path="/reset-password/:token" component={ ResetPassword } /> | ... | ... |
imports/ui/components/AppNavigation.js
1 | 1 | import React from 'react'; |
2 | 2 | import { Navbar } from 'react-bootstrap'; |
3 | 3 | import { Link } from 'react-router'; |
4 | -import { PublicNavigation } from './PublicNavigation.js'; | |
5 | -import { AuthenticatedNavigation } from './AuthenticatedNavigation.js'; | |
4 | +import PublicNavigation from './PublicNavigation.js'; | |
5 | +import AuthenticatedNavigation from './AuthenticatedNavigation.js'; | |
6 | 6 | |
7 | 7 | const renderNavigation = hasUser => (hasUser ? <AuthenticatedNavigation /> : <PublicNavigation />); |
8 | 8 | ... | ... |
imports/ui/components/DocumentEditor.js
... | ... | @@ -0,0 +1,45 @@ |
1 | +/* eslint-disable max-len, no-return-assign */ | |
2 | + | |
3 | +import React from 'react'; | |
4 | +import { FormGroup, ControlLabel, FormControl, Button } from 'react-bootstrap'; | |
5 | +import documentEditor from '../../modules/document-editor.js'; | |
6 | + | |
7 | +export default class DocumentEditor extends React.Component { | |
8 | + componentDidMount() { | |
9 | + documentEditor({ component: this }); | |
10 | + } | |
11 | + | |
12 | + render() { | |
13 | + const { doc } = this.props; | |
14 | + return (<form | |
15 | + ref={ form => this.form = form } | |
16 | + onSubmit={ event => event.preventDefault() } | |
17 | + > | |
18 | + <FormGroup> | |
19 | + <ControlLabel>Title</ControlLabel> | |
20 | + <FormControl | |
21 | + type="text" | |
22 | + name="title" | |
23 | + defaultValue={ doc && doc.title } | |
24 | + placeholder="Oh, The Places You'll Go!" | |
25 | + /> | |
26 | + </FormGroup> | |
27 | + <FormGroup> | |
28 | + <ControlLabel>Body</ControlLabel> | |
29 | + <FormControl | |
30 | + componentClass="textarea" | |
31 | + name="body" | |
32 | + defaultValue={ doc && doc.body } | |
33 | + placeholder="Congratulations! Today is your day. You're off to Great Places! You're off and away!" | |
34 | + /> | |
35 | + </FormGroup> | |
36 | + <Button type="submit" bsStyle="success"> | |
37 | + { doc && doc._id ? 'Save Changes' : 'Add Document' } | |
38 | + </Button> | |
39 | + </form>); | |
40 | + } | |
41 | +} | |
42 | + | |
43 | +DocumentEditor.propTypes = { | |
44 | + doc: React.PropTypes.object, | |
45 | +}; | ... | ... |
imports/ui/components/DocumentsList.js
1 | 1 | import React from 'react'; |
2 | -import { ListGroup, Alert } from 'react-bootstrap'; | |
3 | -import { Document } from './Document.js'; | |
2 | +import { ListGroup, ListGroupItem, Alert } from 'react-bootstrap'; | |
4 | 3 | |
5 | 4 | const DocumentsList = ({ documents }) => ( |
6 | - documents.length > 0 ? <ListGroup className="documents-list"> | |
7 | - {documents.map(doc => ( | |
8 | - <Document key={ doc._id } document={ doc } /> | |
5 | + documents.length > 0 ? <ListGroup className="DocumentsList"> | |
6 | + {documents.map(({ _id, title }) => ( | |
7 | + <ListGroupItem key={ _id } href={`/documents/${_id}`}>{ title }</ListGroupItem> | |
9 | 8 | ))} |
10 | 9 | </ListGroup> : |
11 | 10 | <Alert bsStyle="warning">No documents yet.</Alert> | ... | ... |
imports/ui/containers/AppNavigation.js
1 | 1 | import { composeWithTracker } from 'react-komposer'; |
2 | 2 | import { Meteor } from 'meteor/meteor'; |
3 | -import { AppNavigation } from '../components/AppNavigation.js'; | |
3 | +import AppNavigation from '../components/AppNavigation.js'; | |
4 | 4 | |
5 | 5 | const composer = (props, onData) => onData(null, { hasUser: Meteor.user() }); |
6 | 6 | ... | ... |
imports/ui/containers/DocumentsList.js
1 | 1 | import { composeWithTracker } from 'react-komposer'; |
2 | 2 | import { Meteor } from 'meteor/meteor'; |
3 | -import { Documents } from '../../api/documents/documents.js'; | |
4 | -import { DocumentsList } from '../components/DocumentsList.js'; | |
5 | -import { Loading } from '../components/Loading.js'; | |
3 | +import Documents from '../../api/documents/documents.js'; | |
4 | +import DocumentsList from '../components/DocumentsList.js'; | |
5 | +import Loading from '../components/Loading.js'; | |
6 | 6 | |
7 | 7 | const composer = (params, onData) => { |
8 | - const subscription = Meteor.subscribe('documents'); | |
8 | + const subscription = Meteor.subscribe('documents.list'); | |
9 | 9 | if (subscription.ready()) { |
10 | 10 | const documents = Documents.find().fetch(); |
11 | 11 | onData(null, { documents }); | ... | ... |
imports/ui/containers/EditDocument.js
... | ... | @@ -0,0 +1,16 @@ |
1 | +import { Meteor } from 'meteor/meteor'; | |
2 | +import { composeWithTracker } from 'react-komposer'; | |
3 | +import Documents from '../../api/documents/documents.js'; | |
4 | +import EditDocument from '../pages/EditDocument.js'; | |
5 | +import { Loading } from '../components/Loading.js'; | |
6 | + | |
7 | +const composer = ({ params }, onData) => { | |
8 | + const subscription = Meteor.subscribe('documents.edit', params._id); | |
9 | + | |
10 | + if (subscription.ready()) { | |
11 | + const doc = Documents.findOne(); | |
12 | + onData(null, { doc }); | |
13 | + } | |
14 | +}; | |
15 | + | |
16 | +export default composeWithTracker(composer, Loading)(EditDocument); | ... | ... |
imports/ui/containers/ViewDocument.js
... | ... | @@ -0,0 +1,16 @@ |
1 | +import { Meteor } from 'meteor/meteor'; | |
2 | +import { composeWithTracker } from 'react-komposer'; | |
3 | +import Documents from '../../api/documents/documents.js'; | |
4 | +import ViewDocument from '../pages/ViewDocument.js'; | |
5 | +import { Loading } from '../components/Loading.js'; | |
6 | + | |
7 | +const composer = ({ params }, onData) => { | |
8 | + const subscription = Meteor.subscribe('documents.view', params._id); | |
9 | + | |
10 | + if (subscription.ready()) { | |
11 | + const doc = Documents.findOne(); | |
12 | + onData(null, { doc }); | |
13 | + } | |
14 | +}; | |
15 | + | |
16 | +export default composeWithTracker(composer, Loading)(ViewDocument); | ... | ... |
imports/ui/pages/EditDocument.js
... | ... | @@ -0,0 +1,15 @@ |
1 | +import React from 'react'; | |
2 | +import DocumentEditor from '../components/DocumentEditor.js'; | |
3 | + | |
4 | +const EditDocument = ({ doc }) => ( | |
5 | + <div className="EditDocument"> | |
6 | + <h4 className="page-header">Editing "{ doc.title }"</h4> | |
7 | + <DocumentEditor doc={ doc } /> | |
8 | + </div> | |
9 | +); | |
10 | + | |
11 | +EditDocument.propTypes = { | |
12 | + doc: React.PropTypes.object, | |
13 | +}; | |
14 | + | |
15 | +export default EditDocument; | ... | ... |
imports/ui/pages/NewDocument.js
... | ... | @@ -0,0 +1,11 @@ |
1 | +import React from 'react'; | |
2 | +import DocumentEditor from '../components/DocumentEditor.js'; | |
3 | + | |
4 | +const NewDocument = () => ( | |
5 | + <div className="NewDocument"> | |
6 | + <h4 className="page-header">New Document</h4> | |
7 | + <DocumentEditor /> | |
8 | + </div> | |
9 | +); | |
10 | + | |
11 | +export default NewDocument; | ... | ... |
imports/ui/pages/RecoverPassword.js
1 | 1 | import React from 'react'; |
2 | 2 | import { Row, Col, Alert, FormGroup, FormControl, Button } from 'react-bootstrap'; |
3 | -import { handleRecoverPassword } from '../../modules/recover-password'; | |
3 | +import handleRecoverPassword from '../../modules/recover-password'; | |
4 | 4 | |
5 | 5 | export default class RecoverPassword extends React.Component { |
6 | 6 | componentDidMount() { |
7 | 7 | handleRecoverPassword({ component: this }); |
8 | 8 | } |
9 | 9 | |
10 | - handleSubmit() { | |
11 | - this.preventDefault(); | |
10 | + handleSubmit(event) { | |
11 | + event.preventDefault(); | |
12 | 12 | } |
13 | 13 | |
14 | 14 | render() { | ... | ... |
imports/ui/pages/ResetPassword.js
1 | 1 | import React from 'react'; |
2 | 2 | import { Row, Col, Alert, FormGroup, ControlLabel, FormControl, Button } from 'react-bootstrap'; |
3 | -import { handleResetPassword } from '../../modules/reset-password'; | |
3 | +import handleResetPassword from '../../modules/reset-password'; | |
4 | 4 | |
5 | 5 | export default class ResetPassword extends React.Component { |
6 | 6 | componentDidMount() { |
7 | 7 | handleResetPassword({ component: this, token: this.props.params.token }); |
8 | 8 | } |
9 | 9 | |
10 | - handleSubmit() { | |
11 | - this.preventDefault(); | |
10 | + handleSubmit(event) { | |
11 | + event.preventDefault(); | |
12 | 12 | } |
13 | 13 | |
14 | 14 | render() { | ... | ... |
imports/ui/pages/ViewDocument.js
... | ... | @@ -0,0 +1,39 @@ |
1 | +import React from 'react'; | |
2 | +import { ButtonToolbar, ButtonGroup, Button } from 'react-bootstrap'; | |
3 | +import { browserHistory } from 'react-router'; | |
4 | +import { Bert } from 'meteor/themeteorchef:bert'; | |
5 | +import { removeDocument } from '../../api/documents/methods.js'; | |
6 | + | |
7 | +const handleRemove = (_id) => { | |
8 | + if (confirm('Are you sure? This is permanent!')) { | |
9 | + removeDocument.call({ _id }, (error) => { | |
10 | + if (error) { | |
11 | + Bert.alert(error.reason, 'danger'); | |
12 | + } else { | |
13 | + Bert.alert('Document deleted!', 'success'); | |
14 | + browserHistory.push('/documents'); | |
15 | + } | |
16 | + }); | |
17 | + } | |
18 | +}; | |
19 | + | |
20 | +const ViewDocument = ({ doc }) => ( | |
21 | + <div className="ViewDocument"> | |
22 | + <div className="page-header clearfix"> | |
23 | + <h4 className="pull-left">{ doc.title }</h4> | |
24 | + <ButtonToolbar className="pull-right"> | |
25 | + <ButtonGroup bsSize="small"> | |
26 | + <Button href={`/documents/${doc._id}/edit`}>Edit</Button> | |
27 | + <Button onClick={ () => handleRemove(doc._id) } className="text-danger">Delete</Button> | |
28 | + </ButtonGroup> | |
29 | + </ButtonToolbar> | |
30 | + </div> | |
31 | + { doc.body } | |
32 | + </div> | |
33 | +); | |
34 | + | |
35 | +ViewDocument.propTypes = { | |
36 | + doc: React.PropTypes.object.isRequired, | |
37 | +}; | |
38 | + | |
39 | +export default ViewDocument; | ... | ... |
imports/ui/pages/documents.js
1 | 1 | import React from 'react'; |
2 | -import { Row, Col } from 'react-bootstrap'; | |
2 | +import { Row, Col, Button } from 'react-bootstrap'; | |
3 | 3 | import DocumentsList from '../containers/DocumentsList.js'; |
4 | -import { AddDocument } from '../components/AddDocument.js'; | |
5 | 4 | |
6 | 5 | const Documents = () => ( |
7 | 6 | <Row> |
8 | 7 | <Col xs={ 12 }> |
9 | - <h4 className="page-header">Documents</h4> | |
10 | - <AddDocument /> | |
8 | + <div className="page-header clearfix"> | |
9 | + <h4 className="pull-left">Documents</h4> | |
10 | + <Button | |
11 | + bsStyle="success" | |
12 | + className="pull-right" | |
13 | + href="/documents/new" | |
14 | + >New Document</Button> | |
15 | + </div> | |
11 | 16 | <DocumentsList /> |
12 | 17 | </Col> |
13 | 18 | </Row> | ... | ... |
imports/ui/pages/login.js
1 | 1 | import React from 'react'; |
2 | 2 | import { Link } from 'react-router'; |
3 | 3 | import { Row, Col, FormGroup, ControlLabel, FormControl, Button } from 'react-bootstrap'; |
4 | -import { handleLogin } from '../../modules/login'; | |
4 | +import handleLogin from '../../modules/login'; | |
5 | 5 | |
6 | 6 | export default class Login extends React.Component { |
7 | 7 | componentDidMount() { |
8 | 8 | handleLogin({ component: this }); |
9 | 9 | } |
10 | 10 | |
11 | - handleSubmit() { | |
12 | - this.preventDefault(); | |
11 | + handleSubmit(event) { | |
12 | + event.preventDefault(); | |
13 | 13 | } |
14 | 14 | |
15 | 15 | render() { | ... | ... |
imports/ui/pages/signup.js
1 | 1 | import React from 'react'; |
2 | 2 | import { Link } from 'react-router'; |
3 | 3 | import { Row, Col, FormGroup, ControlLabel, FormControl, Button } from 'react-bootstrap'; |
4 | -import { handleSignup } from '../../modules/signup'; | |
4 | +import handleSignup from '../../modules/signup'; | |
5 | 5 | |
6 | 6 | export default class Signup extends React.Component { |
7 | 7 | componentDidMount() { |
8 | 8 | handleSignup({ component: this }); |
9 | 9 | } |
10 | 10 | |
11 | - handleSubmit() { | |
12 | - this.preventDefault(); | |
11 | + handleSubmit(event) { | |
12 | + event.preventDefault(); | |
13 | 13 | } |
14 | 14 | |
15 | 15 | render() { | ... | ... |
package.json
... | ... | @@ -17,8 +17,7 @@ |
17 | 17 | "eslint-plugin-import": "^1.16.0", |
18 | 18 | "eslint-plugin-jsx-a11y": "^2.2.3", |
19 | 19 | "eslint-plugin-meteor": "^4.0.1", |
20 | - "eslint-plugin-react": "^6.4.1", | |
21 | - "faker": "^3.1.0" | |
20 | + "eslint-plugin-react": "^6.4.1" | |
22 | 21 | }, |
23 | 22 | "eslintConfig": { |
24 | 23 | "parserOptions": { |
... | ... | @@ -58,13 +57,14 @@ |
58 | 57 | "_name" |
59 | 58 | ] |
60 | 59 | } |
61 | - ] | |
60 | + ], | |
61 | + "class-methods-use-this": 0 | |
62 | 62 | } |
63 | 63 | }, |
64 | 64 | "dependencies": { |
65 | 65 | "bcrypt": "^0.8.7", |
66 | 66 | "bootstrap": "^3.3.7", |
67 | - "jquery": "^3.1.1", | |
67 | + "jquery": "^2.2.4", | |
68 | 68 | "jquery-validation": "^1.15.1", |
69 | 69 | "react": "^15.3.2", |
70 | 70 | "react-addons-pure-render-mixin": "^15.3.2", | ... | ... |