From eef4e2113cc034f7fcf0c4d547e24c57abfcf3d6 Mon Sep 17 00:00:00 2001 From: Mahmoud Date: Thu, 6 Oct 2022 13:50:12 -0500 Subject: [PATCH 1/5] Initial work on metric config refactor in settings --- publisher/src/components/Badge/Badge.tsx | 7 +- .../MetricsView/MetricsView.styles.tsx | 35 +++-- .../components/MetricsView/MetricsView.tsx | 139 ++++++++---------- publisher/src/pages/Settings.tsx | 8 +- 4 files changed, 97 insertions(+), 92 deletions(-) diff --git a/publisher/src/components/Badge/Badge.tsx b/publisher/src/components/Badge/Badge.tsx index 0ecc41b1c..7d882d01a 100644 --- a/publisher/src/components/Badge/Badge.tsx +++ b/publisher/src/components/Badge/Badge.tsx @@ -28,11 +28,13 @@ export type BadgeProps = { color: BadgeColors; disabled?: boolean; loading?: boolean; + noMargin?: boolean; }; export const BadgeElement = styled.div<{ color?: BadgeColors; disabled?: boolean; + noMargin?: boolean; }>` height: 24px; display: flex; @@ -55,21 +57,22 @@ export const BadgeElement = styled.div<{ }}; color: ${palette.solid.white}; padding: 4px 8px; - margin-left: 10px; font-size: 0.65rem; font-weight: 600; white-space: nowrap; text-transform: capitalize; + ${({ noMargin }) => !noMargin && `margin-left: 10px;`}; `; export const Badge: React.FC = ({ color, disabled, loading, + noMargin, children, }) => { return ( - + {children} {loading && } diff --git a/publisher/src/components/MetricsView/MetricsView.styles.tsx b/publisher/src/components/MetricsView/MetricsView.styles.tsx index dc4144466..f68d8b9a4 100644 --- a/publisher/src/components/MetricsView/MetricsView.styles.tsx +++ b/publisher/src/components/MetricsView/MetricsView.styles.tsx @@ -31,7 +31,9 @@ export const MetricsViewControlPanel = styled.div` height: calc(100% - 170px); width: 100%; display: flex; + flex-wrap: wrap; justify-content: space-between; + margin-top: 29px; `; export const PanelContainerLeft = styled.div` @@ -55,33 +57,30 @@ export const PanelContainerRight = styled.div` type MetricBoxContainerProps = { enabled?: boolean; - selected?: boolean; }; export const MetricBoxContainer = styled.div` + width: 100%; + height: 197px; display: flex; flex-direction: column; - border: 1px solid - ${({ selected }) => - selected ? palette.solid.blue : palette.highlight.grey2}; - border-radius: 12px; - padding: 15px; - margin-bottom: 11px; + justify-content: space-between; + border: 1px solid ${palette.highlight.grey2}; + padding: 27px 24px; transition: 0.2s ease; color: ${({ enabled }) => enabled ? palette.solid.darkgrey : palette.highlight.grey7}; - ${({ selected }) => - selected && `box-shadow: 0px 4px 10px ${palette.highlight.blue};`} &:hover { cursor: pointer; - ${({ selected }) => - !selected && `border: 1px solid ${palette.highlight.lightblue2}`}; + border: 1px solid ${palette.solid.blue}; } `; export const MetricBoxWrapper = styled.div` - display: block; + display: flex; + max-width: 50%; + flex: 1 1 50%; `; export const ActiveMetricSettingHeader = styled.div` @@ -106,12 +105,13 @@ type MetricNameProps = { isTitle?: boolean }; export const MetricName = styled.div` ${({ isTitle }) => - isTitle ? typography.sizeCSS.title : typography.sizeCSS.medium} + isTitle ? typography.sizeCSS.title : typography.sizeCSS.large} `; export const MetricDescription = styled.div` ${typography.sizeCSS.normal} - color: ${palette.highlight.grey9}; + height: 100%; + margin: 11px 0; @media only screen and (max-width: 1000px) { ${typography.sizeCSS.small} @@ -328,3 +328,10 @@ export const MetricSettingsDisplayError = styled.div` justify-content: center; margin-top: 50px; `; + +export const StickyHeader = styled.div` + width: 100%; + position: sticky; + top: 0; + background: ${palette.solid.white}; +`; diff --git a/publisher/src/components/MetricsView/MetricsView.tsx b/publisher/src/components/MetricsView/MetricsView.tsx index 0faad657a..da4b282af 100644 --- a/publisher/src/components/MetricsView/MetricsView.tsx +++ b/publisher/src/components/MetricsView/MetricsView.tsx @@ -69,6 +69,7 @@ import { PanelContainerRight, RadioButtonGroupWrapper, Slider, + StickyHeader, Subheader, ToggleSwitch, ToggleSwitchInput, @@ -115,11 +116,6 @@ type MetricBoxProps = { setActiveMetricKey: React.Dispatch>; }; -const reportFrequencyBadgeColors: BadgeColorMapping = { - ANNUAL: "ORANGE", - MONTHLY: "GREEN", -}; - const MetricBox: React.FC = ({ metricKey, displayName, @@ -133,23 +129,14 @@ const MetricBox: React.FC = ({ setActiveMetricKey(metricKey)} enabled={enabled} - selected={metricKey === activeMetricKey} > - - - {displayName} - - {frequency} - - - - {!enabled && } - - + {displayName} {description} + + + {frequency} + + ); }; @@ -834,56 +821,58 @@ export const MetricsView: React.FC = observer(() => { return ( <> - Metrics - - - - {userStore.currentAgency?.systems.map((filterOption) => ( - - setActiveMetricFilter(removeSnakeCase(filterOption)) - } - capitalize - > - {removeSnakeCase(filterOption.toLowerCase())} - - ))} - - + + + + {userStore.currentAgency?.systems.map((filterOption) => ( + + setActiveMetricFilter(removeSnakeCase(filterOption)) + } + capitalize + > + {removeSnakeCase(filterOption.toLowerCase())} + + ))} + + + {/* List Of Metrics */} - - {filteredMetricSettings && - Object.values(filteredMetricSettings).map((metric) => ( - { - if (configPanelRef.current) { - configPanelRef.current.scrollTo({ - top: 0, - behavior: "smooth", - }); - } - }} - > - - - ))} - + {/* */} + {filteredMetricSettings && + Object.values(filteredMetricSettings).map((metric) => ( + { + if (configPanelRef.current) { + configPanelRef.current.scrollTo({ + top: 0, + behavior: "smooth", + }); + } + }} + > + + + ))} + {/* */} {/* Data | Configuration | Context */} - + {/* @@ -921,15 +910,15 @@ export const MetricsView: React.FC = observer(() => { ))} - + */} - {/* Data */} - {activeConfigSection === "Data" && ( + {/* Data */} + {/* {activeConfigSection === "Data" && ( - )} + )} */} - {/* Configuration */} - {activeConfigSection === "Configuration" && ( + {/* Configuration */} + {/* {activeConfigSection === "Configuration" && ( { saveAndUpdateMetricSettings={saveAndUpdateMetricSettings} /> - )} + )} */} - {/* Context */} - {activeConfigSection === "Context" && ( + {/* Context */} + {/* {activeConfigSection === "Context" && ( { /> )} - + */} diff --git a/publisher/src/pages/Settings.tsx b/publisher/src/pages/Settings.tsx index 2d5f43489..8abea787f 100644 --- a/publisher/src/pages/Settings.tsx +++ b/publisher/src/pages/Settings.tsx @@ -18,6 +18,7 @@ import React, { useState } from "react"; import { UploadedFiles } from "../components/DataUpload"; +import { MetricsView } from "../components/MetricsView"; import { AccountSettings, ContentDisplay, @@ -25,7 +26,11 @@ import { SettingsMenu, } from "../components/Settings"; -export const menuOptions = ["Your Account", "Uploaded Files"] as const; +export const menuOptions = [ + "Your Account", + "Uploaded Files", + "Metric Configuration", +] as const; export type MenuOptions = typeof menuOptions[number]; const Settings = () => { @@ -46,6 +51,7 @@ const Settings = () => { {activeMenuItem === "Your Account" && } {activeMenuItem === "Uploaded Files" && } + {activeMenuItem === "Metric Configuration" && } ); From 35ee8dee0945c22f1cb908cecba18609a59b6da7 Mon Sep 17 00:00:00 2001 From: Mahmoud Date: Thu, 6 Oct 2022 15:19:34 -0500 Subject: [PATCH 2/5] Continue iterating on refactor up to the breakdown page --- publisher/src/components/Badge/Badge.tsx | 2 +- .../MetricsView/MetricsView.styles.tsx | 26 ++- .../components/MetricsView/MetricsView.tsx | 189 ++++++------------ 3 files changed, 81 insertions(+), 136 deletions(-) diff --git a/publisher/src/components/Badge/Badge.tsx b/publisher/src/components/Badge/Badge.tsx index 7d882d01a..7bdd62691 100644 --- a/publisher/src/components/Badge/Badge.tsx +++ b/publisher/src/components/Badge/Badge.tsx @@ -42,7 +42,7 @@ export const BadgeElement = styled.div<{ align-items: center; background: ${({ color, disabled }) => { if (color === "GREY" || disabled) { - return palette.highlight.grey9; + return palette.highlight.grey8; } if (color === "RED") { return palette.solid.red; diff --git a/publisher/src/components/MetricsView/MetricsView.styles.tsx b/publisher/src/components/MetricsView/MetricsView.styles.tsx index f68d8b9a4..b8da8f5e9 100644 --- a/publisher/src/components/MetricsView/MetricsView.styles.tsx +++ b/publisher/src/components/MetricsView/MetricsView.styles.tsx @@ -60,16 +60,17 @@ type MetricBoxContainerProps = { }; export const MetricBoxContainer = styled.div` - width: 100%; height: 197px; + max-width: 50%; display: flex; + flex: 1 1 50%; flex-direction: column; justify-content: space-between; border: 1px solid ${palette.highlight.grey2}; padding: 27px 24px; transition: 0.2s ease; color: ${({ enabled }) => - enabled ? palette.solid.darkgrey : palette.highlight.grey7}; + enabled ? palette.solid.darkgrey : palette.highlight.grey10}; &:hover { cursor: pointer; @@ -79,8 +80,6 @@ export const MetricBoxContainer = styled.div` export const MetricBoxWrapper = styled.div` display: flex; - max-width: 50%; - flex: 1 1 50%; `; export const ActiveMetricSettingHeader = styled.div` @@ -121,7 +120,7 @@ export const MetricDescription = styled.div` export const MetricDetailsDisplay = styled.div` width: 100%; overflow-y: scroll; - padding: 24px 15px 0 15px; + padding: 24px 0; `; export const MetricOnOffWrapper = styled.div` @@ -335,3 +334,20 @@ export const StickyHeader = styled.div` top: 0; background: ${palette.solid.white}; `; + +export const BackToMetrics = styled.div` + color: ${palette.solid.blue}; + transition: 0.2s ease; + margin-bottom: 24px; + + &:hover { + cursor: pointer; + opacity: 0.85; + } +`; + +export const MetricConfigurationDisplay = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; +`; diff --git a/publisher/src/components/MetricsView/MetricsView.tsx b/publisher/src/components/MetricsView/MetricsView.tsx index da4b282af..8404e4e65 100644 --- a/publisher/src/components/MetricsView/MetricsView.tsx +++ b/publisher/src/components/MetricsView/MetricsView.tsx @@ -27,21 +27,19 @@ import { removeCommaSpaceAndTrim, removeSnakeCase, } from "../../utils"; -import { Badge, BadgeColorMapping } from "../Badge"; -import DatapointsView from "../DataViz/DatapointsView"; +import { Badge } from "../Badge"; import { BinaryRadioButton, BinaryRadioGroupClearButton, BinaryRadioGroupContainer, BinaryRadioGroupQuestion, - NotReportedIcon, TextInput, } from "../Forms"; import { Loading } from "../Loading"; -import { PageTitle, TabbedBar, TabbedItem, TabbedOptions } from "../Reports"; +import { TabbedBar, TabbedItem, TabbedOptions } from "../Reports"; import { showToast } from "../Toast"; import { - ActiveMetricSettingHeader, + BackToMetrics, Dimension, DimensionTitle, DimensionTitleWrapper, @@ -51,22 +49,19 @@ import { Header, Label, MetricBoxContainer, - MetricBoxWrapper, MetricConfigurationContainer, + MetricConfigurationDisplay, MetricContextContainer, MetricContextItem, MetricDescription, MetricDetailsDisplay, MetricDisaggregations, MetricName, - MetricNameBadgeToggleWrapper, MetricNameBadgeWrapper, MetricOnOffWrapper, MetricsViewContainer, MetricsViewControlPanel, MultipleChoiceWrapper, - PanelContainerLeft, - PanelContainerRight, RadioButtonGroupWrapper, Slider, StickyHeader, @@ -134,7 +129,7 @@ const MetricBox: React.FC = ({ {description} - {frequency} + {!enabled ? "Inactive" : frequency.toLowerCase()} @@ -362,11 +357,13 @@ const MetricContextConfiguration: React.FC = ({ }; useEffect(() => { - contexts.forEach((context) => { - if (context.type === "NUMBER") { - contextNumberValidation(context.key, context.value || ""); - } - }); + if (contexts) { + contexts.forEach((context) => { + if (context.type === "NUMBER") { + contextNumberValidation(context.key, context.value || ""); + } + }); + } }, [contexts]); return ( @@ -518,29 +515,16 @@ export type MetricSettings = { export const MetricsView: React.FC = observer(() => { const { reportStore, userStore, datapointsStore } = useStore(); - const configPanelRef = useRef(null); - - // TODO(#13805) Temporarily hiding the data tab until it is implemented. Currently it's only visible to Recidiviz admins. - const configSections = ["Data", "Configuration", "Context"]; - type ConfigSections = typeof configSections[number]; const [activeMetricFilter, setActiveMetricFilter] = useState(); - - const [activeConfigSection, setActiveConfigSection] = - useState("Data"); - const [isLoading, setIsLoading] = useState(true); - const [loadingError, setLoadingError] = useState( undefined ); - const [activeMetricKey, setActiveMetricKey] = useState(""); - const [metricSettings, setMetricSettings] = useState<{ [key: string]: MetricsViewMetric; }>({}); - const [filteredMetricSettings, setFilteredMetricSettings] = useState<{ [key: string]: MetricsViewMetric; }>({}); @@ -752,7 +736,6 @@ export const MetricsView: React.FC = observer(() => { }); setMetricSettings(metricKeyToMetricMap); - setActiveMetricKey(Object.keys(metricKeyToMetricMap)[0]); }; useEffect( @@ -814,131 +797,77 @@ export const MetricsView: React.FC = observer(() => { return ; } - if (!metricSettings[activeMetricKey]) { + if (loadingError) { return
Error: {loadingError}
; } return ( <> - - - - {userStore.currentAgency?.systems.map((filterOption) => ( - - setActiveMetricFilter(removeSnakeCase(filterOption)) - } - capitalize - > - {removeSnakeCase(filterOption.toLowerCase())} - - ))} - - - + {!activeMetricKey && ( + + + + {userStore.currentAgency?.systems.map((filterOption) => ( + + setActiveMetricFilter(removeSnakeCase(filterOption)) + } + capitalize + > + {removeSnakeCase(filterOption.toLowerCase())} + + ))} + + + + )} {/* List Of Metrics */} - {/* */} {filteredMetricSettings && + !activeMetricKey && Object.values(filteredMetricSettings).map((metric) => ( - { - if (configPanelRef.current) { - configPanelRef.current.scrollTo({ - top: 0, - behavior: "smooth", - }); - } - }} - > - - + ))} - {/* */} - - {/* Data | Configuration | Context */} - {/* - - - - {metricSettings[activeMetricKey]?.display_name} - - - {metricSettings[activeMetricKey]?.frequency} - - - - - - {configSections.map((section) => ( - { - setActiveConfigSection(section); - if (configPanelRef.current) { - configPanelRef.current.scrollTo({ - top: 0, - behavior: "smooth", - }); - } - }} - > - {section} - - ))} - - - */} - - {/* Data */} - {/* {activeConfigSection === "Data" && ( - - )} */} - - {/* Configuration */} - {/* {activeConfigSection === "Configuration" && ( + + {/* Metric Configuration */} + {activeMetricKey && ( + + setActiveMetricKey("")}> + ← Back to Metrics + + + + {metricSettings[activeMetricKey]?.display_name} + + - - )} */} - - {/* Context */} - {/* {activeConfigSection === "Context" && ( - - )} - */} + + )} From d643881692e208ec69d386ed2d0f3d5f08e6fae8 Mon Sep 17 00:00:00 2001 From: Mahmoud Date: Thu, 6 Oct 2022 15:21:03 -0500 Subject: [PATCH 3/5] Remove routing to old metrics page - remove from menu --- publisher/src/App.tsx | 2 -- publisher/src/components/Menu/Menu.tsx | 11 ----------- 2 files changed, 13 deletions(-) diff --git a/publisher/src/App.tsx b/publisher/src/App.tsx index f13d69f5e..a46a5a721 100644 --- a/publisher/src/App.tsx +++ b/publisher/src/App.tsx @@ -23,7 +23,6 @@ import { trackNavigation } from "./analytics"; import { DataUpload } from "./components/DataUpload"; import { PageWrapper } from "./components/Forms"; import Header from "./components/Header"; -import { MetricsView } from "./components/MetricsView"; import CreateReports from "./components/Reports/CreateReport"; import ReportDataEntry from "./components/Reports/ReportDataEntry"; import ReviewMetrics from "./components/ReviewMetrics/ReviewMetrics"; @@ -46,7 +45,6 @@ const App: React.FC = (): ReactElement => { } /> } /> } /> - } /> } /> } /> diff --git a/publisher/src/components/Menu/Menu.tsx b/publisher/src/components/Menu/Menu.tsx index a4ea0ee74..d31816fc1 100644 --- a/publisher/src/components/Menu/Menu.tsx +++ b/publisher/src/components/Menu/Menu.tsx @@ -37,7 +37,6 @@ enum MenuItems { LearnMore = "LEARN MORE", Settings = "SETTINGS", Agencies = "AGENCIES", - Metrics = "METRICS", } const Menu = () => { @@ -77,8 +76,6 @@ const Menu = () => { setActiveMenuItem(MenuItems.CreateReport); } else if (location.pathname === "/settings") { setActiveMenuItem(MenuItems.Settings); - } else if (location.pathname === "/metrics") { - setActiveMenuItem(MenuItems.Metrics); } else { setActiveMenuItem(undefined); } @@ -92,14 +89,6 @@ const Menu = () => { `Welcome, ${userStore.nameOrEmail} at ${userStore.currentAgency.name}`} - {/* Metrics View */} - navigate("/metrics")} - active={activeMenuItem === MenuItems.Metrics} - > - Metrics - - {/* Reports */} navigate("/")} From 71276c0ebdd7243c078a6d405fe577e92180e00b Mon Sep 17 00:00:00 2001 From: Mahmoud Date: Thu, 6 Oct 2022 15:25:28 -0500 Subject: [PATCH 4/5] Styling adjustment --- publisher/src/components/MetricsView/MetricsView.styles.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/publisher/src/components/MetricsView/MetricsView.styles.tsx b/publisher/src/components/MetricsView/MetricsView.styles.tsx index b8da8f5e9..57f2ca73a 100644 --- a/publisher/src/components/MetricsView/MetricsView.styles.tsx +++ b/publisher/src/components/MetricsView/MetricsView.styles.tsx @@ -33,7 +33,6 @@ export const MetricsViewControlPanel = styled.div` display: flex; flex-wrap: wrap; justify-content: space-between; - margin-top: 29px; `; export const PanelContainerLeft = styled.div` @@ -333,6 +332,7 @@ export const StickyHeader = styled.div` position: sticky; top: 0; background: ${palette.solid.white}; + margin-bottom: 29px; `; export const BackToMetrics = styled.div` From 1a74170f9f62861b92da2ced2eab4f5a65f559a0 Mon Sep 17 00:00:00 2001 From: Mahmoud Date: Thu, 6 Oct 2022 15:58:11 -0500 Subject: [PATCH 5/5] Fix key warning --- publisher/src/components/MetricsView/MetricsView.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/publisher/src/components/MetricsView/MetricsView.tsx b/publisher/src/components/MetricsView/MetricsView.tsx index 8404e4e65..e5ce2c07c 100644 --- a/publisher/src/components/MetricsView/MetricsView.tsx +++ b/publisher/src/components/MetricsView/MetricsView.tsx @@ -833,6 +833,7 @@ export const MetricsView: React.FC = observer(() => { !activeMetricKey && Object.values(filteredMetricSettings).map((metric) => (