From e70a07d6035d7e99b8bf1cc6f90f546fabdb3eb2 Mon Sep 17 00:00:00 2001
From: john <johnbarlas39@gmail.com>
Date: Tue, 13 Dec 2016 21:42:11 +0200
Subject: [PATCH] chapter refactor - part two

---
 app/components/BookBuilder/Chapter.jsx        |  38 +--
 .../BookBuilder/Chapter/ChapterButtons.jsx    | 173 ++++++++++-
 .../BookBuilder/Chapter/ChapterTitle.jsx      |  22 +-
 .../BookBuilder/Chapter/DeleteModal.jsx       |  41 +++
 .../Chapter/EditingNotification.jsx           | 109 +++++++
 .../BookBuilder/Chapter/FirstRow.jsx          | 273 ++----------------
 ...ProgressIndicator.jsx => ProgressItem.jsx} |  25 +-
 .../BookBuilder/Chapter/ProgressList.jsx      |  62 ++++
 .../BookBuilder/Chapter/SecondRow.jsx         |  62 +---
 app/components/BookBuilder/Chapter/Title.jsx  |  14 -
 .../BookBuilder/Chapter/UnlockModal.jsx       |  42 +++
 .../BookBuilderModal.jsx => utils/Modal.jsx}  |   1 +
 app/components/utils/noop.js                  |   3 +
 13 files changed, 492 insertions(+), 373 deletions(-)
 create mode 100644 app/components/BookBuilder/Chapter/DeleteModal.jsx
 create mode 100644 app/components/BookBuilder/Chapter/EditingNotification.jsx
 rename app/components/BookBuilder/Chapter/{ProgressIndicator.jsx => ProgressItem.jsx} (84%)
 create mode 100644 app/components/BookBuilder/Chapter/ProgressList.jsx
 create mode 100644 app/components/BookBuilder/Chapter/UnlockModal.jsx
 rename app/components/{BookBuilder/BookBuilderModal.jsx => utils/Modal.jsx} (98%)
 create mode 100644 app/components/utils/noop.js

diff --git a/app/components/BookBuilder/Chapter.jsx b/app/components/BookBuilder/Chapter.jsx
index 3cece69..0559817 100644
--- a/app/components/BookBuilder/Chapter.jsx
+++ b/app/components/BookBuilder/Chapter.jsx
@@ -1,14 +1,11 @@
+import { flow } from 'lodash'
 import React from 'react'
 import { DragSource, DropTarget } from 'react-dnd'
 
-import { flow } from 'lodash'
-
 import FirstRow from './Chapter/FirstRow'
-// import SecondRow from './Chapter/SecondRow'
-
-import { chapterSource, chapterTarget, collectDrag, collectDrop, itemTypes } from '../utils/DnD'
-
+import SecondRow from './Chapter/SecondRow'
 import styles from './styles/bookBuilder.local.scss'
