diff --git a/opentech/apply/funds/views.py b/opentech/apply/funds/views.py
index 611b64d8dbdb713a1b58cbfbea8c4a0b3b0d7263..a2e4499a799630dc41ed2063b8af0db8e560c110 100644
--- a/opentech/apply/funds/views.py
+++ b/opentech/apply/funds/views.py
@@ -303,11 +303,12 @@ class SubmissionsByStatus(BaseAdminSubmissionsTable, DelegateableListView):
 
     def dispatch(self, request, *args, **kwargs):
         self.status = kwargs.get('status')
-        status_data = self.status_mapping[self.status]
+        try:
+            status_data = self.status_mapping[self.status]
+        except KeyError:
+            raise Http404(_("No statuses match the requested value"))
         self.status_name = status_data['name']
         self.statuses = status_data['statuses']
-        if self.status not in self.status_mapping:
-            raise Http404(_("No statuses match the requested value"))
         return super().dispatch(request, *args, **kwargs)
 
     def get_filterset_kwargs(self, filterset_class, **kwargs):
diff --git a/opentech/static_src/src/app/src/components/DetailView/index.js b/opentech/static_src/src/app/src/components/DetailView/index.js
index 76fcd36830e116c12346a82706411b0ee544558a..682d3d42977a19828eb9e2c50c21e9cec049ea8b 100644
--- a/opentech/static_src/src/app/src/components/DetailView/index.js
+++ b/opentech/static_src/src/app/src/components/DetailView/index.js
@@ -1,4 +1,4 @@
-import React, { Component } from 'react'
+import React from 'react'
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import { withWindowSizeListener } from 'react-window-size-listener';
@@ -12,80 +12,77 @@ import FullScreenLoadingPanel from '@components/FullScreenLoadingPanel';
 
 import './style.scss';
 
