From b73bba304bbe2ab6c6ee13586984be13831ee8a9 Mon Sep 17 00:00:00 2001 From: Urban Hafner Date: Wed, 24 Apr 2024 16:12:04 +0200 Subject: [PATCH] Start making the code generic by passing in functions --- .../components/clustering/BrandSelector.jsx | 38 ++++++ .../components/clustering/LoadingOverlay.jsx | 18 +++ .../admin/components/clustering/Summary.jsx | 13 ++ .../src/admin/micro-clusters/App.jsx | 121 ++---------------- .../src/admin/micro-clusters/CreateRow.jsx | 13 +- .../micro-clusters/DisplayMacroClusters.jsx | 21 ++- .../micro-clusters/DisplayMicroCluster.jsx | 9 +- .../micro-clusters/DisplayMicroClusters.jsx | 15 ++- .../src/admin/micro-clusters/GenericApp.jsx | 68 ++++++++++ 9 files changed, 187 insertions(+), 129 deletions(-) create mode 100644 app/javascript/src/admin/components/clustering/BrandSelector.jsx create mode 100644 app/javascript/src/admin/components/clustering/LoadingOverlay.jsx create mode 100644 app/javascript/src/admin/components/clustering/Summary.jsx create mode 100644 app/javascript/src/admin/micro-clusters/GenericApp.jsx diff --git a/app/javascript/src/admin/components/clustering/BrandSelector.jsx b/app/javascript/src/admin/components/clustering/BrandSelector.jsx new file mode 100644 index 000000000..7e53bdb54 --- /dev/null +++ b/app/javascript/src/admin/components/clustering/BrandSelector.jsx @@ -0,0 +1,38 @@ +import React, { useContext } from "react"; +import Select from "react-select"; +import _ from "lodash"; + +import { DispatchContext, StateContext } from "../../micro-clusters/GenericApp"; +import { UPDATE_SELECTED_BRANDS } from "./actions"; +import { setInBrandSelector } from "./keyDownListener"; + +export const BrandSelector = ({ field }) => { + const dispatch = useContext(DispatchContext); + const { microClusters, selectedBrands } = useContext(StateContext); + const values = _.countBy(microClusters.map((c) => c[field])); + const options = _.sortBy( + _.map(values, (value, key) => ({ + value: key, + label: `${key} (${value})` + })), + "label" + ); + return ( +
+ { - dispatch({ type: UPDATE_SELECTED_BRANDS, payload: selected }); - }} - isMulti - value={selectedBrands} - onFocus={() => { - setInBrandSelector(true); - }} - onBlur={() => { - setInBrandSelector(false); - }} - /> -
- ); -}; +export const App = () => ( + +); diff --git a/app/javascript/src/admin/micro-clusters/CreateRow.jsx b/app/javascript/src/admin/micro-clusters/CreateRow.jsx index e3a8ad5fe..400e0b012 100644 --- a/app/javascript/src/admin/micro-clusters/CreateRow.jsx +++ b/app/javascript/src/admin/micro-clusters/CreateRow.jsx @@ -1,17 +1,16 @@ import React, { useCallback, useContext, useEffect } from "react"; import _ from "lodash"; import { postRequest, putRequest } from "../../fetch"; -import { StateContext, DispatchContext } from "./App"; +import { StateContext, DispatchContext } from "./GenericApp"; import { groupedInks } from "./groupedInks"; import { UPDATING, ADD_MACRO_CLUSTER, REMOVE_MICRO_CLUSTER } from "../components/clustering/actions"; -import { assignCluster } from "./assignCluster"; import { keyDownListener } from "../components/clustering/keyDownListener"; -export const CreateRow = ({ afterCreate }) => { +export const CreateRow = ({ afterCreate, assignCluster }) => { const { updating, activeCluster } = useContext(StateContext); const dispatch = useContext(DispatchContext); const values = computeValues(activeCluster); @@ -20,9 +19,10 @@ export const CreateRow = ({ afterCreate }) => { values, activeCluster.id, dispatch, - afterCreate + afterCreate, + assignCluster ); - }, [activeCluster.id, afterCreate, dispatch, values]); + }, [activeCluster.id, afterCreate, dispatch, values, assignCluster]); const ignore = () => { ignoreCluster(activeCluster).then( dispatch({ type: REMOVE_MICRO_CLUSTER, payload: activeCluster }) @@ -89,7 +89,8 @@ const createMacroClusterAndAssign = ( values, microClusterId, dispatch, - afterCreate + afterCreate, + assignCluster ) => { dispatch({ type: UPDATING }); setTimeout(() => { diff --git a/app/javascript/src/admin/micro-clusters/DisplayMacroClusters.jsx b/app/javascript/src/admin/micro-clusters/DisplayMacroClusters.jsx index 28bd6dded..69d026368 100644 --- a/app/javascript/src/admin/micro-clusters/DisplayMacroClusters.jsx +++ b/app/javascript/src/admin/micro-clusters/DisplayMacroClusters.jsx @@ -4,10 +4,9 @@ import levenshtein from "fast-levenshtein"; import ScrollIntoViewIfNeeded from "react-scroll-into-view-if-needed"; import { matchSorter } from "match-sorter"; -import { assignCluster } from "./assignCluster"; import { CollectedInksList } from "./CollectedInksList"; import { SearchLink } from "../components/clustering/SearchLink"; -import { StateContext, DispatchContext } from "./App"; +import { StateContext, DispatchContext } from "./GenericApp"; import { ASSIGN_TO_MACRO_CLUSTER, NEXT_MACRO_CLUSTER, @@ -20,7 +19,7 @@ import { } from "../components/clustering/keyDownListener"; import { useCallback } from "react"; -export const DisplayMacroClusters = ({ afterAssign }) => { +export const DisplayMacroClusters = ({ afterAssign, assignCluster }) => { const dispatch = useContext(DispatchContext); useEffect(() => { return keyDownListener(({ keyCode }) => { @@ -28,10 +27,12 @@ export const DisplayMacroClusters = ({ afterAssign }) => { if (keyCode == 75) dispatch({ type: PREVIOUS_MACRO_CLUSTER }); }); }, [dispatch]); - return ; + return ( + + ); }; -const MacroClusterRows = ({ afterAssign }) => { +const MacroClusterRows = ({ afterAssign, assignCluster }) => { const { macroClusters, activeCluster, @@ -73,6 +74,7 @@ const MacroClusterRows = ({ afterAssign }) => { key={macroCluster.id} macroCluster={macroCluster} afterAssign={afterAssign} + assignCluster={assignCluster} selected={index == selectedMacroClusterIndex} /> )); @@ -165,7 +167,12 @@ const stripped = (str) => { .replace(/\s+/i, ""); }; -const MacroClusterRow = ({ macroCluster, afterAssign, selected }) => { +const MacroClusterRow = ({ + macroCluster, + afterAssign, + selected, + assignCluster +}) => { const { activeCluster, updating } = useContext(StateContext); const dispatch = useContext(DispatchContext); const [showInks, setShowInks] = useState(false); @@ -181,7 +188,7 @@ const MacroClusterRow = ({ macroCluster, afterAssign, selected }) => { afterAssign(microCluster); }); }, 10); - }, [activeCluster.id, afterAssign, dispatch, macroCluster.id]); + }, [activeCluster.id, afterAssign, dispatch, macroCluster.id, assignCluster]); useEffect(() => { if (!selected) return; diff --git a/app/javascript/src/admin/micro-clusters/DisplayMicroCluster.jsx b/app/javascript/src/admin/micro-clusters/DisplayMicroCluster.jsx index 12c9ad8c1..c69ce7a29 100644 --- a/app/javascript/src/admin/micro-clusters/DisplayMicroCluster.jsx +++ b/app/javascript/src/admin/micro-clusters/DisplayMicroCluster.jsx @@ -1,11 +1,11 @@ import React, { useContext } from "react"; -import { StateContext } from "./App"; +import { StateContext } from "./GenericApp"; import { DisplayMacroClusters } from "./DisplayMacroClusters"; import { CollectedInksList } from "./CollectedInksList"; import { CreateRow } from "./CreateRow"; -export const DisplayMicroCluster = ({ afterCreate }) => { +export const DisplayMicroCluster = ({ afterCreate, assignCluster }) => { const { activeCluster } = useContext(StateContext); return (
@@ -18,7 +18,10 @@ export const DisplayMicroCluster = ({ afterCreate }) => { - +
diff --git a/app/javascript/src/admin/micro-clusters/DisplayMicroClusters.jsx b/app/javascript/src/admin/micro-clusters/DisplayMicroClusters.jsx index a030faa9d..5e2da76b4 100644 --- a/app/javascript/src/admin/micro-clusters/DisplayMicroClusters.jsx +++ b/app/javascript/src/admin/micro-clusters/DisplayMicroClusters.jsx @@ -1,7 +1,7 @@ import React, { useEffect, useContext } from "react"; import { DisplayMicroCluster } from "./DisplayMicroCluster"; -import { DispatchContext, StateContext } from "./App"; +import { DispatchContext, StateContext } from "./GenericApp"; import { PREVIOUS, NEXT, @@ -9,16 +9,18 @@ import { } from "../components/clustering/actions"; import { keyDownListener } from "../components/clustering/keyDownListener"; import { useCallback } from "react"; -import { updateMacroCluster } from "./macroClusters"; -export const DisplayMicroClusters = () => { +export const DisplayMicroClusters = ({ + macroClusterUpdater, + assignCluster +}) => { const dispatch = useContext(DispatchContext); const { activeCluster } = useContext(StateContext); const { prev, next } = useNavigation(dispatch); const afterAssign = (newClusterData) => { dispatch({ type: REMOVE_MICRO_CLUSTER, payload: newClusterData }); const id = newClusterData.macro_cluster.id; - updateMacroCluster(id, dispatch); + macroClusterUpdater(id, dispatch); }; if (activeCluster) { return ( @@ -27,7 +29,10 @@ export const DisplayMicroClusters = () => {
- +
diff --git a/app/javascript/src/admin/micro-clusters/GenericApp.jsx b/app/javascript/src/admin/micro-clusters/GenericApp.jsx new file mode 100644 index 000000000..c2438404d --- /dev/null +++ b/app/javascript/src/admin/micro-clusters/GenericApp.jsx @@ -0,0 +1,68 @@ +import React, { useEffect, useReducer } from "react"; + +import { Spinner } from "../components/Spinner"; +import { DisplayMicroClusters } from "./DisplayMicroClusters"; +import { reducer, initalState } from "../components/clustering/reducer"; +import { Summary } from "../components/clustering/Summary"; +import { LoadingOverlay } from "../components/clustering/LoadingOverlay"; +import { BrandSelector } from "../components/clustering/BrandSelector"; + +export const StateContext = React.createContext(); +export const DispatchContext = React.createContext(); + +export const GenericApp = ({ + brandSelectorField, + microClusterLoader, + macroClusterLoader, + macroClusterUpdater, + assignCluster +}) => { + const [state, dispatch] = useReducer(reducer, initalState); + const { loadingMacroClusters, loadingMicroClusters } = state; + useEffect(() => { + microClusterLoader(dispatch); + }, [microClusterLoader]); + useEffect(() => { + macroClusterLoader(dispatch); + }, [macroClusterLoader]); + useEffect(() => { + if ( + loadingMicroClusters || + loadingMacroClusters || + state.microClusters.length > 0 + ) + return; + const intervalId = setInterval(() => { + microClusterLoader(dispatch); + }, 30 * 1000); + return () => { + clearInterval(intervalId); + }; + }, [ + loadingMicroClusters, + loadingMacroClusters, + state.microClusters.length, + microClusterLoader + ]); + if (!loadingMicroClusters && !loadingMacroClusters) { + return ( + + +
+ + + + {/*** TODO: DisplayMicroClusters is not yet generic ***/} + + {/*** TODO: DisplayMicroClusters is not yet generic ***/} +
+
+
+ ); + } else { + return ; + } +};