webui: add open/closed issues count

Quentin Gliech created

Change summary

webui/src/list/FilterToolbar.js | 82 ++++++++++++++++++++++++++++------
webui/src/list/ListQuery.js     |  2 
2 files changed, 68 insertions(+), 16 deletions(-)

Detailed changes

webui/src/list/FilterToolbar.js 🔗

@@ -1,10 +1,17 @@
 import { makeStyles } from '@material-ui/styles';
+import { useQuery } from '@apollo/react-hooks';
+import gql from 'graphql-tag';
 import React from 'react';
 import Toolbar from '@material-ui/core/Toolbar';
 import ErrorOutline from '@material-ui/icons/ErrorOutline';
 import CheckCircleOutline from '@material-ui/icons/CheckCircleOutline';
 import Filter, { parse, stringify } from './Filter';
 
+// simple pipe operator
+// pipe(o, f, g, h) <=> h(g(f(o)))
+// TODO: move this out?
+const pipe = (initial, ...funcs) => funcs.reduce((acc, f) => f(acc), initial);
+
 const useStyles = makeStyles(theme => ({
   toolbar: {
     backgroundColor: theme.palette.grey['100'],
@@ -18,40 +25,85 @@ const useStyles = makeStyles(theme => ({
   },
 }));
 
+const BUG_COUNT_QUERY = gql`
+  query($query: String) {
+    defaultRepository {
+      bugs: allBugs(query: $query) {
+        totalCount
+      }
+    }
+  }
+`;
+
+// This prepends the filter text with a count
+function CountingFilter({ query, children, ...props }) {
+  const { data, loading, error } = useQuery(BUG_COUNT_QUERY, {
+    variables: { query },
+  });
+
+  var prefix;
+  if (loading) prefix = '...';
+  else if (error) prefix = '???';
+  // TODO: better prefixes & error handling
+  else prefix = data.defaultRepository.bugs.totalCount;
+
+  return (
+    <Filter {...props}>
+      {prefix} {children}
+    </Filter>
+  );
+}
+
 function FilterToolbar({ query, queryLocation }) {
   const classes = useStyles();
   const params = parse(query);
+
   const hasKey = key => params[key] && params[key].length > 0;
   const hasValue = (key, value) => hasKey(key) && params[key].includes(value);
-  const replaceParam = (key, value) => {
-    const p = {
-      ...params,
-      [key]: [value],
-    };
-    return queryLocation(stringify(p));
-  };
+  const loc = params => pipe(params, stringify, queryLocation);
+  const replaceParam = (key, value) => params => ({
+    ...params,
+    [key]: [value],
+  });
+  const clearParam = key => params => ({
+    ...params,
+    [key]: [],
+  });
 
-  // TODO: open/closed count
   // TODO: author/label filters
   return (
     <Toolbar className={classes.toolbar}>
-      <Filter
+      <CountingFilter
         active={hasValue('status', 'open')}
-        to={replaceParam('status', 'open')}
+        query={pipe(
+          params,
+          replaceParam('status', 'open'),
+          clearParam('sort'),
+          stringify
+        )}
+        to={pipe(params, replaceParam('status', 'open'), loc)}
         icon={ErrorOutline}
       >
         open
-      </Filter>
-      <Filter
+      </CountingFilter>
+      <CountingFilter
         active={hasValue('status', 'closed')}
-        to={replaceParam('status', 'closed')}
+        query={pipe(
+          params,
+          replaceParam('status', 'closed'),
+          clearParam('sort'),
+          stringify
+        )}
+        to={pipe(params, replaceParam('status', 'closed'), loc)}
         icon={CheckCircleOutline}
       >
         closed
-      </Filter>
+      </CountingFilter>
       <div className={classes.spacer} />
+      {/*
       <Filter active={hasKey('author')}>Author</Filter>
       <Filter active={hasKey('label')}>Label</Filter>
+      */}
       <Filter
         dropdown={[
           ['id', 'ID'],
@@ -62,7 +114,7 @@ function FilterToolbar({ query, queryLocation }) {
         ]}
         active={hasKey('sort')}
         itemActive={key => hasValue('sort', key)}
-        to={key => replaceParam('sort', key)}
+        to={key => pipe(params, replaceParam('sort', key), loc)}
       >
         Sort
       </Filter>

webui/src/list/ListQuery.js 🔗

@@ -187,7 +187,7 @@ function ListQuery() {
   const location = useLocation();
   const history = useHistory();
   const params = new URLSearchParams(location.search);
-  const query = params.get('q');
+  const query = params.get('q') || '';
 
   const [input, setInput] = useState(query);