Skip to content

Commit

Permalink
[Publisher][Admin Panel] Add functionality to choose metrics/child ag…
Browse files Browse the repository at this point in the history
…encies during metric copy (#1282)

* Add functionality to choose metrics/child agencies during metric copy

* [Publisher][Admin Panel] Add functionality to search metrics/child agencies by sectors (#1283)

* Add functionality to search metrics/child agencies by sectors

* Add sector to metric name

* Fix default metrics key propagation

* Fixes

* Fixes

* Fix naming

* Block saving if there are no child agencies or metrics selected to copy

* FIx disabling button behavior

* Small disable save button logic refactor

* Small re-refactor of save disabled - I gave bad advice

---------

Co-authored-by: Mahmoud <mahmoud@Mahmouds-MacBook-Pro.local>
  • Loading branch information
nasaownsky and Mahmoud committed May 3, 2024
1 parent 8341078 commit 8980d24
Show file tree
Hide file tree
Showing 5 changed files with 296 additions and 52 deletions.
5 changes: 2 additions & 3 deletions publisher/src/components/AdminPanel/AdminPanel.styles.tsx
Expand Up @@ -826,17 +826,16 @@ export const GraphicLines = styled.div<{ type?: SaveConfirmationType }>`

export const WarningMessage = styled.div`
${typography.sizeCSS.small}
max-width: 350px;
max-width: 750px;
color: ${palette.solid.red};
margin-top: 5px;
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
border: 1px solid ${palette.solid.red};
border-radius: 4px;
padding: 16px;
margin-left: 12px;
margin: 5px auto 16px;
p {
text-align: center;
Expand Down
285 changes: 237 additions & 48 deletions publisher/src/components/AdminPanel/AgencyProvisioning.tsx
Expand Up @@ -77,10 +77,12 @@ export const AgencyProvisioning: React.FC<ProvisioningProps> = observer(
users,
agencies,
agenciesByID,
metrics,
systems,
agencyProvisioningUpdates,
searchableCounties,
searchableSystems,
searchableMetrics,
csgAndRecidivizUsers,
csgAndRecidivizDefaultRole,
updateAgencyName,
Expand Down Expand Up @@ -124,11 +126,20 @@ export const AgencyProvisioning: React.FC<ProvisioningProps> = observer(
? new Set(agencyProvisioningUpdates.child_agency_ids)
: new Set()
);
const [selectedChildAgencyIDsToCopy, setSelectedChildAgencyIDsToCopy] =
useState<Set<number>>(
agencyProvisioningUpdates.child_agency_ids
? new Set(agencyProvisioningUpdates.child_agency_ids)
: new Set()
);
const [selectedSystems, setSelectedSystems] = useState<Set<AgencySystem>>(
agencyProvisioningUpdates.systems
? new Set(agencyProvisioningUpdates.systems)
: new Set()
);
const [selectedMetricsKeys, setSelectedMetricsKeys] = useState<Set<string>>(
new Set()
);
const [selectedTeamMembersToAdd, setSelectedTeamMembersToAdd] = useState<
Set<number>
>(new Set());
Expand Down Expand Up @@ -174,9 +185,14 @@ export const AgencyProvisioning: React.FC<ProvisioningProps> = observer(
);

/** A list of superagencies and child agencies to select from */
const childAgencies = availableAgencies.filter(
(agency) => !agency.is_superagency
);
const childAgencies = availableAgencies
.filter((agency) => !agency.is_superagency)
.map((agency) => ({
...agency,
sectors: agency.systems.map((system) =>
removeSnakeCase(system.toLocaleLowerCase())
),
}));
const superagencies = availableAgencies.filter(
(agency) => agency.is_superagency
);
Expand Down Expand Up @@ -288,7 +304,8 @@ export const AgencyProvisioning: React.FC<ProvisioningProps> = observer(
String(agencyProvisioningUpdates.agency_id),
agencyProvisioningUpdates.name,
userStore.email,
["ALL"]
Array.from(selectedMetricsKeys),
Array.from(selectedChildAgencyIDsToCopy).map((id) => String(id))
);
}

Expand Down Expand Up @@ -474,26 +491,38 @@ export const AgencyProvisioning: React.FC<ProvisioningProps> = observer(
Object.keys(teamMemberRoleUpdates).length > 0 ||
selectedTeamMembersToAdd.size > 0 ||
selectedTeamMembersToDelete.size > 0;
/**
* An update has been made when there are child agencies or metrics selected to copy
*/
const hasChildAgenciesCopyUpdates = selectedChildAgencyIDsToCopy.size > 0;
const hasMetricsCopyUpdates = selectedMetricsKeys.size > 0;
/**
* Saving is disabled if saving is in progress OR an existing agency has made no updates to either the name, state,
* county, systems, dashboard enabled checkbox, superagency checkbox and child agencies, child agency's superagency
* selection, and team member additions/deletions/role updates, or a newly created agency has no input for both name and state.
*/
const isSaveDisabled =
!isCopySuperagencyMetricSettingsSelected && // Allows user to save if all they do is select that they want to copy superagency metric settings
(isSaveInProgress ||
const hasCopySuperagencyMetricSettingsUpdates =
hasChildAgenciesCopyUpdates && hasMetricsCopyUpdates;
const hasAgencyInfoUpdates =
hasNameUpdate ||
hasStateUpdate ||
hasCountyUpdates ||
hasSystemUpdates ||
hasDashboardEnabledStatusUpdate ||
hasIsSuperagencyUpdate ||
hasChildAgencyUpdates ||
hasSuperagencyUpdate ||
hasTeamMemberOrRoleUpdates;
const hasRequiredCreateAgencyFields =
hasNameUpdate && hasStateUpdate && hasSystems;

const isSaveDisabled = isCopySuperagencyMetricSettingsSelected
? !hasCopySuperagencyMetricSettingsUpdates
: isSaveInProgress ||
!hasSystems ||
(selectedAgency
? !hasNameUpdate &&
!hasStateUpdate &&
!hasCountyUpdates &&
!hasSystemUpdates &&
!hasDashboardEnabledStatusUpdate &&
!hasIsSuperagencyUpdate &&
!hasChildAgencyUpdates &&
!hasSuperagencyUpdate &&
!hasTeamMemberOrRoleUpdates
: !(hasNameUpdate && hasStateUpdate && hasSystems)));
? !hasAgencyInfoUpdates
: !hasRequiredCreateAgencyFields);

/** Automatically adds CSG and Recidiviz users to a newly created agency with the proper roles */
useEffect(() => {
Expand All @@ -520,12 +549,19 @@ export const AgencyProvisioning: React.FC<ProvisioningProps> = observer(
};
});
}

if (isCopySuperagencyMetricSettingsSelected && selectedIDToEdit) {
adminPanelStore.fetchAgencyMetrics(String(selectedIDToEdit));
}
}, [
selectedAgency,
adminPanelStore,
api,
csgAndRecidivizUsers,
csgAndRecidivizDefaultRole,
secondaryCreatedId,
selectedIDToEdit,
isCopySuperagencyMetricSettingsSelected,
]);