+import { chapterSource, chapterTarget, collectDrag, collectDrop, itemTypes } from '../utils/DnD'
 
 export class Chapter extends React.Component {
   constructor (props) {
@@ -21,19 +18,6 @@ export class Chapter extends React.Component {
     }
   }
 
-  // _viewOrEdit () {
-  //   const { roles, chapter } = this.props
-  //
-  //   if (includes(roles, 'production-editor')) return this.setState({ canEdit: true })
-  //
-  //   if (chapter.progress['review'] === 1 && includes(roles, 'author') ||
-  //       chapter.progress['edit'] === 1 && includes(roles, 'copy-editor')) {
-  //     this.setState({ canEdit: true })
-  //   } else {
-  //     this.setState({ canEdit: false })
-  //   }
-  // }
-
   update (changedChapter) {
     const { book, update } = this.props
     update(book, changedChapter)
@@ -45,20 +29,23 @@ export class Chapter extends React.Component {
       chapter,
       connectDragSource,
       connectDropTarget,
-      // ink,
+      ink,
       isDragging,
       outerContainer,
-      // roles,
+      roles,
       title,
       type
     } = this.props
 
-    const opacity = isDragging ? 0 : 1
+    const listItemStyle = {
+      opacity: isDragging ? 0 : 1
+    }
 
     return connectDragSource(connectDropTarget(
       <li
         className={styles.chapterContainer + ' col-lg-12 bb-chapter ' + (chapter.subCategory === 'chapter' ? styles.isChapter : styles.isPart)}
-        style={{ opacity: opacity }}>
+        style={listItemStyle}
+      >
 
         <div className={styles.grabIcon + ' ' + (chapter.division === 'body' ? styles.grabIconBody : '')}>
           <i className='fa fa-circle' />
@@ -72,6 +59,7 @@ export class Chapter extends React.Component {
             book={book}
             chapter={chapter}
             outerContainer={outerContainer}
+            roles={roles}
             title={title}
             type={type}
             update={this.update}
@@ -79,14 +67,14 @@ export class Chapter extends React.Component {
 
           <div className={styles.chapterBottomLine} />
 
-          {/* <SecondRow
+          <SecondRow
             chapter={chapter}
             ink={ink}
             outerContainer={outerContainer}
             roles={roles}
             update={this.update}
             viewOrEdit={this._viewOrEdit}
-          /> */}
+          />
         </div>
 
         <div className={chapter.division === 'body' ? styles.leftBorderBody : styles.leftBorderComponent} />
diff --git a/app/components/BookBuilder/Chapter/ChapterButtons.jsx b/app/components/BookBuilder/Chapter/ChapterButtons.jsx
index d10878d..ff09e1f 100644
--- a/app/components/BookBuilder/Chapter/ChapterButtons.jsx
+++ b/app/components/BookBuilder/Chapter/ChapterButtons.jsx
@@ -1,12 +1,161 @@
-// import React from 'react'
-// import { LinkContainer } from 'react-router-bootstrap'
-//
-// class ChapterButtons extends React.Component {
-//   // constructor (props) {
-//   //   super(props)
-//   // }
-//
-//   render () {
-//
-//   }
-// }
+import { get, includes } from 'lodash'
+import React from 'react'
+import { LinkContainer } from 'react-router-bootstrap'
+
+import DeleteModal from './DeleteModal'
+import EditingNotification from './EditingNotification'
+import styles from './styles/bookBuilder.local.scss'
+
+class ChapterButtons extends React.Component {
+  constructor (props) {
+    super(props)
+
+    this.state = {
+      showDeleteModal: false
+    }
+  }
+
+  // TODO -- should maybe check for lock
+  isLocked () {
+    const { chapter } = this.props
+    return get(chapter, 'lock.editor.username')
+  }
+
+  canEdit () {
+    const { chapter, roles } = this.props
+
+    if (includes(roles, 'admin') || includes(roles, 'production-editor')) {
+      return true
+    }
+
+    if (includes(roles, 'copy-editor')) {
+      const isEditing = (chapter.progress.edit === 1)
+      if (isEditing) return true
+    }
+
+    if (includes(roles, 'author')) {
+      const isReviewing = (chapter.progress.review === 1)
+      if (isReviewing) return true
+    }
+
+    return false
+  }
+
+  toggleDeleteModal () {
+    this.setState({
+      showDeleteModal: !this.state.showDeleteModal
+    })
+  }
+
+  renderEditingNotification () {
+    const { chapter, modalContainer, roles, update } = this.props
+
+    return (
+      <EditingNotification
+        chapter={chapter}
+        modalContainer={modalContainer}
+        roles={roles}
+        update={update}
+      />
+    )
+  }
+
+  renderRenameButton () {
+    const { chapter, isRenaming } = this.props
+    const type = chapter.type
+
+    if (type === 'chapter' || type === 'part') {
+      let renameButtonText = 'Rename'
+      let renameButtonFunction = this.onClickRename
+
+      if (isRenaming) {
+        renameButtonText = 'Save'
+        renameButtonFunction = this.onSaveRename
+      }
+
+      return (
+        <a id='bb-rename'
+          onClick={renameButtonFunction}>
+          { renameButtonText } &nbsp;&nbsp;
+        </a>
+      )
+    }
+
+    return null
+  }
+
+  renderEditButton () {
+    const { bookId, chapter } = this.props
+    const text = this.canEdit() ? 'Edit' : 'View'
+    const url = `/books/${bookId}/fragments/${chapter.id}`
+
+    return (
+      <LinkContainer id='bb-edit' to={url} >
+        <a> { text } &nbsp;&nbsp; </a>
+      </LinkContainer>
+    )
+  }
+
+  renderDeleteButton () {
+    const { chapter, modalContainer, remove } = this.props
+    const { showDeleteModal } = this.state
+    const toggle = this.toggleDeleteModal
+
+    const deleteModal = (
+      <DeleteModal
+        chapter={chapter}
+        container={modalContainer}
+        remove={remove}
+        show={showDeleteModal}
+        toggle={toggle}
+      />
+    )
+
+    return (
+      <a id='bb-delete' onClick={toggle} >
+        Delete
+        { deleteModal }
+      </a>
+    )
+  }
+
+  renderRightArea () {
+    if (this.isLocked()) return this.renderEditingNotification()
+
+    const renameButton = this.renderRenameButton()
+    const editButton = this.renderEditButton()
+    const deleteButton = this.renderDeleteButton()
+
+    return (
+      <div>
+        { renameButton }
+        { editButton }
+        { deleteButton }
+      </div>
+    )
+  }
+
+  render () {
+    let rightArea = this.renderRightArea()
+
+    return (
+      <div className={styles.chapterActions + ' pull-right'}>
+        { rightArea }
+      </div>
+    )
+  }
+}
+
+ChapterButtons.propTypes = {
+  bookId: React.PropTypes.string.isRequired,
+  chapter: React.PropTypes.object.isRequired,
+  isRenaming: React.PropTypes.bool.isRequired,
+  modalContainer: React.PropTypes.object.isRequired,
+  onClickRename: React.PropTypes.func.isRequired,
+  onSaveRename: React.PropTypes.func.isRequired,
+  remove: React.PropTypes.func.isRequired,
+  roles: React.PropTypes.array.isRequired,
+  update: React.PropTypes.func.isRequired
+}
+
+export default ChapterButtons
diff --git a/app/components/BookBuilder/Chapter/ChapterTitle.jsx b/app/components/BookBuilder/Chapter/ChapterTitle.jsx
index 8334f89..652e60f 100644
--- a/app/components/BookBuilder/Chapter/ChapterTitle.jsx
+++ b/app/components/BookBuilder/Chapter/ChapterTitle.jsx
@@ -7,26 +7,18 @@ import Title from './Title'
 import styles from '../styles/bookBuilder.local.scss'
 
 class ChapterTitle extends React.Component {
-  constructor (props) {
-    super(props)
-
-    this.state = {
-      isRenameEmpty: false,
-      isRenamingTitle: false
-    }
-  }
-
   render () {
     const {
       chapter,
       division,
+      isRenaming,
+      isRenameEmpty,
       onClickRename,
       onSaveRename,
       title,
       type,
       update
     } = this.props
-    const { isRenameEmpty, isRenaming } = this.state
 
     let titleArea
 
@@ -52,9 +44,15 @@ class ChapterTitle extends React.Component {
 
     return (
       <div className={styles.chapterTitle}>
+
         { titleArea }
-        <RenameEmptyError isRenameEmpty={isRenameEmpty} />
+
+        <RenameEmptyError
+          isRenameEmpty={isRenameEmpty}
+        />
+
         <div className={styles.separator} />
+
       </div>
     )
   }
@@ -63,6 +61,8 @@ class ChapterTitle extends React.Component {
 ChapterTitle.propTypes = {
   chapter: React.PropTypes.object.isRequired,
   division: React.PropTypes.string.isRequired,
+  isRenaming: React.PropTypes.bool.isRequired,
+  isRenameEmpty: React.PropTypes.bool.isRequired,
   onClickRename: React.PropTypes.func.isRequired,
   onSaveRename: React.PropTypes.func.isRequired,
   title: React.PropTypes.string.isRequired,
diff --git a/app/components/BookBuilder/Chapter/DeleteModal.jsx b/app/components/BookBuilder/Chapter/DeleteModal.jsx
new file mode 100644
index 0000000..5ef2167
--- /dev/null
+++ b/app/components/BookBuilder/Chapter/DeleteModal.jsx
@@ -0,0 +1,41 @@
+import React from 'react'
+
+import Modal from '../../utils/Modal'
+
+class DeleteModal extends React.Component {
+  onDelete () {
+    const { chapter, remove, toggle } = this.props
+
+    remove(chapter)
+    toggle()
+  }
+
+  render () {
+    const { chapter, container, show, toggle } = this.props
+    const type = chapter.type
+
+    return (
+      <Modal
+        title={'Delete ' + type}
+        chapter={chapter}
+        action='delete'
+        successText='Delete'
+        type={type}
+        successAction={this.onDelete}
+        show={show}
+        toggle={toggle}
+        container={container}
+      />
+    )
+  }
+}
+
+DeleteModal.propTypes = {
+  chapter: React.PropTypes.object.isRequired,
+  container: React.PropTypes.object.isRequired,
+  show: React.PropTypes.bool.isRequired,
+  remove: React.PropTypes.func.isRequired,
+  toggle: React.PropTypes.func.isRequired
+}
+
+export default DeleteModal
diff --git a/app/components/BookBuilder/Chapter/EditingNotification.jsx b/app/components/BookBuilder/Chapter/EditingNotification.jsx
new file mode 100644
index 0000000..d020cf7
--- /dev/null
+++ b/app/components/BookBuilder/Chapter/EditingNotification.jsx
@@ -0,0 +1,109 @@
+import { includes } from 'lodash'
+import React from 'react'
+
+import noop from '../../utils/noop'
+import styles from '../styles/bookBuilder.local.scss'
+import UnlockModal from './UnlockModal'
+
+class EditingNotification extends React.Component {
+  constructor (props) {
+    super(props)
+
+    this.state = {
+      showModal: false
+    }
+  }
+
+  toggleModal () {
+    this.setState({
+      showModal: !this.state.showModal
+    })
+  }
+
+  isAdmin () {
+    const { roles } = this.props
+    return includes(roles, 'admin')
+  }
+
+  formatDate (timestamp) {
+    const date = new Date(timestamp)
+
+    const day = date.getDate()
+    const month = date.getMonth() + 1
+    const year = date.getFullYear()
+
+    let hours = date.getHours().toString()
+    if (hours.length === 1) {
+      hours = '0' + hours
+    }
+
+    let minutes = date.getMinutes().toString()
+    if (minutes.length === 1) {
+      minutes = '0' + minutes
+    }
+
+    const theDate = month + '/' + day + '/' + year
+    const theTime = hours + ':' + minutes
+    const formatted = theDate + ' ' + theTime
+
+    return formatted
+  }
+
+  render () {
+    const { chapter, modalContainer, update } = this.props
+    const { showModal } = this.state
+    const username = chapter.lock.editor.username
+
+    let message = username + ' is editing'
+    let hoverTitle, unlockModal
+    let toggle = noop
+
+    if (this.isAdmin()) {
+      toggle = this.toggle()
+
+      unlockModal = (
+        <UnlockModal
+          chapter={chapter}
+          container={modalContainer}
+          show={showModal}
+          toggle={toggle}
+          update={update}
+        />
+      )
+
+      if (chapter.lock.timestamp) {
+        const date = this.formatDate(chapter.lock.timestamp)
+        hoverTitle = username + ' has been editing since ' + date
+      }
+    }
+
+    return (
+      <a id='bb-unlock'
+        className={styles.lEditing}
+        onClick={toggle}
+        title={hoverTitle}
+      >
+        <i
+          className={styles.lockIcon + ' fa fa-lock'}
+          aria-hidden='true'
+          alt='unlock'
+        />
+
+        <span className={styles.lockMessage}>
+          { message }
+        </span>
+
+        { unlockModal }
+      </a>
+    )
+  }
+}
+
+EditingNotification.propTypes = {
+  chapter: React.PropTypes.object.isRequired,
+  modalContainer: React.PropTypes.object.isRequired,
+  roles: React.PropTypes.array.isRequired,
+  update: React.PropTypes.func.isRequired
+}
+
+export default EditingNotification
diff --git a/app/components/BookBuilder/Chapter/FirstRow.jsx b/app/components/BookBuilder/Chapter/FirstRow.jsx
index 17344be..2e08387 100644
--- a/app/components/BookBuilder/Chapter/FirstRow.jsx
+++ b/app/components/BookBuilder/Chapter/FirstRow.jsx
@@ -1,250 +1,45 @@
-import { get, includes, map, slice } from 'lodash'
 import React from 'react'
-import { DropdownButton, MenuItem } from 'react-bootstrap'
-import { LinkContainer } from 'react-router-bootstrap'
-
-import { findDOMNode } from 'react-dom'
-
-import BookBuilderModal from '../BookBuilderModal'
-import TextInput from '../../utils/TextInput'
 
+import ChapterButtons from './ChapterButtons'
 import ChapterTitle from './ChapterTitle'
 
 class ChapterFirstRow extends React.Component {
   constructor (props) {
     super(props)
 
-    this._viewOrEdit = this._viewOrEdit.bind(this)
-
-    this._onClickRename = this._onClickRename.bind(this)
-    this._onSaveRename = this._onSaveRename.bind(this)
-
-    this._onClickDelete = this._onClickDelete.bind(this)
-    this._onClickUnlock = this._onClickUnlock.bind(this)
-    this._toggleDelete = this._toggleDelete.bind(this)
-    this._toggleUnlock = this._toggleUnlock.bind(this)
-
-    this._isAdmin = this._isAdmin.bind(this)
-    this._formatDate = this._formatDate.bind(this)
-
-    this._viewOrEdit = this._viewOrEdit.bind(this)
-
-    // this._onClickTitleDropdown = this._onClickTitleDropdown.bind(this)
-    // this._onClickCustomTitle = this._onClickCustomTitle.bind(this)
+    this.onClickRename = this.onClickRename.bind(this)
+    this.onSaveRename = this.onSaveRename.bind(this)
 
     this.state = {
-      canEdit: true,
       isRenameEmpty: false,
-      isRenamingTitle: false,
-      showDeleteModal: false,
-      showUnlockModal: false
+      isRenamingTitle: false
     }
   }
 
-  _toggleDelete () {
-    this.setState({ showDeleteModal: !this.state.showDeleteModal })
-  }
-
-  _toggleUnlock () {
-    if (!this._isAdmin()) { return }
-    this.setState({ showUnlockModal: !this.state.showUnlockModal })
-  }
-
-  _isAdmin () {
-    const { roles } = this.props
-    return includes(roles, 'admin')
-  }
-
-  _onClickUnlock () {
-    const { book, chapter, update } = this.props
-    const isAdmin = this._isAdmin()
-
-    if (!isAdmin) { return }
-
-    chapter.lock = null
-    update(book, chapter)
-    this._toggleUnlock()
-  }
-
-  _onClickRename () {
+  onClickRename () {
     this.setState({
       isRenamingTitle: true
     })
   }
 
-  _onSaveRename (title) {
-    // save button has been clicked from outside the component
-    if (typeof title !== 'string') {
-      // console.log(this.refs)
-      title = this.refs.chapterInput.state.value
-    }
+  // TODO -- trim title
+  onSaveRename (title) {
+    const { book, chapter, update } = this.props
 
-    if (title.length === 0) {
-      this.setState({
-        isRenameEmpty: true
-      })
-      return
-    }
+    // handle save button click (from outside the component)
+    if (typeof title !== 'string') title = this.refs.chapterInput.state.value
 
-    this.setState({
-      isRenameEmpty: false
-    })
+    if (title.length === 0) return this.setState({ isRenameEmpty: true })
+    this.setState({ isRenameEmpty: false })
 
-    const { book, chapter, update } = this.props
     chapter.title = title
-
     update(book, chapter)
-
-    this.setState({
-      isRenamingTitle: false
-    })
-  }
-
-  _onClickDelete () {
-    const { chapter, remove } = this.props
-    remove(chapter)
-    this._toggleDelete()
-  }
-
-  _viewOrEdit () {
-    const { roles, chapter } = this.props
-
-    if (includes(roles, 'production-editor')) return this.setState({ canEdit: true })
-
-    if (chapter.progress['review'] === 1 && includes(roles, 'author') ||
-        chapter.progress['edit'] === 1 && includes(roles, 'copy-editor')) {
-      this.setState({ canEdit: true })
-    } else {
-      this.setState({ canEdit: false })
-    }
-  }
-
-  _formatDate (timestamp) {
-    const date = new Date(timestamp)
-
-    const day = date.getDate()
-    const month = date.getMonth() + 1
-    const year = date.getFullYear()
-
-    let hours = date.getHours().toString()
-    if (hours.length === 1) {
-      hours = '0' + hours
-    }
-
-    let minutes = date.getMinutes().toString()
-    if (minutes.length === 1) {
-      minutes = '0' + minutes
-    }
-
-    const theDate = month + '/' + day + '/' + year
-    const theTime = hours + ':' + minutes
-    const formatted = theDate + ' ' + theTime
-    return formatted
+    this.setState({ isRenamingTitle: false })
   }
 
   render () {
-    const {
-      book,
-      chapter,
-      outerContainer,
-      title,
-      type,
-      update
-    } = this.props
-
+    const { book, chapter, outerContainer, remove, roles, title, type, update } = this.props
     const { isRenameEmpty, isRenamingTitle } = this.state
-
-    // let titleArea = null
-    // let renameButton = null
-
-    // let renameEmptyError = isRenameEmpty
-    // ? (
-    //   <span className={styles.emptyTitle}>
-    //     New title cannot be empty
-    //   </span>
-    // )
-    // : null
-
-    // if (type === 'chapter' || type === 'part') {
-    //   // if type is chapter, make the title editable text
-    //   let renameButtonText, renameButtonFunction
-    //
-    //   const input = (
-    //     <TextInput
-    //       className='edit'
-    //       ref='chapterInput'
-    //       onSave={this._onSaveRename}
-    //       value={title}
-    //     />
-    //   )
-    //
-    //   if (isRenamingTitle) {
-    //     titleArea = input
-    //     renameButtonText = 'Save'
-    //     renameButtonFunction = this._onSaveRename
-    //   } else {
-    //     titleArea = (<h3 onDoubleClick={this._onClickRename}> { title } </h3>)
-    //     renameButtonText = 'Rename'
-    //     renameButtonFunction = this._onClickRename
-    //   }
-    //
-    //   // add id so that it can be selected for testing
-    //   // could do with refs, but that would mean mounting instead of
-    //   // shallow rendering to access enzyme's refs() api method
-    //   renameButton = (
-    //     <a id='bb-rename'
-    //       onClick={renameButtonFunction}>
-    //       { renameButtonText } &nbsp;&nbsp;
-    //     </a>
-    //   )
-    // }
-
-    // const editOrView = this.state.canEdit ? 'Edit' : 'View'
-
-    // const buttons = (
-    //   <div>
-    //     { renameButton }
-    //     <LinkContainer
-    //       to={`/books/${book.id}/fragments/${chapter.id}`}
-    //       id='bb-edit'
-    //     >
-    //       <a>{ editOrView } &nbsp;&nbsp;</a>
-    //     </LinkContainer>
-    //
-    //     <a id='bb-delete'
-    //       onClick={this._toggleDelete}>
-    //       Delete
-    //     </a>
-    //   </div>
-    // )
-
-    // let editorArea
-    // if (get(chapter, 'lock.editor.username')) {
-    //   let message = ' is editing'
-    //   if (chapter.lock.timestamp && this._isAdmin()) {
-    //     message = ' has been editing since ' + this._formatDate(chapter.lock.timestamp)
-    //   }
-    //
-    //   editorArea = (
-    //     <a id='bb-unlock'
-    //       className={styles.lEditing}
-    //       onClick={this._toggleUnlock}>
-    //
-    //       <i
-    //         className={styles.lockIcon + ' fa fa-lock'}
-    //         aria-hidden='true'
-    //         alt='unlock'
-    //       />
-    //       <span className={styles.lockMessage}>
-    //         { chapter.lock.editor.username + message}
-    //       </span>
-    //
-    //     </a>
-    //   )
-    // }
-
-    // const rightArea = chapter.lock ? editorArea : buttons
-
     const division = chapter.division
 
     return (
@@ -252,41 +47,23 @@ class ChapterFirstRow extends React.Component {
         <ChapterTitle
           chapter={chapter}
           division={division}
-          onClickRename={this._onClickRename}
-          onSaveRename={this._onSaveRename}
+          isRenaming={isRenamingTitle}
+          isRenameEmpty={isRenameEmpty}
+          onClickRename={this.onClickRename}
+          onSaveRename={this.onSaveRename}
           title={title}
           type={type}
           update={update}
         />
 
-        {/* <ChapterButtons /> */}
-
-        {/* <div className={styles.chapterActions + ' pull-right'}>
-          { rightArea }
-        </div> */}
-
-        <BookBuilderModal
-          title={'Delete ' + type}
+        <ChapterButtons
+          bookId={book.id}
           chapter={chapter}
-          action='delete'
-          successText='Delete'
-          type={type}
-          successAction={this._onClickDelete}
-          show={this.state.showDeleteModal}
-          toggle={this._toggleDelete}
-          container={outerContainer}
-        />
-
-        <BookBuilderModal
-          title={'Unlock ' + type}
-          chapter={chapter}
-          action='unlock'
-          successText='Unlock'
-          type={type}
-          successAction={this._onClickUnlock}
-          show={this.state.showUnlockModal}
-          toggle={this._toggleUnlock}
-          container={outerContainer}
+          isRenaming={isRenamingTitle}
+          modalContainer={outerContainer}
+          remove={remove}
+          roles={roles}
+          update={update}
         />
       </span>
     )
diff --git a/app/components/BookBuilder/Chapter/ProgressIndicator.jsx b/app/components/BookBuilder/Chapter/ProgressItem.jsx
similarity index 84%
rename from app/components/BookBuilder/Chapter/ProgressIndicator.jsx
rename to app/components/BookBuilder/Chapter/ProgressItem.jsx
index f5015cf..82b1333 100644
--- a/app/components/BookBuilder/Chapter/ProgressIndicator.jsx
+++ b/app/components/BookBuilder/Chapter/ProgressItem.jsx
@@ -4,15 +4,16 @@ import { Alert } from 'react-bootstrap'
 import BookBuilderModal from '../BookBuilderModal'
 import styles from '../styles/bookBuilder.local.scss'
 
-export class ProgressIndicator extends React.Component {
+export class ProgressItem extends React.Component {
   constructor (props) {
     super(props)
 
-    this._isAllowedToChange = this._isAllowedToChange.bind(this)
+    this.canChange = this.canChange.bind(this)
     this._onClick = this._onClick.bind(this)
     this._toggleModal = this._toggleModal.bind(this)
     this._changeWorkflowState = this._changeWorkflowState.bind(this)
 
+    // TODO -- move to config
     this.progressValues = {
       style: ['To Style', 'Styling', 'Styled'],
       edit: ['To Edit', 'Editing', 'Edited'],
@@ -26,15 +27,21 @@ export class ProgressIndicator extends React.Component {
     }
   }
 
-  _isAllowedToChange () {
+  canChange () {
     const { type, roles, chapter } = this.props
 
-    if (includes(roles, 'production-editor')) return true
+    if (includes(roles, 'admin') || includes(roles, 'production-editor')) return true
 
-    const isOne = (chapter.progress[type] === 1)
+    const isActive = (chapter.progress[type] === 1)
 
-    if (chapter.progress[type] === 1 && type === 'edit' && includes(roles, 'copy-editor')) return true
-    if (isOne && type === 'review' && includes(roles, 'author')) return true
+    if (isActive) {
+      if (type === 'edit') {
+        if (includes(roles, 'copy-editor')) return true
+      }
+      if (type === 'review') {
+        if (includes(roles, 'author')) return true
+      }
+    }
 
     return false
   }
@@ -121,7 +128,7 @@ export class ProgressIndicator extends React.Component {
   }
 }
 
-ProgressIndicator.propTypes = {
+ProgressItem.propTypes = {
   type: React.PropTypes.string.isRequired,
   chapter: React.PropTypes.object.isRequired,
   hasIcon: React.PropTypes.bool,
@@ -131,4 +138,4 @@ ProgressIndicator.propTypes = {
   viewOrEdit: React.PropTypes.func
 }
 
-export default ProgressIndicator
+export default ProgressItem
diff --git a/app/components/BookBuilder/Chapter/ProgressList.jsx b/app/components/BookBuilder/Chapter/ProgressList.jsx
new file mode 100644
index 0000000..163a1b1
--- /dev/null
+++ b/app/components/BookBuilder/Chapter/ProgressList.jsx
@@ -0,0 +1,62 @@
+import React from 'react'
+
+import ProgressItem from './ProgressItem'
+import styles from '../styles/bookBuilder.local.scss'
+
+class ProgressList extends React.Component {
+  render () {
+    const { chapter, roles, modalContainer, update } = this.props
+
+    return (
+      <ul className={styles.secondActions + ' col-lg-7 col-md-12 col-sm-12 col-xs-12'}>
+        <ProgressItem
+          type='style'
+          chapter={chapter}
+          update={update}
+          roles={roles}
+          modalContainer={modalContainer}
+          hasIcon
+          // viewOrEdit={this._viewOrEdit}
+        />
+
+        <ProgressItem
+          type='edit'
+          chapter={chapter}
+          update={update}
+          roles={roles}
+          modalContainer={modalContainer}
+          hasIcon
+          // viewOrEdit={this._viewOrEdit}
+        />
+
+        <ProgressItem
+          type='review'
+          chapter={chapter}
+          update={update}
+          roles={roles}
+          modalContainer={modalContainer}
+          hasIcon
+          // viewOrEdit={this._viewOrEdit}
+        />
+
+        <ProgressItem
+          type='clean'
+          chapter={chapter}
+          roles={roles}
+          modalContainer={modalContainer}
+          update={update}
+          // viewOrEdit={this._viewOrEdit}
+        />
+      </ul>
+    )
+  }
+}
+
+ProgressList.propTypes = {
+  chapter: React.PropTypes.object.isRequired,
+  modalContainer: React.PropTypes.object.isRequired,
+  roles: React.PropTypes.array.isRequired,
+  update: React.PropTypes.func.isRequired
+}
+
+export default ProgressList
diff --git a/app/components/BookBuilder/Chapter/SecondRow.jsx b/app/components/BookBuilder/Chapter/SecondRow.jsx
index 131623d..1e2844c 100644
--- a/app/components/BookBuilder/Chapter/SecondRow.jsx
+++ b/app/components/BookBuilder/Chapter/SecondRow.jsx
@@ -1,19 +1,14 @@
 import React from 'react'
 
 import PagePositionAlignment from './PagePositionAlignment'
-import ProgressIndicator from './ProgressIndicator'
+import ProgressList from './ProgressList'
 import UploadWordButton from './UploadWordBtn'
 
 import styles from '../styles/bookBuilder.local.scss'
 
 class ChapterSecondRow extends React.Component {
   render () {
-    const {
-      chapter,
-      ink,
-      outerContainer,
-      roles
-    } = this.props
+    const { chapter, ink, outerContainer, roles, update } = this.props
 
     return (
       <div className={styles.secondLineContainer}>
@@ -27,46 +22,12 @@ class ChapterSecondRow extends React.Component {
           />
         </div>
 
-        <ul className={styles.secondActions + ' col-lg-7 col-md-12 col-sm-12 col-xs-12'}>
-          <ProgressIndicator
-            type='style'
-            chapter={chapter}
-            update={this.update}
-            roles={roles}
-            outerContainer={outerContainer}
-            hasIcon
-            viewOrEdit={this._viewOrEdit}
-          />
-
-          <ProgressIndicator
-            type='edit'
-            chapter={chapter}
-            update={this.update}
-            roles={roles}
-            outerContainer={outerContainer}
-            hasIcon
-            viewOrEdit={this._viewOrEdit}
-          />
-
-          <ProgressIndicator
-            type='review'
-            chapter={chapter}
-            update={this.update}
-            roles={roles}
-            outerContainer={outerContainer}
-            hasIcon
-            viewOrEdit={this._viewOrEdit}
-          />
-
-          <ProgressIndicator
-            type='clean'
-            chapter={chapter}
-            roles={roles}
-            outerContainer={outerContainer}
-            update={this.update}
-            viewOrEdit={this._viewOrEdit}
-          />
-        </ul>
+        <ProgressList
+          chapter={chapter}
+          modalContainer={outerContainer}
+          roles={roles}
+          update={update}
+        />
 
         <div className={styles.noPadding + ' col-lg-3 col-md-12 col-sm-12 col-xs-12'}>
           <PagePositionAlignment
@@ -82,17 +43,10 @@ class ChapterSecondRow extends React.Component {
 }
 
 ChapterSecondRow.propTypes = {
-  book: React.PropTypes.object.isRequired,
   chapter: React.PropTypes.object.isRequired,
-  connectDragSource: React.PropTypes.func.isRequired,
-  connectDropTarget: React.PropTypes.func.isRequired,
   ink: React.PropTypes.func.isRequired,
-  isDragging: React.PropTypes.bool.isRequired,
   outerContainer: React.PropTypes.object.isRequired,
-  remove: React.PropTypes.func.isRequired,
   roles: React.PropTypes.array,
-  title: React.PropTypes.string.isRequired,
-  type: React.PropTypes.string.isRequired,
   update: React.PropTypes.func.isRequired
 }
 
diff --git a/app/components/BookBuilder/Chapter/Title.jsx b/app/components/BookBuilder/Chapter/Title.jsx
index dac49bc..eb65cd7 100644
--- a/app/components/BookBuilder/Chapter/Title.jsx
+++ b/app/components/BookBuilder/Chapter/Title.jsx
@@ -3,20 +3,6 @@ import React from 'react'
 import TextInput from '../../utils/TextInput'
 
 class Title extends React.Component {
-  // constructor (props) {
-  //   super(props)
-  //
-  //   // this.state = {
-  //   //   isRenaming: false
-  //   // }
-  // }
-
-  // _onClickRename () {
-  //   this.setState({
-  //     isRenaming: true
-  //   })
-  // }
-
   render () {
     const { isRenaming, onClickRename, onSaveRename, title } = this.props
 
diff --git a/app/components/BookBuilder/Chapter/UnlockModal.jsx b/app/components/BookBuilder/Chapter/UnlockModal.jsx
new file mode 100644
index 0000000..4c3ca1e
--- /dev/null
+++ b/app/components/BookBuilder/Chapter/UnlockModal.jsx
@@ -0,0 +1,42 @@
+import React from 'react'
+
+import Modal from '../../utils/Modal'
+
+class UnlockModal extends React.Component {
+  onUnlock () {
+    const { chapter, toggle, update } = this.props
+
+    chapter.lock = null
+    update(chapter)
+    toggle()
+  }
+
+  render () {
+    const { chapter, container, show, toggle } = this.props
+    const type = chapter.type
+
+    return (
+      <Modal
+        title={'Unlock ' + type}
+        chapter={chapter}
+        action='unlock'
+        successText='Unlock'
+        type={type}
+        successAction={this.onUnlock}
+        show={show}
+        toggle={toggle}
+        container={container}
+      />
+    )
+  }
+}
+
+UnlockModal.propTypes = {
+  chapter: React.PropTypes.object.isRequired,
+  container: React.PropTypes.object.isRequired,
+  show: React.PropTypes.bool.isRequired,
+  toggle: React.PropTypes.func.isRequired,
+  update: React.PropTypes.func.isRequired
+}
+
+export default UnlockModal
diff --git a/app/components/BookBuilder/BookBuilderModal.jsx b/app/components/utils/Modal.jsx
similarity index 98%
rename from app/components/BookBuilder/BookBuilderModal.jsx
rename to app/components/utils/Modal.jsx
index 0a2a649..8d052e7 100644
--- a/app/components/BookBuilder/BookBuilderModal.jsx
+++ b/app/components/utils/Modal.jsx
@@ -28,6 +28,7 @@ export class BookBuilderModal extends React.Component {
       { successText }
     </a> : null
 
+    // TODO -- move to config
     if (action === 'delete') {
       modalBodyText = (
         <div>
diff --git a/app/components/utils/noop.js b/app/components/utils/noop.js
new file mode 100644
index 0000000..9b98d9c
--- /dev/null
+++ b/app/components/utils/noop.js
@@ -0,0 +1,3 @@
+const noop = () => {}
+
+export default noop
-- 
GitLab