From 4fbe998cfe69b3c78f16b250a4f2b7191a9f377d Mon Sep 17 00:00:00 2001
From: Tomasz Knapik <hi@tmkn.org>
Date: Tue, 15 Jan 2019 14:40:54 +0000
Subject: [PATCH] Refactor listing

---
 .../src/app/src/components/Listing/index.js   | 67 +++++++++++--------
 .../src/app/src/components/ListingGroup.js    | 21 ++++++
 .../src/app/src/containers/ByStatusListing.js | 31 +++++----
 .../src/app/src/redux/reducers/submissions.js |  2 +-
 .../app/src/redux/selectors/submissions.js    | 23 -------
 5 files changed, 77 insertions(+), 67 deletions(-)
 create mode 100644 opentech/static_src/src/app/src/components/ListingGroup.js

diff --git a/opentech/static_src/src/app/src/components/Listing/index.js b/opentech/static_src/src/app/src/components/Listing/index.js
index b04e54070..421625e1f 100644
--- a/opentech/static_src/src/app/src/components/Listing/index.js
+++ b/opentech/static_src/src/app/src/components/Listing/index.js
@@ -2,40 +2,54 @@ import React from 'react';
 import PropTypes from 'prop-types';
 
 import ListingHeading from '@components/ListingHeading';