-class DetailView extends Component {
-    static propTypes = {
-        listing: PropTypes.element.isRequired,
-        showSubmision: PropTypes.bool,
-        windowSize: PropTypes.objectOf(PropTypes.number),
-        clearSubmission: PropTypes.func.isRequired,
-        isLoading: PropTypes.bool,
-        errorMessage: PropTypes.string,
-        isEmpty: PropTypes.bool,
-        isErrored: PropTypes.bool,
-    };
+const DetailView = props => {
+    const isMobile = (width) => (width ? width : props.windowSize.windowWidth) < 1024
 
-    isMobile = (width) => (width ? width : this.props.windowSize.windowWidth) < 1024
+    const renderDisplay = () => <DisplayPanel />
 
-    renderDisplay () {
-        return <DisplayPanel />
-    }
+    const { listing, isLoading, isErrored, isEmpty, showSubmision, errorMessage } = props
 
-    render() {
-        const { listing, isLoading, isErrored, isEmpty, showSubmision, errorMessage } = this.props;
+    if (isErrored) {
+        return (
+            <div className="loading-panel">
+                <h5>Something went wrong!</h5>
+                <p>{errorMessage}</p>
+            </div>
+        )
+    } else if (!isLoading && isEmpty) {
+        return (
+            <div className="loading-panel">
+                <h5>No submissions available</h5>
+            </div>
+        )
+    }
 
-        if (isErrored) {
-            return (
-                <div className="loading-panel">
-                    <h5>Something went wrong!</h5>
-                    <p>{errorMessage}</p>
-                </div>
-            )
-        } else if (!isLoading && isEmpty) {
-            return (
-                <div className="loading-panel">
-                    <h5>No submissions available</h5>
-                </div>
-            )
-        }
+    if (!props.windowSize.windowWidth) {
+        return null
+    }
 
-        if (!this.props.windowSize.windowWidth) {
-            return null
-        }
+    let activeDisplay
 
-        if (this.isMobile()) {
-            var activeDisplay;
-            if (showSubmision) {
-                activeDisplay = (
-                    <SlideInRight key={"display"}>
-                        { this.renderDisplay() }
-                    </SlideInRight>
-                )
-            } else {
-                activeDisplay = (
-                    <SlideOutLeft key={"listing"}>
-                        { React.cloneElement(listing, { shouldSelectFirst: false }) }
-                    </SlideOutLeft>
-                )
-            }
+    if (isMobile()) {
+        if (showSubmision) {
+            activeDisplay = (
+                <SlideInRight key={"display"}>
+                    { renderDisplay() }
+                </SlideInRight>
+            )
         } else {
             activeDisplay = (
-                <>
-                    {listing}
-                    {this.renderDisplay()}
-                </>
+                <SlideOutLeft key={"listing"}>
+                    { React.cloneElement(listing, { shouldSelectFirst: false }) }
+                </SlideOutLeft>
             )
         }
-        return (
+    } else {
+        activeDisplay = (
             <>
-                {isLoading &&
-                    <FullScreenLoadingPanel />
-                }
-                <div className="detail-view">
-                    {activeDisplay}
-                </div>
+                {listing}
+                {renderDisplay()}
             </>
         )
     }
+    return (
+        <>
+            {isLoading &&
+                <FullScreenLoadingPanel />
+            }
+            <div className="detail-view">
+                {activeDisplay}
+            </div>
+        </>
+    )
+}
+
+DetailView.propTypes = {
+    listing: PropTypes.element.isRequired,
+    showSubmision: PropTypes.bool,
+    windowSize: PropTypes.objectOf(PropTypes.number),
+    clearSubmission: PropTypes.func.isRequired,
+    isLoading: PropTypes.bool,
+    errorMessage: PropTypes.string,
+    isEmpty: PropTypes.bool,
+    isErrored: PropTypes.bool,
 }
 
 const mapDispatchToProps = {
diff --git a/opentech/static_src/src/app/src/components/MessageBar/index.js b/opentech/static_src/src/app/src/components/MessageBar/index.js
index e60dfcb47236e8675841e81a9ce834a7de8ca155..53ec08acd79a2f18689ee02d392c462f16cb2954 100644
--- a/opentech/static_src/src/app/src/components/MessageBar/index.js
+++ b/opentech/static_src/src/app/src/components/MessageBar/index.js
@@ -3,14 +3,14 @@ import PropTypes from 'prop-types'
 
 import { MESSAGE_TYPES } from '@actions/messages'
 
-const MessageBar = ({ message, type, onDismiss }) => {
-    const modifierClass = type ? `messages__text--${type}` : '';
 
+const MessageBar = ({ message, type, onDismiss, dismissMessage='OK' }) => {
+    const modifierClass = type ? `messages__text--${type}` : '';
     return (
         <li className={`messages__text ${modifierClass}`}>
             <div className="messages__inner">
                 <p className="messages__copy">{message}</p>
-                {onDismiss && <button className="button messages__button" onClick={onDismiss}>Ok</button>}
+                {onDismiss && <button className="button messages__button" onClick={onDismiss}>{dismissMessage}</button>}
             </div>
         </li>
     )
@@ -20,6 +20,7 @@ MessageBar.propTypes = {
     type: PropTypes.oneOf(Object.values(MESSAGE_TYPES)),
     message: PropTypes.string,
     onDismiss: PropTypes.func,
+    dismissMessage: PropTypes.string,
 }
 
 export default MessageBar
diff --git a/opentech/static_src/src/app/src/components/MessagesList/index.js b/opentech/static_src/src/app/src/components/MessagesList/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..ddad485fe6a98041844e07b7604ff1e91be1745d
--- /dev/null
+++ b/opentech/static_src/src/app/src/components/MessagesList/index.js
@@ -0,0 +1,18 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+
+import MessageBar from '@components/MessageBar'
+
+const MessagesList = ({ children }) => {
+    return (
+        <ul className="messages">
+            { children }
+        </ul>
+    )
+}
+
+MessagesList.propTypes = {
+    children: PropTypes.oneOfType([PropTypes.arrayOf(MessageBar), MessageBar])
+}
+
+export default MessagesList
diff --git a/opentech/static_src/src/app/src/components/SubmissionDisplay/answers.js b/opentech/static_src/src/app/src/components/SubmissionDisplay/answers.js
index a3a79823d4ccd89b1021c3b8d7044d361249422e..bee7f2646bd304414b2942928b59a505e7f827c5 100644
--- a/opentech/static_src/src/app/src/components/SubmissionDisplay/answers.js
+++ b/opentech/static_src/src/app/src/components/SubmissionDisplay/answers.js
@@ -80,6 +80,7 @@ const answerTypes = {
     'rich_text': RichTextAnswer,
     'address': AddressAnswer,
     'category': BasicListAnswer,
+
     // Files
     'file': FileAnswer,
     'multi_file': MultiFileAnswer,
diff --git a/opentech/static_src/src/app/src/containers/ByRoundListing.js b/opentech/static_src/src/app/src/containers/ByRoundListing.js
index 8d0a298bab09280c0fdd6f6e55501639665d488c..e9aaebf1a01c572f974340cda1a4852998c6a425 100644
--- a/opentech/static_src/src/app/src/containers/ByRoundListing.js
+++ b/opentech/static_src/src/app/src/containers/ByRoundListing.js
@@ -60,7 +60,8 @@ class ByRoundListing extends React.Component {
         const { isLoading, rounds, submissions } = this.props;
         if (isLoading)
             return []
-        return submissions.map(submission => submission.round)
+        return submissions.sort((a, b) => a.id - b.id )
+                          .map(submission => submission.round)
                           .filter((round, index, arr) => arr.indexOf(round) === index)
                           .map((round, i) => ({
                               display: rounds[parseInt(round)].title,
diff --git a/opentech/static_src/src/app/src/containers/ByStatusListing.js b/opentech/static_src/src/app/src/containers/ByStatusListing.js
index 99c565d6fbf279cc5e85ea84a652bc09e44d1269..3f2b7f683dc2703b7138ec186f954f940dfab6c0 100644
--- a/opentech/static_src/src/app/src/containers/ByStatusListing.js
+++ b/opentech/static_src/src/app/src/containers/ByStatusListing.js
@@ -57,7 +57,7 @@ class ByStatusListing extends React.Component {
         const slugify = value => value.toLowerCase().replace(/\s/g, '-')
         const workflow = round.workflow
         const order = workflow.reduce((accumulator, {display, value}, idx) => {
-            const key = slugify(value);
+            const key = slugify(display);
             const existing = accumulator[key] || {}
             const existingValues = existing.values || []
             const position = existing.position || idx
diff --git a/opentech/static_src/src/app/src/containers/CurrentSubmissionDisplay.js b/opentech/static_src/src/app/src/containers/CurrentSubmissionDisplay.js
index 4be90e1365997a9df7a230e1196116586a74b209..246514a8125164534e93b2c491f715c68028923a 100644
--- a/opentech/static_src/src/app/src/containers/CurrentSubmissionDisplay.js
+++ b/opentech/static_src/src/app/src/containers/CurrentSubmissionDisplay.js
@@ -1,6 +1,7 @@
-import React from 'react';
+import React, { useEffect, useState } from 'react'
 import PropTypes from 'prop-types';
 import { connect } from 'react-redux'
+import useInterval from '@rooks/use-interval'
 
 import { loadCurrentSubmission } from '@actions/submissions'
 import {
@@ -8,36 +9,89 @@ import {
     getCurrentSubmissionID,
 } from '@selectors/submissions'
 import SubmissionDisplay from '@components/SubmissionDisplay';
+import MessagesList from '@components/MessagesList'
+import MessageBar from '@components/MessageBar'
+
 
 const loadData = props => {
-    props.loadCurrentSubmission(['questions'])
+    return props.loadCurrentSubmission(['questions'], { bypassCache: true })
+}
 
+const hasChanged = (prevSubmission, submission, keys) => {
+    return keys.some(key => {
+        return JSON.stringify(prevSubmission[key]) !== JSON.stringify(submission[key])
+    })
 }
 
-class CurrentSubmissionDisplay extends React.Component {
-    static propTypes = {
-        submission: PropTypes.object,
-        submissionID: PropTypes.number,
-    }
+const hasContentUpdated = (prevSubmission, submission) => {
+    return hasChanged(prevSubmission, submission, ['metaQuestions', 'questions'])
 
-    componentDidMount() {
-        loadData(this.props)
-    }
+}
+
+
+const  CurrentSubmissionDisplay = props => {
+    const { submission, submissionID } = props
+
+    const { start, stop } = useInterval(() => loadData(props), 30000)
+
+    const [localSubmission, setSubmission] = useState(undefined);
+    const [updated, setUpdated] = useState(false);
+    const [updateMessage, setUpdateMessage] = useState('')
+
+    // Load newly selected submission.
+    useEffect(() => {
+        setUpdated(false)
+        loadData(props)
+        start()
+        return () => stop()
+    }, [submissionID])
 
-    componentDidUpdate(prevProps) {
-        if (this.props.submissionID !== prevProps.submissionID ) {
-            loadData(this.props)
+    // Determine if the submission has been updated by someone else.
+    useEffect(() => {
+        if (!submission || !submission.questions || submission.isFetching) {
+            return;
         }
+
+        if (!localSubmission || localSubmission.id !== submissionID) {
+            setSubmission(submission)
+        } else if (hasContentUpdated(localSubmission, submission)) {
+            setUpdated(true)
+            setUpdateMessage('The contents of this application have been changed by someone else.')
+        }
+    }, [submission])
+
+    const handleUpdateSubmission = () => {
+        setSubmission(submission)
+        setUpdated(false)
+    }
+
+    if ( !localSubmission ) {
+        return <p>Loading</p>
     }
 
-    render () {
-        const { submission } = this.props
-        return <SubmissionDisplay
-                    submission={submission}
-                    isLoading={!submission || submission.isFetching}
-                    isError={submission && submission.isErrored} />
+    const renderUpdatedMessage = () =>{
+        return <MessagesList>
+            <MessageBar
+                    type='info'
+                    message={updateMessage}
+                    onDismiss={handleUpdateSubmission}
+                    dismissMessage="Show Updated"
+               />
+        </MessagesList>
     }
 
+    return <>
+        {updated && renderUpdatedMessage()}
+        <SubmissionDisplay
+                submission={localSubmission}
+                isLoading={!localSubmission || localSubmission.isFetching}
+                isError={localSubmission && localSubmission.isErrored} />
+    </>
+}
+
+CurrentSubmissionDisplay.propTypes = {
+    submission: PropTypes.object,
+    submissionID: PropTypes.number,
 }
 
 const mapStateToProps = state => ({
@@ -45,5 +99,9 @@ const mapStateToProps = state => ({
     submission: getCurrentSubmission(state),
 })
 
+const mapDispatchToProps = dispatch => ({
+    loadCurrentSubmission: (fields, options) => dispatch(loadCurrentSubmission(fields, options))
+})
+
 
-export default connect(mapStateToProps, {loadCurrentSubmission})(CurrentSubmissionDisplay)
+export default connect(mapStateToProps, mapDispatchToProps)(CurrentSubmissionDisplay)
diff --git a/opentech/static_src/src/app/src/containers/DisplayPanel/index.js b/opentech/static_src/src/app/src/containers/DisplayPanel/index.js
index 291aaff84d61973de9509cfdf1b515129447698f..cbcb98f73d0d9ba40273568dd5235a4093b01dfc 100644
--- a/opentech/static_src/src/app/src/containers/DisplayPanel/index.js
+++ b/opentech/static_src/src/app/src/containers/DisplayPanel/index.js
@@ -1,89 +1,126 @@
-import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
-import { withWindowSizeListener } from 'react-window-size-listener';
+import React, { useEffect, useState } from 'react'
+import { connect } from 'react-redux'
+import PropTypes from 'prop-types'
+import { withWindowSizeListener } from 'react-window-size-listener'
 
-import { clearCurrentSubmission } from '@actions/submissions';
+import { MESSAGE_TYPES, addMessage } from '@actions/messages'
+import { clearCurrentSubmission } from '@actions/submissions'
 import {
     getCurrentSubmission,
     getCurrentSubmissionID,
-} from '@selectors/submissions';
+} from '@selectors/submissions'
 
 import CurrentSubmissionDisplay from '@containers/CurrentSubmissionDisplay'
 import ReviewInformation from '@containers/ReviewInformation'
-import AddNoteForm from '@containers/AddNoteForm';
-import NoteListing from '@containers/NoteListing';
-import StatusActions from '@containers/StatusActions';
+import AddNoteForm from '@containers/AddNoteForm'
+import NoteListing from '@containers/NoteListing'
+import StatusActions from '@containers/StatusActions'
 import Tabber, {Tab} from '@components/Tabber'
 import SubmissionLink from '@components/SubmissionLink';
-import './style.scss';
-
-class DisplayPanel extends React.Component  {
-    static propTypes = {
-        submissionID: PropTypes.number,
-        loadSubmission: PropTypes.func,
-        clearSubmission: PropTypes.func.isRequired,
-        windowSize: PropTypes.objectOf(PropTypes.number)
-    };
-
-    render() {
-        const { windowSize: { windowWidth: width }, submissionID, clearSubmission } = this.props;
-        const isMobile = width < 1024;
-
-        const submission = <CurrentSubmissionDisplay />
-
-        let tabs = [
-            <Tab button="Notes" key="note">
-                <NoteListing submissionID={submissionID} />
-                <AddNoteForm submissionID={submissionID} />
-            </Tab>,
-            <Tab button="Status" key="status">
-                <StatusActions submissionID={submissionID} />
-                <ReviewInformation submissionID={submissionID} />
-                <SubmissionLink submissionID={submissionID} />
-            </Tab>
-        ]
 
-        if ( isMobile ) {
-            tabs = [
-                <Tab button="Back" key="back" handleClick={ clearSubmission } />,
-                <Tab button="Application" key="application">
-                    { submission }
-                </Tab>,
-                ...tabs
-            ]
+import './style.scss'
+
+
+const DisplayPanel = props => {
+    const { submissionID, submission, addMessage} = props
+    const [ currentStatus, setCurrentStatus ] = useState(undefined)
+    const [ localSubmissionID, setLocalSubmissionID ] = useState(submissionID)
+
+    useEffect(() => {
+        setCurrentStatus(undefined)
+        setLocalSubmissionID(submissionID)
+    }, [submissionID])
+
+    useEffect(() => {
+        if (localSubmissionID !== submissionID) {
+            return
         }
 
-        return (
-            <div className="display-panel">
-                { !isMobile && (
-                    <div className="display-panel__column">
-                        <div className="display-panel__header display-panel__header--spacer"></div>
-                        <div className="display-panel__body display-panel__body--center">
-                            { submission }
-                        </div>
-                    </div>
-                )}
+        if (!submission || !submission.status) {
+            setCurrentStatus(undefined)
+            return
+        }
+
+        const { status, changedLocally } = submission
+
+        if (currentStatus && status !== currentStatus && !changedLocally) {
+            addMessage(
+                'The status of this application has changed by another user.',
+                MESSAGE_TYPES.INFO
+            )
+        }
+
+        setCurrentStatus(status)
+    })
+
+    const { windowSize: {windowWidth: width}  } = props;
+    const { clearSubmission } = props;
+
+    const isMobile = width < 1024;
+    const submissionLink = "/apply/submissions/" + submissionID + "/";
+
+    let tabs = [
+        <Tab button="Notes" key="note">
+            <NoteListing submissionID={submissionID} />
+            <AddNoteForm submissionID={submissionID} />
+        </Tab>,
+        <Tab button="Status" key="status">
+            <StatusActions submissionID={submissionID} />
+            <ReviewInformation submissionID={submissionID} />
+            <SubmissionLink submissionID={submissionID} />
+        </Tab>
+    ]
+
+    if ( isMobile ) {
+        tabs = [
+            <Tab button="Back" key="back" handleClick={ clearSubmission } />,
+            <Tab button="Application" key="application">
+                <CurrentSubmissionDisplay />
+            </Tab>,
+            ...tabs
+        ]
+    }
+
+    return (
+        <div className="display-panel">
+            { !isMobile && (
                 <div className="display-panel__column">
-                    <div className="display-panel__body">
-                        <Tabber>
-                            { tabs }
-                        </Tabber>
+                    <div className="display-panel__header display-panel__header--spacer"></div>
+                    <div className="display-panel__body display-panel__body--center">
+                        <a target="_blank" rel="noopener noreferrer" href={ submissionLink }>Open in new tab</a>
+                        <CurrentSubmissionDisplay />
                     </div>
                 </div>
+            )}
+            <div className="display-panel__column">
+                <div className="display-panel__body">
+                    <Tabber>
+                        { tabs }
+                    </Tabber>
+                </div>
             </div>
+        </div>
 
-        )
-    }
+    )
+}
+
+DisplayPanel.propTypes = {
+    submission: PropTypes.object,
+    submissionID: PropTypes.number,
+    loadSubmission: PropTypes.func,
+    clearSubmission: PropTypes.func.isRequired,
+    windowSize: PropTypes.objectOf(PropTypes.number),
+    addMessage: PropTypes.func,
 }
 
 const mapStateToProps = state => ({
     submissionID: getCurrentSubmissionID(state),
     submission: getCurrentSubmission(state),
-});
+})
 
 const mapDispatchToProps = {
-    clearSubmission: clearCurrentSubmission
+    clearSubmission: clearCurrentSubmission,
+    addMessage: addMessage,
 }
 
 export default connect(mapStateToProps, mapDispatchToProps)(withWindowSizeListener(DisplayPanel));
diff --git a/opentech/static_src/src/app/src/containers/GroupByRoundDetailView.js b/opentech/static_src/src/app/src/containers/GroupByRoundDetailView.js
index a3a75a0798ac192f13c7aa61d5ae73d2b1092ded..a5492c4afdc14f771b14db0b0a500ea8ab6151ea 100644
--- a/opentech/static_src/src/app/src/containers/GroupByRoundDetailView.js
+++ b/opentech/static_src/src/app/src/containers/GroupByRoundDetailView.js
@@ -1,13 +1,13 @@
-import React from 'react';
-import PropTypes from 'prop-types';
+import React from 'react'
+import PropTypes from 'prop-types'
 import { connect } from 'react-redux'
 
-import DetailView from '@components/DetailView';
-import ByRoundListing from '@containers/ByRoundListing';
+import DetailView from '@components/DetailView'
+import ByRoundListing from '@containers/ByRoundListing'
 import {
     getRoundsFetching,
     getRoundsErrored,
-} from '@selectors/rounds';
+} from '@selectors/rounds'
 import {
     getCurrentStatusesSubmissions,
     getCurrentSubmissionID,
@@ -17,32 +17,31 @@ import {
     getByStatusesError,
 } from '@selectors/statuses';
 
+const GroupByRoundDetailView = props => {
+    const listing = <ByRoundListing submissionStatuses={props.submissionStatuses} />
+    const { isLoading, isErrored, submissions, submissionID, errorMessage } = props
+    const isEmpty = submissions.length === 0
+    const activeSubmision = !!submissionID
 
-class GroupByRoundDetailView extends React.Component {
-    static propTypes = {
-        submissions: PropTypes.arrayOf(PropTypes.object),
-        submissionID: PropTypes.number,
-        isLoading: PropTypes.bool,
-        isErrored: PropTypes.bool,
-        errorMessage: PropTypes.string,
-    };
-
-    render() {
-        const { isLoading, isErrored, submissions, submissionID, errorMessage } = this.props;
-        const isEmpty = submissions.length === 0;
-        const activeSubmision = !!submissionID;
+    return (
+        <DetailView
+            isEmpty={isEmpty}
+            listing={listing}
+            isLoading={isLoading}
+            showSubmision={activeSubmision}
+            isErrored={isErrored}
+            errorMessage={errorMessage}
+        />
+    )
+}
 
-        return (
-            <DetailView
-                isEmpty={isEmpty}
-                listing={<ByRoundListing />}
-                isLoading={isLoading}
-                showSubmision={activeSubmision}
-                isErrored={isErrored}
-                errorMessage={errorMessage || "Something went wrong"}
-            />
-        );
-    }
+GroupByRoundDetailView.propTypes = {
+    submissionStatuses: PropTypes.arrayOf(PropTypes.string),
+    submissions: PropTypes.arrayOf(PropTypes.object),
+    submissionID: PropTypes.number,
+    isLoading: PropTypes.bool,
+    isErrored: PropTypes.bool,
+    errorMessage: PropTypes.string,
 }
 
 const mapStateToProps = (state, ownProps) => ({
@@ -54,6 +53,5 @@ const mapStateToProps = (state, ownProps) => ({
     submissionID: getCurrentSubmissionID(state),
 })
 
-export default connect(
-    mapStateToProps,
-)(GroupByRoundDetailView);
+
+export default connect(mapStateToProps)(GroupByRoundDetailView)
diff --git a/opentech/static_src/src/app/src/containers/GroupByStatusDetailView.js b/opentech/static_src/src/app/src/containers/GroupByStatusDetailView.js
index e28f0420ea45cf07dd23126f38606a38246116fe..0b1be3df2e6b5de57aafed9166813b1d605be276 100644
--- a/opentech/static_src/src/app/src/containers/GroupByStatusDetailView.js
+++ b/opentech/static_src/src/app/src/containers/GroupByStatusDetailView.js
@@ -1,13 +1,13 @@
-import React from 'react';
-import PropTypes from 'prop-types';
+import React from 'react'
+import PropTypes from 'prop-types'
 import { connect } from 'react-redux'
 
-import DetailView from '@components/DetailView';
-import ByStatusListing from '@containers/ByStatusListing';
-
+import DetailView from '@components/DetailView'
+import ByStatusListing from '@containers/ByStatusListing'
 import {
     getSubmissionsByRoundError,
     getCurrentRoundSubmissions,
+    getCurrentSubmission,
     getCurrentSubmissionID,
     getSubmissionErrorState,
 } from '@selectors/submissions';
@@ -16,33 +16,33 @@ import {
 } from '@selectors/rounds';
 
 
-class GroupByStatusDetailView extends React.Component {
-    static propTypes = {
-        submissions: PropTypes.arrayOf(PropTypes.object),
-        submissionID: PropTypes.number,
-        round: PropTypes.object,
-        isErrored: PropTypes.bool,
-        errorMessage: PropTypes.string,
-    };
-
-    render() {
-        const listing = <ByStatusListing />;
-        const { round, isErrored, submissions, submissionID, errorMessage } = this.props;
-        const isLoading = !round || (round && (round.isFetching || round.submissions.isFetching))
-        const isEmpty = submissions.length === 0;
-        const activeSubmision = !!submissionID;
-
-        return (
-            <DetailView
-                isErrored={isErrored}
-                listing={listing}
-                isEmpty={isEmpty}
-                isLoading={isLoading}
-                showSubmision={activeSubmision}
-                errorMessage={errorMessage || 'Fetching failed.'}
-            />
-        );
-    }
+const GroupByStatusDetailView = ({ currentSubmission, round, isErrored, submissions, submissionID, errorMessage }) => {
+    const listing = <ByStatusListing />
+    const isLoading = !round || (round && (round.isFetching || round.submissions.isFetching))
+    const isEmpty = submissions.length === 0
+    const activeSubmision = !!submissionID
+
+    return (
+        <DetailView
+            isErrored={isErrored}
+            listing={listing}
+            isEmpty={isEmpty}
+            isLoading={isLoading}
+            showSubmision={activeSubmision}
+            errorMessage={errorMessage || 'Fetching failed.'}
+        />
+    );
+}
+
+GroupByStatusDetailView.propTypes = {
+    submissions: PropTypes.arrayOf(PropTypes.object),
+    submissionID: PropTypes.number,
+    round: PropTypes.object,
+    isErrored: PropTypes.bool,
+    errorMessage: PropTypes.string,
+    currentSubmission: PropTypes.shape({
+        status: PropTypes.string
+    }),
 }
 
 const mapStateToProps = state => ({
@@ -50,9 +50,11 @@ const mapStateToProps = state => ({
     isErrored: getSubmissionErrorState(state),
     errorMessage: getSubmissionsByRoundError(state),
     submissions: getCurrentRoundSubmissions(state),
+    currentSubmission: getCurrentSubmission(state),
     submissionID: getCurrentSubmissionID(state),
 })
 
-export default connect(
-    mapStateToProps,
-)(GroupByStatusDetailView);
+
+export default connect(mapStateToProps)(
+    GroupByStatusDetailView
+)
diff --git a/opentech/static_src/src/app/src/containers/MessagesContainer.js b/opentech/static_src/src/app/src/containers/MessagesContainer.js
index 535a43a359f2ca80e485690d6133bef7dce3a1d3..1ff163bb146116692b0b51683ecc924f6d8068c3 100644
--- a/opentech/static_src/src/app/src/containers/MessagesContainer.js
+++ b/opentech/static_src/src/app/src/containers/MessagesContainer.js
@@ -3,17 +3,18 @@ import { connect } from 'react-redux'
 import PropTypes from 'prop-types'
 
 import MessageBar from '@components/MessageBar'
+import MessagesList from '@components/MessagesList'
 import { getMessages } from '@selectors/messages'
 import { dismissMessage } from '@actions/messages'
 
 const MessagesContainer = ({ messages, dismiss }) => {
     return (
-        <ul className="messages">
+        <MessagesList>
             {Object.values(messages).map(({ message, type, id}) =>
                 <MessageBar key={id} message={message} type={type}
                     onDismiss={() => dismiss(id)} />
             )}
-        </ul>
+        </MessagesList>
     )
 }
 
diff --git a/opentech/static_src/src/app/src/redux/actions/submissions.js b/opentech/static_src/src/app/src/redux/actions/submissions.js
index 005860211cda3e7a5e4b41ff3949f2d711410468..6ba68106ae1e4bfdc73ab228d762e359b6d56a0a 100644
--- a/opentech/static_src/src/app/src/redux/actions/submissions.js
+++ b/opentech/static_src/src/app/src/redux/actions/submissions.js
@@ -246,14 +246,14 @@ const fetchSubmission = (submissionID) => ({
     submissionID,
 })
 
-export const loadCurrentSubmission = (requiredFields=[]) => (dispatch, getState) => {
+export const loadCurrentSubmission = (requiredFields=[], { bypassCache = false }) => (dispatch, getState) => {
     const submissionID = getCurrentSubmissionID(getState())
     if ( !submissionID ) {
         return null
     }
     const submission = getCurrentSubmission(getState())
 
-    if (submission && requiredFields.every(key => submission.hasOwnProperty(key))) {
+    if (!bypassCache && submission && requiredFields.every(key => submission.hasOwnProperty(key))) {
         return null
     }
 
@@ -281,4 +281,5 @@ export const executeSubmissionAction = (submissionID, action) => ({
         endpoint: api.executeSubmissionAction(submissionID, action),
     },
     submissionID,
+    changedLocally: true,
 })
diff --git a/opentech/static_src/src/app/src/redux/reducers/statuses.js b/opentech/static_src/src/app/src/redux/reducers/statuses.js
index b0f914d7d054064b4ad5887fd6aed17f487546e6..107f40721ed14563c23f100a83f19e0a9bb989e9 100644
--- a/opentech/static_src/src/app/src/redux/reducers/statuses.js
+++ b/opentech/static_src/src/app/src/redux/reducers/statuses.js
@@ -5,6 +5,7 @@ import {
     UPDATE_BY_STATUSES,
     START_LOADING_BY_STATUSES,
     FAIL_LOADING_BY_STATUSES,
+    UPDATE_SUBMISSION,
 } from '@actions/submissions';
 
 
@@ -30,6 +31,16 @@ function submissionsByStatuses(state = {}, action) {
                     return accumulator
                 }, state)
             };
+        case UPDATE_SUBMISSION:
+            state = Object.entries(state).reduce(
+                (accumulator, [status, ids]) => {
+                    accumulator[status] = ids.filter(id => id !== action.data.id);
+                    return accumulator;
+                }, {});
+            return {
+                ...state,
+                [action.data.status]: [...(state[action.data.status] || []), action.data.id],
+            };
         default:
             return state
     }
diff --git a/opentech/static_src/src/app/src/redux/reducers/submissions.js b/opentech/static_src/src/app/src/redux/reducers/submissions.js
index 08aab44c57d3e39d366ad80f3d320ecb44b06b44..529239aa11da8aea2572ba8259a315f0b3dbb63f 100644
--- a/opentech/static_src/src/app/src/redux/reducers/submissions.js
+++ b/opentech/static_src/src/app/src/redux/reducers/submissions.js
@@ -38,6 +38,7 @@ function submission(state={comments: []}, action) {
                 isExecutingAction: false,
                 isExecutingActionErrored: false,
                 executionActionError: undefined,
+                changedLocally: action.changedLocally === true
             };
         case UPDATE_NOTES:
             return {
diff --git a/package-lock.json b/package-lock.json
index 7d30996318d42ab39c0e257f4a7244b65c830b46..7d8bc54bfc3c5cee1c388a6f283f857c50034b95 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3073,8 +3073,7 @@
         "detect-node": {
             "version": "2.0.4",
             "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz",
-            "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==",
-            "dev": true
+            "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw=="
         },
         "diffie-hellman": {
             "version": "5.0.3",
@@ -6768,6 +6767,11 @@
             "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
             "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
         },
+        "lodash.isequal": {
+            "version": "4.5.0",
+            "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+            "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
+        },
         "lodash.kebabcase": {
             "version": "4.1.1",
             "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz",
@@ -6785,6 +6789,11 @@
             "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz",
             "integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ=="
         },
+        "lodash.pick": {
+            "version": "4.4.0",
+            "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
+            "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM="
+        },
         "lodash.some": {
             "version": "4.6.0",
             "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz",
diff --git a/package.json b/package.json
index afd1acccf7253bed76bf2568c69a64d8bfd10a22..2a88da568a139251abb5a585cb143bd0c0006ee5 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
         "@svgr/webpack": "^4.1.0",
         "connected-react-router": "^6.3.1",
         "del": "^3.0.0",
+        "detect-node": "^2.0.4",
         "gulp": "^4.0.0",
         "gulp-babel": "^8.0.0",
         "gulp-clean-css": "^3.10.0",
@@ -27,6 +28,8 @@
         "gulp-uglify": "^3.0.1",
         "humps": "^2.0.1",
         "js-cookie": "^2.2.0",
+        "lodash.isequal": "^4.5.0",
+        "lodash.pick": "^4.4.0",
         "node-sass-import-once": "^1.2.0",
         "prop-types": "^15.6.2",
         "react": "^16.8.1",