/** Here we are making the auto-adding if user was created via the secondary modal */
Expand All @@ -546,6 +582,18 @@ export const AgencyProvisioning: React.FC<ProvisioningProps> = observer(
}
}, [users, secondaryCreatedId, csgAndRecidivizDefaultRole]);

/** Here we are making the auto-selecting all metrics by default */
useEffect(() => {
setSelectedMetricsKeys(
new Set(searchableMetrics.map((metric) => String(metric.id)))
);
}, [searchableMetrics]);

const selectedChildAgencies = childAgencies.filter((agency) =>
selectedChildAgencyIDs.has(Number(agency.id))
);
const hasChildAgencyMetrics = metrics.length > 0;

return (
<Styled.ModalContainer offScreen={activeSecondaryModal === Setting.USERS}>
{showSaveConfirmation.show ? (
Expand Down Expand Up @@ -909,37 +957,178 @@ export const AgencyProvisioning: React.FC<ProvisioningProps> = observer(
</Styled.InputLabelWrapper>

{hasSystems && hasChildAgencies && (
<Styled.InputLabelWrapper flexRow wrapLabelText>
<input
id="copy-superagency-metric-settings"
name="copy-superagency-metric-settings"
type="checkbox"
onChange={() =>
setIsCopySuperagencyMetricSettingsSelected(
(prev) => !prev
)
}
checked={isCopySuperagencyMetricSettingsSelected}
/>
<label htmlFor="copy-superagency-metric-settings">
Copy all metric settings to child agencies across
all sectors
</label>
{isCopySuperagencyMetricSettingsSelected && (
<Styled.WarningMessage>
<img src={alertIcon} alt="" width="24px" />
<p>
WARNING! This action cannot be undone. This
will OVERWRITE all metric settings in all
child agencies. After clicking{" "}
<strong>Save</strong>, the copying process
will begin and you will receive an email
confirmation once the metrics settings have
been copied over.
</p>
</Styled.WarningMessage>
)}
</Styled.InputLabelWrapper>
<>
<Styled.InputLabelWrapper flexRow wrapLabelText>
<input
id="copy-superagency-metric-settings"
name="copy-superagency-metric-settings"
type="checkbox"
onChange={() => {
setIsCopySuperagencyMetricSettingsSelected(
(prev) => !prev
);
}}
checked={
isCopySuperagencyMetricSettingsSelected
}
/>
<label htmlFor="copy-superagency-metric-settings">
Copy metric settings to child agencies
</label>
</Styled.InputLabelWrapper>
{isCopySuperagencyMetricSettingsSelected &&
hasChildAgencyMetrics && (
<>
<Styled.WarningMessage>
<img src={alertIcon} alt="" width="24px" />
<p>
WARNING! This action cannot be undone.
This will OVERWRITE metric settings in
child agencies. After clicking{" "}
<strong>Save</strong>, the copying process
will begin and you will receive an email
confirmation once the metrics settings
have been copied over.
</p>
</Styled.WarningMessage>
<Styled.InputLabelWrapper>
{showSelectionBox ===
SelectionInputBoxTypes.COPY_CHILD_AGENCIES && (
<InteractiveSearchList
list={selectedChildAgencies}
boxActionType={
InteractiveSearchListActions.ADD
}
selections={
selectedChildAgencyIDsToCopy
}
buttons={getInteractiveSearchListSelectDeselectCloseButtons(
setSelectedChildAgencyIDsToCopy,
new Set(
selectedChildAgencies.map(
(agency) => +agency.id
)
)
)}
updateSelections={({ id }) => {
setSelectedChildAgencyIDsToCopy(
(prev) =>
toggleAddRemoveSetItem(prev, +id)
);
}}
searchByKeys={["name", "sectors"]}
metadata={{
listBoxLabel:
"Select child agencies to copy",
searchBoxLabel: "Search agencies",
}}
isActiveBox={
showSelectionBox ===
SelectionInputBoxTypes.COPY_CHILD_AGENCIES
}
/>
)}
<Styled.ChipContainer
onClick={() => {
setShowSelectionBox(
SelectionInputBoxTypes.COPY_CHILD_AGENCIES
);
}}
fitContentHeight
hoverable
>
{selectedChildAgencyIDsToCopy.size ===
0 ? (
<Styled.EmptyListMessage>
No child agencies selected to copy
</Styled.EmptyListMessage>
) : (
Array.from(
selectedChildAgencyIDsToCopy
).map((agencyID) => (
<Styled.Chip key={agencyID}>
{agenciesByID[agencyID]?.[0].name}
</Styled.Chip>
))
)}
</Styled.ChipContainer>
<Styled.ChipContainerLabel>
Child agencies to copy
</Styled.ChipContainerLabel>
</Styled.InputLabelWrapper>

<Styled.InputLabelWrapper>
{showSelectionBox ===
SelectionInputBoxTypes.COPY_AGENCY_METRICS && (
<InteractiveSearchList
list={searchableMetrics}
boxActionType={
InteractiveSearchListActions.ADD
}
selections={selectedMetricsKeys}
buttons={getInteractiveSearchListSelectDeselectCloseButtons(
setSelectedMetricsKeys,
new Set(
searchableMetrics.map((metric) =>
String(metric.id)
)
)
)}
updateSelections={({ id }) => {
setSelectedMetricsKeys((prev) =>
toggleAddRemoveSetItem(
prev,
String(id)
)
);
}}
searchByKeys={["name", "sectors"]}
metadata={{
listBoxLabel:
"Select metrics to copy",
searchBoxLabel: "Search metrics",
}}
isActiveBox={
showSelectionBox ===
SelectionInputBoxTypes.COPY_AGENCY_METRICS
}
/>
)}
<Styled.ChipContainer
onClick={() => {
setShowSelectionBox(
SelectionInputBoxTypes.COPY_AGENCY_METRICS
);
}}
fitContentHeight
hoverable
>
{selectedMetricsKeys.size === 0 ? (
<Styled.EmptyListMessage>
No metrics selected to copy
</Styled.EmptyListMessage>
) : (
Array.from(selectedMetricsKeys).map(
(metricKey) => (
<Styled.Chip key={metricKey}>
{
searchableMetrics.find(
(metric) =>
metric.id === metricKey
)?.name
}
</Styled.Chip>
)
)
)}
</Styled.ChipContainer>
<Styled.ChipContainerLabel>
Metrics to copy
</Styled.ChipContainerLabel>
</Styled.InputLabelWrapper>
</>
)}
</>
)}
</>
)}
Expand Down

0 comments on commit 8980d24

Please sign in to comment.