From e3f59ca3b59acccc50532a1f8262c4cc63e18df2 Mon Sep 17 00:00:00 2001
From: john <johnbarlas39@gmail.com>
Date: Fri, 14 Apr 2017 20:51:29 +0300
Subject: [PATCH] refactor booklist into dashboard and give delete book action
 its own modal

---
 app/components/BookBuilder/BookList.jsx       | 106 ----------------
 .../{BookBuilder => Dashboard}/AddBook.jsx    |   3 +
 app/components/Dashboard/Book.jsx             | 115 ++++++++++++++++++
 app/components/Dashboard/BookList.jsx         |  44 +++++++
 app/components/Dashboard/Dashboard.jsx        |  97 +++++++++++++++
 app/components/Dashboard/DashboardHeader.jsx  |  31 +++++
 app/components/Dashboard/RemoveBookModal.jsx  |  46 +++++++
 .../dashboard.local.scss}                     |   0
 app/components/common/AbstractModal.jsx       |  14 ++-
 app/routes.jsx                                |   4 +-
 10 files changed, 351 insertions(+), 109 deletions(-)
 delete mode 100644 app/components/BookBuilder/BookList.jsx
 rename app/components/{BookBuilder => Dashboard}/AddBook.jsx (97%)
 create mode 100644 app/components/Dashboard/Book.jsx
 create mode 100644 app/components/Dashboard/BookList.jsx
 create mode 100644 app/components/Dashboard/Dashboard.jsx
 create mode 100644 app/components/Dashboard/DashboardHeader.jsx
 create mode 100644 app/components/Dashboard/RemoveBookModal.jsx
 rename app/components/{BookBuilder/styles/bookList.local.scss => Dashboard/dashboard.local.scss} (100%)

