diff --git a/src/actions/DocumentActions.js b/src/actions/DocumentActions.js index 54a93a940..63aea582a 100644 --- a/src/actions/DocumentActions.js +++ b/src/actions/DocumentActions.js @@ -5,6 +5,8 @@ import { createAction } from 'redux-actions'; export const resetDocument = createAction('RESET_DOCUMENT'); +// GET + export const FETCH_DOCUMENT_PENDING = 'FETCH_DOCUMENT_PENDING'; export const FETCH_DOCUMENT_SUCCESS = 'FETCH_DOCUMENT_SUCCESS'; export const FETCH_DOCUMENT_FAILURE = 'FETCH_DOCUMENT_FAILURE'; @@ -29,6 +31,8 @@ export function fetchDocumentAsync(documentId) { }; }; +// POST/UPDATE + export const SAVE_DOCUMENT_PENDING = 'SAVE_DOCUMENT_PENDING'; export const SAVE_DOCUMENT_SUCCESS = 'SAVE_DOCUMENT_SUCCESS'; export const SAVE_DOCUMENT_FAILURE = 'SAVE_DOCUMENT_FAILURE'; @@ -65,3 +69,23 @@ export function saveDocumentAsync(atlasId, documentId, title, text) { }; }; +// documents.delete + +export const deleteDocumentPending = createAction('DELETE_DOCUMENT_PENDING'); +export const deleteDocumentSuccess = createAction('DELETE_DOCUMENT_SUCCESS'); +export const deleteDocumentFailure = createAction('DELETE_DOCUMENT_FAILURE'); + +export const deleteDocument = (documentId, returnPath) => { + return (dispatch) => { + dispatch(deleteDocumentPending()); + + client.post('/documents.delete', { id: documentId }) + .then(data => { + dispatch(deleteDocumentSuccess(documentId)); + dispatch(replace(returnPath)); + }) + .catch((err) => { + dispatch(deleteDocumentFailure(err)); + }) + }; +}; diff --git a/src/components/DropdownMenu/DropdownMenu.js b/src/components/DropdownMenu/DropdownMenu.js new file mode 100644 index 000000000..1f2c1f125 --- /dev/null +++ b/src/components/DropdownMenu/DropdownMenu.js @@ -0,0 +1,66 @@ +import React from 'react'; +import styles from './DropdownMenu.scss'; + +const MenuItem = (props) => { + return ( +
{ props.children }
+ ); +}; + +MenuItem.propTypes = { + onClick: React.PropTypes.func, + children: React.PropTypes.node.isRequired, +}; + +// + +class DropdownMenu extends React.Component { + static propTypes = { + label: React.PropTypes.string.isRequired, + children: React.PropTypes.node.isRequired, + } + + state = { + menuVisible: false, + } + + onMouseEnter = () => { + this.setState({ menuVisible: true }); + } + + onMouseLeave = () => { + this.setState({ menuVisible: false }); + } + + onClick = () => { + this.setState({ menuVisible: !this.state.menuVisible }); + } + + render() { + return ( +
+
+ { this.props.label } +
+ + { this.state.menuVisible ? ( +
+ { this.props.children } +
+ ) : null } +
+ ); + } +}; + +export default DropdownMenu; +export { + MenuItem, +} \ No newline at end of file diff --git a/src/components/DropdownMenu/DropdownMenu.scss b/src/components/DropdownMenu/DropdownMenu.scss new file mode 100644 index 000000000..beda6dc53 --- /dev/null +++ b/src/components/DropdownMenu/DropdownMenu.scss @@ -0,0 +1,40 @@ +@import '../../utils/constants.scss'; + +.label { + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + + min-height: 43px; + padding: 0 0.5rem; + color: $linkColor; +} + +.menuContainer { + position: relative; + + .menu { + position: absolute; + top: 42px; + right: 0; + z-index: 1000; + border: 1px solid #eee; + min-width: 150px; + padding: 5px 0; + } +} + +.menuItem { + margin: 0; + padding: 5px 10px; + display: flex; + justify-content: flex-start; + align-items: center; + cursor: pointer; + + a { + color: $textColor; + text-decoration: none; + } +} \ No newline at end of file diff --git a/src/components/DropdownMenu/index.js b/src/components/DropdownMenu/index.js new file mode 100644 index 000000000..934329f8d --- /dev/null +++ b/src/components/DropdownMenu/index.js @@ -0,0 +1,5 @@ +import DropdownMenu, { MenuItem } from './DropdownMenu'; +export default DropdownMenu; +export { + MenuItem, +}; diff --git a/src/components/Layout/Layout.js b/src/components/Layout/Layout.js index 532e8b80c..5a5ed621b 100644 --- a/src/components/Layout/Layout.js +++ b/src/components/Layout/Layout.js @@ -1,10 +1,13 @@ import React from 'react'; import { connect } from 'react-redux'; import Link from 'react-router/lib/Link'; +import { bindActionCreators } from 'redux'; +import { logoutUser } from 'actions/UserActions'; -import HeaderMenu from './components/HeaderMenu'; +import DropdownMenu, { MenuItem } from 'components/DropdownMenu'; import Flex from 'components/Flex'; import LoadingIndicator from 'components/LoadingIndicator'; +import { Avatar } from 'rebass'; import styles from './Layout.scss'; import classNames from 'classnames/bind'; @@ -18,6 +21,10 @@ class Layout extends React.Component { loading: React.PropTypes.bool, } + onLogout = () => { + this.props.logoutUser(); + } + render() { return (
@@ -35,11 +42,19 @@ class Layout extends React.Component { { this.props.actions } - - - + + + }> + Logout +
+
{ this.props.children }
@@ -55,6 +70,13 @@ const mapStateToProps = (state) => { } }; +const mapDispatchToProps = (dispatch) => { + return bindActionCreators({ + logoutUser, + }, dispatch) +} + export default connect( mapStateToProps, + mapDispatchToProps, )(Layout); \ No newline at end of file diff --git a/src/components/Layout/Layout.scss b/src/components/Layout/Layout.scss index 1ed76ccb4..40c57c913 100644 --- a/src/components/Layout/Layout.scss +++ b/src/components/Layout/Layout.scss @@ -43,7 +43,3 @@ justify-content: center; } -.actions a { - text-decoration: none; - margin-right: 15px; -} diff --git a/src/components/Layout/components/HeaderMenu/HeaderMenu.js b/src/components/Layout/components/HeaderMenu/HeaderMenu.js deleted file mode 100644 index 167170b18..000000000 --- a/src/components/Layout/components/HeaderMenu/HeaderMenu.js +++ /dev/null @@ -1,67 +0,0 @@ -import React from 'react'; -import { connect } from 'react-redux'; -import { logoutUser } from 'actions/UserActions'; - -import styles from './HeaderMenu.scss'; - -class HeaderMenu extends React.Component { - static propTypes = { - children: React.PropTypes.node.isRequired, - } - - state = { - menuVisible: false, - } - - onMouseEnter = () => { - this.setState({ menuVisible: true }); - } - - onMouseLeave = () => { - this.setState({ menuVisible: false }); - } - - onClick = () => { - this.setState({ menuVisible: !this.state.menuVisible }); - } - - logout = (event) => { - event.preventDefault(); - this.props.logout(); - } - - render() { - return ( -
-
- { this.props.children } -
- - { this.state.menuVisible ? ( -
- -
- ) : null } -
- ); - } -}; - -const mapDispatchToProps = (dispatch) => { - return { - logout: () => { - dispatch(logoutUser()) - }, - } -}; - - -export default connect(null, mapDispatchToProps)(HeaderMenu); \ No newline at end of file diff --git a/src/components/Layout/components/HeaderMenu/HeaderMenu.scss b/src/components/Layout/components/HeaderMenu/HeaderMenu.scss deleted file mode 100644 index f910f428c..000000000 --- a/src/components/Layout/components/HeaderMenu/HeaderMenu.scss +++ /dev/null @@ -1,51 +0,0 @@ -@import '../../../../utils/constants.scss'; - -.content { - display: flex; - justify-content: center; - align-items: center; - cursor: pointer; - - min-height: 43px; - min-width: 43px; -} - -.menu { - position: relative; - - img { - height: 24px; - width: 24px; - border-radius: 12px; - } - - .menu { - position: absolute; - top: 42px; - right: 0; - z-index: 1000; - border: 1px solid #eee; - - ul { - margin: 0; - padding: 5px 0; - font-size: 0.9em; - } - - li { - margin: 0; - padding: 0; - list-style-type: none; - - a { - display: flex; - margin: 0; - padding: 5px 10px; - min-width: 150px; - - color: $textColor; - text-decoration: none; - } - } - } -} \ No newline at end of file diff --git a/src/components/Layout/components/HeaderMenu/index.js b/src/components/Layout/components/HeaderMenu/index.js deleted file mode 100644 index cc2de652b..000000000 --- a/src/components/Layout/components/HeaderMenu/index.js +++ /dev/null @@ -1,2 +0,0 @@ -import HeaderMenu from './HeaderMenu'; -export default HeaderMenu; \ No newline at end of file diff --git a/src/components/Layout/index.js b/src/components/Layout/index.js index 82ecfc082..203673809 100644 --- a/src/components/Layout/index.js +++ b/src/components/Layout/index.js @@ -1,8 +1,10 @@ import Layout from './Layout'; import Title from './components/Title'; +import HeaderLink from './components/HeaderLink'; export default Layout; export { Title, + HeaderLink, }; diff --git a/src/scenes/DocumentScene/DocumentScene.js b/src/scenes/DocumentScene/DocumentScene.js index fad80e16a..48360f665 100644 --- a/src/scenes/DocumentScene/DocumentScene.js +++ b/src/scenes/DocumentScene/DocumentScene.js @@ -2,12 +2,16 @@ import React from 'react'; import { connect } from 'react-redux'; import Link from 'react-router/lib/Link'; import { bindActionCreators } from 'redux'; -import { fetchDocumentAsync } from 'actions/DocumentActions'; +import { + fetchDocumentAsync, + deleteDocument, +} from 'actions/DocumentActions'; -import Layout from 'components/Layout'; +import Layout, { HeaderLink } from 'components/Layout'; import AtlasPreviewLoading from 'components/AtlasPreviewLoading'; import CenteredContent from 'components/CenteredContent'; import Document from 'components/Document'; +import DropdownMenu, { MenuItem } from 'components/DropdownMenu'; import styles from './DocumentScene.scss'; @@ -35,12 +39,30 @@ class DocumentScene extends React.Component { } } + onDelete = () => { + if (confirm("Are you sure you want to delete this document?")) { + this.props.deleteDocument( + this.props.document.id, + `/atlas/${ this.props.document.atlas.id }`, + ); + }; + } + render() { const document = this.props.document; let title; let actions; if (document) { - actions = Edit; + actions = ( +
+ + Edit + + + Delete + +
+ ); title = `${document.atlas.name} - ${document.title}`; } @@ -72,6 +94,7 @@ const mapStateToProps = (state) => { const mapDispatchToProps = (dispatch) => { return bindActionCreators({ fetchDocumentAsync, + deleteDocument, }, dispatch) } diff --git a/src/scenes/DocumentScene/DocumentScene.scss b/src/scenes/DocumentScene/DocumentScene.scss index e69de29bb..c7d614611 100644 --- a/src/scenes/DocumentScene/DocumentScene.scss +++ b/src/scenes/DocumentScene/DocumentScene.scss @@ -0,0 +1,4 @@ +.actions { + display: flex; + flex-direction: row; +} \ No newline at end of file