+import ListingGroup from '@components/ListingGroup';
 import ListingItem from '@components/ListingItem';
 
 import './style.scss';
 
 export default class Listing extends React.Component {
     renderListItems() {
-        if (this.props.isLoading) {
+        const { isLoading, isError, items } = this.props;
+
+        if (isLoading) {
             return <ListingItem title={"Loading..."} />;
-        } else if (this.props.isError) {
+        } else if (isError) {
             return <ListingItem title={"Something went wrong. Please try again later."} />;
-        } else if (this.props.items.length === 0) {
+        } else if (items.length === 0) {
             return <ListingItem title={"No results found."} />;
         }
 
-        const listItems = [];
-        for (const item of this.props.items) {
-            listItems.push(
-                <ListingHeading key={`item-${item.id}`} title={item.title} count={item.subitems.length} />
-            );
+        return this.getOrderedItems().filter(v => v.items.length !== 0).map(v =>
+            <ListingGroup key={`listing-group-${v.group}`} item={v.group}>
+                {v.items.map(item =>
+                    <ListingItem key={`listing-item-${item.id}`} title={item.title}/>
+                )}
+            </ListingGroup>
+        );
+    }
 
-            const subitems = [];
-            for (const subitem of item.subitems) {
+    getGroupedItems() {
+        const { groupBy, items } = this.props;
 
-                subitems.push(
-                    <ListingItem key={`subitem-${subitem.id}`} title={subitem.title} />
-                );
+        return items.reduce((tmpItems, v) => {
+            const groupByValue = v[groupBy];
+            if (!(groupByValue in tmpItems)) {
+                tmpItems[groupByValue] = [];
             }
-            listItems.push(
-                <ul key={`subitems-listing-${item.id}`}>
-                    {subitems}
-                </ul>
-            );
-        }
-        return listItems;
+            tmpItems[groupByValue].push({...v});
+            return tmpItems;
+        }, {});
+    }
+
+    getOrderedItems() {
+        const groupedItems = this.getGroupedItems();
+        const { order = [] } = this.props;
+        const orderedItems = [];
+        const leftOverKeys = Object.keys(groupedItems).filter(v => !order.includes(v));
+        return order.concat(leftOverKeys).map(key => ({
+            group: key,
+            items: groupedItems[key] || []
+        }));
     }
 
     render() {
@@ -52,7 +66,7 @@ export default class Listing extends React.Component {
                 </form>
                 </div>
                 <ul className="listing__list">
-                {this.renderListItems()}
+                    {this.renderListItems()}
                 </ul>
             </div>
         );
@@ -60,14 +74,9 @@ export default class Listing extends React.Component {
 }
 
 Listing.propTypes = {
-    items: PropTypes.arrayOf(PropTypes.shape({
-        title: PropTypes.string,
-        id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
-        subitems: PropTypes.arrayOf(PropTypes.shape({
-            title: PropTypes.string,
-            id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
-        })),
-    })),
+    items: PropTypes.array,
     isLoading: PropTypes.bool,
     isError: PropTypes.bool,
+    groupBy: PropTypes.string,
+    order: PropTypes.arrayOf(PropTypes.string),
 };
diff --git a/opentech/static_src/src/app/src/components/ListingGroup.js b/opentech/static_src/src/app/src/components/ListingGroup.js
new file mode 100644
index 000000000..0e072445d
--- /dev/null
+++ b/opentech/static_src/src/app/src/components/ListingGroup.js
@@ -0,0 +1,21 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import ListingHeading from '@components/ListingHeading';
+
+export default class ListingGroup extends React.Component {
+    render() {
+        return (
+            <>
+                <ListingHeading title={this.props.item} count={this.props.children.length} />
+                <ul>
+                    {this.props.children}
+                </ul>
+            </>
+        );
+    }
+}
+
+ListingGroup.propTypes = {
+    item: PropTypes.object,
+};
diff --git a/opentech/static_src/src/app/src/containers/ByStatusListing.js b/opentech/static_src/src/app/src/containers/ByStatusListing.js
index 58aaf392d..c5fd7a7a2 100644
--- a/opentech/static_src/src/app/src/containers/ByStatusListing.js
+++ b/opentech/static_src/src/app/src/containers/ByStatusListing.js
@@ -5,7 +5,7 @@ import { connect } from 'react-redux'
 import Listing from '@components/Listing';
 import {
     getCurrentRound,
-    getCurrentRoundSubmissionsByStatus,
+    getCurrentRoundSubmissions,
     getSubmissionsByRoundErrorState,
     getSubmissionsByRoundLoadingState,
 } from '@selectors/submissions';
@@ -31,23 +31,26 @@ class ByStatusListing extends React.Component {
 
     render() {
         const { isLoading, isError } = this.props;
-        return <Listing isLoading={isLoading} isError={isError} items={this.getListingItems()} />;
-    }
-
-    getListingItems() {
-        return this.props.items.map(v => ({
-            id: v.id,
-            title: v.title,
-            subitems: v.submissions.map(v => ({
-                title: v.title,
-                id: v.id,
-            })),
-        }));
+        return <Listing
+                    isLoading={isLoading}
+                    isError={isError}
+                    items={this.props.items}
+                    groupBy={'status'}
+                    order={[
+                        'in_discussion',
+                        'more_info',
+                        'internal_review',
+                        'post_review_discussion',
+                        'post_review_more_info',
+                        'accepted',
+                        'rejected',
+                    ]}
+            />;
     }
 }
 
 const mapStateToProps = state => ({
-    items: getCurrentRoundSubmissionsByStatus(state),
+    items: getCurrentRoundSubmissions(state),
     roundId: getCurrentRound(state),
     isError: getSubmissionsByRoundErrorState(state),
     isLoading: getSubmissionsByRoundLoadingState(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 12f8869ae..c866758b7 100644
--- a/opentech/static_src/src/app/src/redux/reducers/submissions.js
+++ b/opentech/static_src/src/app/src/redux/reducers/submissions.js
@@ -26,7 +26,7 @@ export default function submissions(state = initialState, action) {
                 items: {
                     ...state.items,
                     ...action.data.results.reduce((newItems, v) => {
-                        newItems[v.id] = v;
+                        newItems[v.id] = { ...v };
                         return newItems;
                     }, {}),
                 },
diff --git a/opentech/static_src/src/app/src/redux/selectors/submissions.js b/opentech/static_src/src/app/src/redux/selectors/submissions.js
index 892da24e1..6b29a2fb9 100644
--- a/opentech/static_src/src/app/src/redux/selectors/submissions.js
+++ b/opentech/static_src/src/app/src/redux/selectors/submissions.js
@@ -14,28 +14,6 @@ const getCurrentRoundSubmissions = createSelector(
     }
 );
 
-const getCurrentRoundSubmissionsByStatus = createSelector(
-    [getCurrentRoundSubmissions] ,
-    currentRoundSubmissions => {
-        const submissionsByStatus = {};
-        for (const submission of currentRoundSubmissions) {
-            if (!(submission.status in submissionsByStatus)) {
-                submissionsByStatus[submission.status] = [];
-            }
-            submissionsByStatus[submission.status].push({ ...submission });
-        }
-        const formattedSubmissionsByStatus = [];
-        for (const [submissionStatus, statusSubmissions] of Object.entries(submissionsByStatus)) {
-            formattedSubmissionsByStatus.push({
-                id: submissionStatus,
-                title: submissionStatus,
-                submissions: statusSubmissions,
-            });
-        }
-        return formattedSubmissionsByStatus;
-    }
-);
-
 const getSubmissionsByRoundErrorState = state => state.submissions.itemsByRoundLoadingError;
 
 const getSubmissionsByRoundLoadingState = state => state.submissions.itemsByRoundLoading;
@@ -43,7 +21,6 @@ const getSubmissionsByRoundLoadingState = state => state.submissions.itemsByRoun
 export {
     getCurrentRound,
     getCurrentRoundSubmissions,
-    getCurrentRoundSubmissionsByStatus,
     getSubmissionsByRoundErrorState,
     getSubmissionsByRoundLoadingState,
 };
-- 
GitLab