diff --git a/app/components/BookBuilder/BookList.jsx b/app/components/BookBuilder/BookList.jsx
deleted file mode 100644
index 2ea56f1..0000000
--- a/app/components/BookBuilder/BookList.jsx
+++ /dev/null
@@ -1,106 +0,0 @@
-import Actions from 'pubsweet-client/src/actions'
-import React from 'react'
-import { bindActionCreators } from 'redux'
-import { connect } from 'react-redux'
-import { Link } from 'react-router'
-
-import AddBook from './AddBook'
-import styles from './styles/bookList.local.scss'
-
-export class BookList extends React.Component {
-  constructor (props) {
-    super(props)
-
-    this.toggleModal = this.toggleModal.bind(this)
-
-    this.state = {
-      showModal: false
-    }
-  }
-
-  toggleModal () {
-    this.setState({ showModal: !this.state.showModal })
-  }
-
-  createCollection = newTitle => {
-    const { createCollection } = this.props.actions
-    const collection = { title: newTitle || 'Untitled' }
-
-    createCollection(collection)
-    this.toggleModal()
-  }
-
-  _removeCollection = (collection) => {
-    if (confirm('Remove this book?')) {
-      this.props.actions.deleteCollection(collection)
-    }
-  }
-
-  _sortNewestFirst = (a, b) => {
-    return (b.created < a.created) ? -1 : ((b.created > a.created) ? 1 : 0)
-  }
-
-  render () {
-    const { books } = this.props
-    const { showModal } = this.state
-
-    return (
-      <div className={styles.bookList + ' bootstrap pubsweet-component pubsweet-component-scroll'}>
-        <div className='container col-lg-offset-2 col-lg-8'>
-
-          <div className='col-lg-12'>
-            <h1 className={styles.bookTitle}>
-              Books
-              <div className={styles.addBookBtn} onClick={this.toggleModal}>
-                <a>add book</a>
-              </div>
-            </h1>
-          </div>
-
-          <div className='col-lg-12'>
-            {books ? books.sort(this._sortNewestFirst).map(book => (
-              <div key={book.id} className={styles.bookContainer}>
-                <h2>{book.title}</h2>
-
-                <div className={styles.bookActions}>
-                  <Link to={`/books/${book.id}/book-builder`} className={styles.editBook}>Edit</Link>
-
-                  <a href='#' onClick={() => this._removeCollection(book)} className={styles.editBook}>Remove</a>
-                </div>
-              </div>
-            )) : 'Fetching…' }
-          </div>
-        </div>
-
-        <AddBook
-          container={this}
-          create={this.createCollection}
-          show={showModal}
-          toggle={this.toggleModal}
-        />
-      </div>
-    )
-  }
-}
-
-BookList.propTypes = {
-  books: React.PropTypes.arrayOf(React.PropTypes.object),
-  actions: React.PropTypes.object.isRequired
-}
-
-function mapStateToProps (state, { params }) {
-  return {
-    books: state.collections
-  }
-}
-
-function mapDispatchToProps (dispatch) {
-  return {
-    actions: bindActionCreators(Actions, dispatch)
-  }
-}
-
-export default connect(
-  mapStateToProps,
-  mapDispatchToProps
-)(BookList)
diff --git a/app/components/BookBuilder/AddBook.jsx b/app/components/Dashboard/AddBook.jsx
similarity index 97%
rename from app/components/BookBuilder/AddBook.jsx
rename to app/components/Dashboard/AddBook.jsx
index a0746d0..6450107 100644
--- a/app/components/BookBuilder/AddBook.jsx
+++ b/app/components/Dashboard/AddBook.jsx
@@ -20,7 +20,10 @@ export default class AddBook extends React.Component {
 
   handleKeyOnInput (event) {
     if (event.charCode !== 13) return
+
+    const { toggle } = this.props
     this.onCreate()
+    toggle()
   }
 
   onCreate () {
diff --git a/app/components/Dashboard/Book.jsx b/app/components/Dashboard/Book.jsx
new file mode 100644
index 0000000..2e0b91e
--- /dev/null
+++ b/app/components/Dashboard/Book.jsx
@@ -0,0 +1,115 @@
+import React from 'react'
+import { Link } from 'react-router'
+
+import RemoveBookModal from './RemoveBookModal'
+import styles from './dashboard.local.scss'
+
+class Book extends React.Component {
+  constructor (props) {
+    super(props)
+
+    this.removeBook = this.removeBook.bind(this)
+    this.toggleModal = this.toggleModal.bind(this)
+
+    this.state = {
+      showModal: false
+    }
+  }
+
+  toggleModal () {
+    this.setState({
+      showModal: !this.state.showModal
+    })
+  }
+
+  removeBook () {
+    const { book, remove } = this.props
+    remove(book)
+  }
+
+  renderTitle () {
+    const { book } = this.props
+
+    return (
+      <h2> { book.title } </h2>
+    )
+  }
+
+  renderEdit () {
+    const { book } = this.props
+
+    return (
+      <Link
+        className={styles.editBook}
+        to={`/books/${book.id}/book-builder`}
+      >
+        Edit
+      </Link>
+    )
+  }
+
+  renderRemove () {
+    return (
+      <a
+        className={styles.editBook}
+        href='#'
+        onClick={this.toggleModal}
+      >
+        Remove
+      </a>
+    )
+  }
+
+  renderButtons () {
+    const { book } = this.props
+    const edit = this.renderEdit(book)
+    const remove = this.renderRemove(book)
+
+    return (
+      <div className={styles.bookActions}>
+        { edit }
+        { remove }
+      </div>
+    )
+  }
+
+  renderRemoveModal () {
+    const { book, container } = this.props
+    const { showModal } = this.state
+    if (!showModal) return null
+
+    return (
+      <RemoveBookModal
+        book={book}
+        container={container}
+        remove={this.removeBook}
+        show={showModal}
+        toggle={this.toggleModal}
+      />
+    )
+  }
+
+  render () {
+    const { book } = this.props
+
+    const title = this.renderTitle(book)
+    const buttons = this.renderButtons(book)
+    const removeModal = this.renderRemoveModal()
+
+    return (
+      <div className={styles.bookContainer}>
+        { title }
+        { buttons }
+        { removeModal }
+      </div>
+    )
+  }
+}
+
+Book.propTypes = {
+  book: React.PropTypes.object.isRequired,
+  container: React.PropTypes.object.isRequired,
+  remove: React.PropTypes.func.isRequired
+}
+
+export default Book
diff --git a/app/components/Dashboard/BookList.jsx b/app/components/Dashboard/BookList.jsx
new file mode 100644
index 0000000..7bacc75
--- /dev/null
+++ b/app/components/Dashboard/BookList.jsx
@@ -0,0 +1,44 @@
+import { map, reverse, sortBy } from 'lodash'
+import React from 'react'
+
+import Book from './Book'
+
+class BookList extends React.Component {
+  renderBookList () {
+    const { books, container, remove } = this.props
+    if (!books) return 'Fetching...'
+
+    const items = reverse(sortBy(books, 'created'))
+
+    const bookComponents = map(items, book => {
+      return (
+        <Book
+          book={book}
+          container={container}
+          key={book.id}
+          remove={remove}
+        />
+      )
+    })
+
+    return bookComponents
+  }
+
+  render () {
+    const bookList = this.renderBookList()
+
+    return (
+      <div className='col-lg-12'>
+        { bookList }
+      </div>
+    )
+  }
+}
+
+BookList.propTypes = {
+  books: React.PropTypes.array.isRequired,
+  container: React.PropTypes.object.isRequired,
+  remove: React.PropTypes.func.isRequired
+}
+
+export default BookList
diff --git a/app/components/Dashboard/Dashboard.jsx b/app/components/Dashboard/Dashboard.jsx
new file mode 100644
index 0000000..0d1a1c4
--- /dev/null
+++ b/app/components/Dashboard/Dashboard.jsx
@@ -0,0 +1,97 @@
+import Actions from 'pubsweet-client/src/actions'
+import React from 'react'
+import { bindActionCreators } from 'redux'
+import { connect } from 'react-redux'
+// import { Link } from 'react-router'
+
+import AddBook from './AddBook'
+import BookList from './BookList'
+import DashboardHeader from './DashboardHeader'
+import styles from './dashboard.local.scss'
+
+export class Dashboard extends React.Component {
+  constructor (props) {
+    super(props)
+
+    this.createCollection = this.createCollection.bind(this)
+    this.removeCollection = this.removeCollection.bind(this)
+    this.toggleModal = this.toggleModal.bind(this)
+
+    this.state = {
+      showModal: false
+    }
+  }
+
+  toggleModal () {
+    this.setState({
+      showModal: !this.state.showModal
+    })
+  }
+
+  createCollection (newTitle) {
+    const { createCollection } = this.props.actions
+
+    const collection = {
+      title: newTitle || 'Untitled'
+    }
+
+    createCollection(collection)
+  }
+
+  removeCollection (collection) {
+    const { deleteCollection } = this.props.actions
+    deleteCollection(collection)
+  }
+
+  render () {
+    const { books } = this.props
+    const { showModal } = this.state
+
+    const className = styles.bookList +
+      ' bootstrap pubsweet-component pubsweet-component-scroll'
+
+    return (
+      <div className={className}>
+        <div className='container col-lg-offset-2 col-lg-8'>
+
+          <DashboardHeader toggle={this.toggleModal} />
+
+          <BookList
+            books={books}
+            container={this}
+            remove={this.removeCollection}
+          />
+        </div>
+
+        <AddBook
+          container={this}
+          create={this.createCollection}
+          show={showModal}
+          toggle={this.toggleModal}
+        />
+      </div>
+    )
+  }
+}
+
+Dashboard.propTypes = {
+  books: React.PropTypes.arrayOf(React.PropTypes.object),
+  actions: React.PropTypes.object.isRequired
+}
+
+function mapStateToProps (state, { params }) {
+  return {
+    books: state.collections
+  }
+}
+
+function mapDispatchToProps (dispatch) {
+  return {
+    actions: bindActionCreators(Actions, dispatch)
+  }
+}
+
+export default connect(
+  mapStateToProps,
+  mapDispatchToProps
+)(Dashboard)
diff --git a/app/components/Dashboard/DashboardHeader.jsx b/app/components/Dashboard/DashboardHeader.jsx
new file mode 100644
index 0000000..0ae63e3
--- /dev/null
+++ b/app/components/Dashboard/DashboardHeader.jsx
@@ -0,0 +1,31 @@
+import React from 'react'
+
+import styles from './dashboard.local.scss'
+
+class DashboardHeader extends React.Component {
+  render () {
+    const { toggle } = this.props
+
+    return (
+      <div className='col-lg-12'>
+        <h1 className={styles.bookTitle}>
+          Books
+
+          <div
+            className={styles.addBookBtn}
+            onClick={toggle}
+          >
+            <a>add book</a>
+          </div>
+
+        </h1>
+      </div>
+    )
+  }
+}
+
+DashboardHeader.propTypes = {
+  toggle: React.PropTypes.func.isRequired
+}
+
+export default DashboardHeader
diff --git a/app/components/Dashboard/RemoveBookModal.jsx b/app/components/Dashboard/RemoveBookModal.jsx
new file mode 100644
index 0000000..a1f1e1e
--- /dev/null
+++ b/app/components/Dashboard/RemoveBookModal.jsx
@@ -0,0 +1,46 @@
+import React from 'react'
+
+import AbstractModal from '../common/AbstractModal'
+
+class RemoveBookModal extends React.Component {
+  renderBody () {
+    const { book } = this.props
+
+    return (
+      <span>
+        Are you sure you want to permanently delete { book.title }?
+      </span>
+    )
+  }
+
+  render () {
+    const { container, remove, show, toggle } = this.props
+
+    const title = 'Delete Book'
+    const successText = 'Delete'
+
+    const body = this.renderBody()
+
+    return (
+      <AbstractModal
+        body={body}
+        container={container}
+        show={show}
+        successAction={remove}
+        successText={successText}
+        title={title}
+        toggle={toggle}
+      />
+    )
+  }
+}
+
+RemoveBookModal.propTypes = {
+  book: React.PropTypes.object.isRequired,
+  container: React.PropTypes.object.isRequired,
+  remove: React.PropTypes.func.isRequired,
+  show: React.PropTypes.bool.isRequired,
+  toggle: React.PropTypes.func.isRequired
+}
+
+export default RemoveBookModal
diff --git a/app/components/BookBuilder/styles/bookList.local.scss b/app/components/Dashboard/dashboard.local.scss
similarity index 100%
rename from app/components/BookBuilder/styles/bookList.local.scss
rename to app/components/Dashboard/dashboard.local.scss
diff --git a/app/components/common/AbstractModal.jsx b/app/components/common/AbstractModal.jsx
index 491e07c..2bf9cc2 100644
--- a/app/components/common/AbstractModal.jsx
+++ b/app/components/common/AbstractModal.jsx
@@ -2,6 +2,18 @@ import React from 'react'
 import { Modal } from 'react-bootstrap'
 
 export class BookBuilderModal extends React.Component {
+  constructor (props) {
+    super(props)
+    this.performAction = this.performAction.bind(this)
+  }
+
+  performAction () {
+    const { successAction, toggle } = this.props
+
+    successAction()
+    toggle()
+  }
+
   renderHeader () {
     const { title } = this.props
 
@@ -28,7 +40,7 @@ export class BookBuilderModal extends React.Component {
     const { successAction, successText, toggle } = this.props
 
     const success = successAction ? <a className='modal-button bb-modal-act'
-      onClick={successAction}>
+      onClick={this.performAction}>
       { successText }
     </a> : null
 
diff --git a/app/routes.jsx b/app/routes.jsx
index 6acdba9..48cecfd 100644
--- a/app/routes.jsx
+++ b/app/routes.jsx
@@ -11,7 +11,7 @@ import Blog from 'pubsweet-component-blog/Blog'
 
 // Editoria
 import BookBuilder from './components/BookBuilder/BookBuilder'
-import BookList from './components/BookBuilder/BookList'
+import Dashboard from './components/Dashboard/Dashboard'
 import SimpleEditorWrapper from './components/SimpleEditor/SimpleEditorWrapper'
 
 // Authentication
@@ -38,7 +38,7 @@ export default (
     <Redirect from='/manage/posts' to='books' />
 
     <Route path='/' component={AuthenticatedManage}>
-      <Route path='books' component={BookList} />
+      <Route path='books' component={Dashboard} />
       <Route path='blog' component={Blog} />
       <Route path='books/:id/book-builder' component={BookBuilder} />
       <Route path='books/:bookId/fragments/:fragmentId' component={SimpleEditorWrapper} />
-- 
GitLab