Skip to content

Commit

Permalink
[Publisher][Bug] Set Up Metrics Overview Page: Fix grouping of action…
Browse files Browse the repository at this point in the history
… required, available, and unavailable for supervision agencies with subpopulations (#1320)

* Intial work on fix for supervision subsystems in metric overview

* Refactor using a reducer - update types

* Adjust type

* Fix the frequency displayed in the overview page

* Add comments

* Add more comments

* Clean up

* Refactor getMetricFrequencies to get a list of frequencies from disaggregated supervision subsystems

* Comment fix

---------

Co-authored-by: Mahmoud <mahmoud@Mahmouds-MacBook-Pro.local>
Co-authored-by: Mahmoud <mahmoud@Mahmouds-MBP.cable.rcn.com>
  • Loading branch information
3 people committed May 2, 2024
1 parent 29ceed7 commit 8341078
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 51 deletions.
4 changes: 2 additions & 2 deletions common/utils/helperUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ export const slugify = (str: string): string =>
str?.replace(/\s/g, "-")?.toLowerCase();

export const frequencyString = (frequency?: string) => {
if (frequency === "ANNUAL") {
return "ANNUALLY";
if (frequency?.includes("ANNUAL")) {
return frequency.replaceAll("ANNUAL", "ANNUALLY");
}
return frequency;
};
Expand Down
198 changes: 161 additions & 37 deletions publisher/src/components/MetricsConfiguration/MetricsOverview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ import {
AgencySystem,
SupervisionSubsystems,
} from "@justice-counts/common/types";
import { frequencyString } from "@justice-counts/common/utils/helperUtils";
import {
frequencyString,
removeSnakeCase,
} from "@justice-counts/common/utils/helperUtils";
import { observer } from "mobx-react-lite";
import React from "react";
import { useParams } from "react-router-dom";
Expand All @@ -32,17 +35,22 @@ import { ReactComponent as RightArrowIcon } from "../assets/bold-right-arrow-ico
import { AppGuideKeys, GuideKeys } from "../HelpCenter/types";
import { createURLToGuide } from "../HelpCenter/utils";
import { DisclaimerBanner } from "../primitives";
import { useSettingsSearchParams } from "../Settings";
import {
replaceSystemMetricKeyWithNewSystem,
useSettingsSearchParams,
} from "../Settings";
import { ChildAgenciesDropdown } from "./ChildAgenciesDropdown";
import * as Styled from "./MetricsOverview.styled";
import { MetricInfo } from "./types";

export const MetricsOverview = observer(() => {
const [settingsSearchParams, setSettingsSearchParams] =
useSettingsSearchParams();

const { agencyId } = useParams() as { agencyId: string };
const { userStore, metricConfigStore } = useStore();

const { getMetricsBySystem } = metricConfigStore;
const { metrics, getMetricsBySystem } = metricConfigStore;

const { system: systemSearchParam } = settingsSearchParams;

Expand All @@ -58,6 +66,13 @@ export const MetricsOverview = observer(() => {
const currentAgency = userStore.getAgency(agencyId);
const currentSystem = systemSearchParam || currentAgency?.systems[0];

const agencySupervisionSubsystems = currentAgency?.systems.filter((system) =>
SupervisionSubsystems.includes(system)
);

const hasSupervisionSubsystems =
agencySupervisionSubsystems && agencySupervisionSubsystems.length > 0;

const isSuperagency = userStore.isAgencySuperagency(agencyId);

const isSuperagencySystem = currentSystem === "SUPERAGENCY";
Expand All @@ -68,20 +83,126 @@ export const MetricsOverview = observer(() => {
const showSystems =
currentAgency?.systems && currentAgency?.systems?.length > 1;

const actionRequiredMetrics = getMetricsBySystem(currentSystem)?.filter(
({ metric }) => metric.enabled === null
);
const metricsByCurrentSystem = getMetricsBySystem(currentSystem);

// Returns a given metric frequency(ies) accounting for supervision agencies disaggregated by subsystems
const getMetricFrequency = (metric: MetricInfo) => {
// Default frequencies (custom or default) for agencies without supervision subsystems
if (
!(hasSupervisionSubsystems && metric.disaggregatedBySupervisionSubsystems)
) {
return [metric.customFrequency || metric.defaultFrequency];
}

/**
* For supervision agencies w/ subsystems and disaggregated metrics, we find all
* enabled subsystems and return a list of their frequency and subsystem name.
*/
const supervisionSubsystemMetricFrequencies =
agencySupervisionSubsystems?.reduce((acc, subsystem) => {
const replacedSystemMetricKey = replaceSystemMetricKeyWithNewSystem(
`SUPERVISION-${metric.key}`,
subsystem
);
if (metrics[replacedSystemMetricKey].enabled) {
const frequency =
metrics[replacedSystemMetricKey].customFrequency ||
metrics[replacedSystemMetricKey].defaultFrequency;
acc.push(
`${frequency} (${removeSnakeCase(subsystem).toLocaleLowerCase()})`
);
}
return acc;
}, [] as string[]);

return supervisionSubsystemMetricFrequencies;
};

/**
* Returns a boolean that indicates whether or not a supervision agency has a
* subsystem metric with an enabled status matching the `status` argument provided
*/
const hasSupervisionSubsystemWithEnabledStatus = (
status: boolean | null,
metricKey: string | undefined
) => {
if (!metricKey) return undefined;
return !!agencySupervisionSubsystems?.find((subsystem) => {
const replacedSystemMetricKey = replaceSystemMetricKeyWithNewSystem(
`SUPERVISION-${metricKey}`,
subsystem
);
return metrics[replacedSystemMetricKey].enabled === status;
});
};

const { actionRequiredMetrics, availableMetrics, unavailableMetrics } =
metricsByCurrentSystem?.reduce(
(acc, metric) => {
// Default grouping for agencies without supervision subsystems
if (
!(
hasSupervisionSubsystems &&
metric.disaggregatedBySupervisionSubsystems
)
) {
if (metric.enabled) {
acc.availableMetrics.push(metric);
return acc;
}
if (metric.enabled === false) {
acc.unavailableMetrics.push(metric);
return acc;
}
if (metric.enabled === null) {
acc.actionRequiredMetrics.push(metric);
return acc;
}
}

/**
* For supervision agencies w/ subsystems and disaggregated metrics, we need to do
* an extra check of all of the subsystems' metric enabled status.
*
* `actionRequiredMetrics` will include the metrics if at least one subsystem has the metric as `null` (untouched)
* - currently, we will never reach this case as disaggregating a metric by default enables the subsystem metrics
* `availableMetrics` will include the metric if at least one subsystem has the metric enabled
* `unavailableMetrics` will include the metric if ALL of the subsystems have the metric disabled
*
*/
const hasSubsystemsWithEnabledMetrics =
hasSupervisionSubsystemWithEnabledStatus(true, metric.key);
const hasSubsystemsWithDisabledMetrics =
hasSupervisionSubsystemWithEnabledStatus(false, metric.key);
const hasSubsystemsWithUntoucheddMetrics =
hasSupervisionSubsystemWithEnabledStatus(null, metric.key);

if (hasSubsystemsWithEnabledMetrics) {
acc.availableMetrics.push(metric);
return acc;
}
if (hasSubsystemsWithDisabledMetrics) {
acc.unavailableMetrics.push(metric);
return acc;
}
if (hasSubsystemsWithUntoucheddMetrics) {
acc.actionRequiredMetrics.push(metric);
return acc;
}
return acc;
},
{
actionRequiredMetrics: [],
availableMetrics: [],
unavailableMetrics: [],
} as { [key: string]: MetricInfo[] }
) ?? {};

const hasActionRequiredMetrics =
actionRequiredMetrics && actionRequiredMetrics.length > 0;

const availableMetrics = getMetricsBySystem(currentSystem)?.filter(
({ metric }) => metric.enabled
);
const hasAvailableMetrics = availableMetrics && availableMetrics.length > 0;

const unavailableMetrics = getMetricsBySystem(currentSystem)?.filter(
({ metric }) => metric.enabled === false
);
const hasUnavailableMetrics =
unavailableMetrics && unavailableMetrics.length > 0;

Expand Down Expand Up @@ -145,13 +266,13 @@ export const MetricsOverview = observer(() => {
<Styled.MetricsSectionTitle isAlertCaption>
Action required
</Styled.MetricsSectionTitle>
{actionRequiredMetrics?.map(({ key, metric }) => (
{actionRequiredMetrics?.map((metric) => (
<Styled.MetricItem
key={key}
key={metric.key}
onClick={() =>
setSettingsSearchParams({
system: currentSystem,
metric: key,
metric: metric.key,
})
}
>
Expand All @@ -168,39 +289,42 @@ export const MetricsOverview = observer(() => {
<Styled.MetricsSectionTitle>
Available
</Styled.MetricsSectionTitle>
{availableMetrics?.map(({ key, metric }) => (
<Styled.MetricItem
key={key}
onClick={() =>
setSettingsSearchParams({
system: currentSystem,
metric: key,
})
}
>
<Styled.MetricItemName>
{metric.label}
<span>
{frequencyString(metric.customFrequency)?.toLowerCase()}
</span>
</Styled.MetricItemName>
<RightArrowIcon width="16px" height="16px" />
</Styled.MetricItem>
))}
{availableMetrics?.map((metric) => {
const frequency = getMetricFrequency(metric);
return (
<Styled.MetricItem
key={metric.key}
onClick={() =>
setSettingsSearchParams({
system: currentSystem,
metric: metric.key,
})
}
>
<Styled.MetricItemName>
{metric.label}
<span>
{frequencyString(frequency.join(", "))?.toLowerCase()}
</span>
</Styled.MetricItemName>
<RightArrowIcon width="16px" height="16px" />
</Styled.MetricItem>
);
})}
</Styled.MetricsSection>
)}
{hasUnavailableMetrics && (
<Styled.MetricsSection>
<Styled.MetricsSectionTitle>
Unavailable Metrics
</Styled.MetricsSectionTitle>
{unavailableMetrics?.map(({ key, metric }) => (
{unavailableMetrics?.map((metric) => (
<Styled.MetricItem
key={key}
key={metric.key}
onClick={() =>
setSettingsSearchParams({
system: currentSystem,
metric: key,
metric: metric.key,
})
}
>
Expand Down
1 change: 1 addition & 0 deletions publisher/src/components/MetricsConfiguration/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export type MetricSettings = {
};

export type MetricInfo = {
key?: string;
enabled?: boolean | null;
label?: string;
description?: Metric["description"];
Expand Down
14 changes: 2 additions & 12 deletions publisher/src/stores/MetricConfigStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import {
MetricContext,
MetricDisaggregationDimensions,
MetricDisaggregations,
ReportFrequency,
} from "@justice-counts/common/types";
import { makeAutoObservable, runInAction } from "mobx";

Expand Down Expand Up @@ -177,21 +176,12 @@ class MetricConfigStore {
MetricConfigStore.splitSystemMetricKey(systemMetricKey);

if (system.toLowerCase() === systemName.toLowerCase()) {
filteredMetrics.push({ key: metricKey, metric });
filteredMetrics.push({ key: metricKey, ...metric });
}

return filteredMetrics;
},
[] as {
key: string;
metric: {
enabled?: boolean | null;
label?: string;
description?: Metric["description"];
defaultFrequency?: ReportFrequency;
customFrequency?: Metric["custom_frequency"];
};
}[]
[] as MetricInfo[]
);

return metrics;
Expand Down

0 comments on commit 8341078

Please sign in to comment.