mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-14 00:19:16 +01:00
feat: select multiple apps (#5860)
This commit is contained in:
parent
e3fc4b51fa
commit
6ba4591c7f
@ -8,13 +8,18 @@ import {
|
||||
import { IFeatureMetricsRaw } from 'interfaces/featureToggle';
|
||||
import { Grid } from '@mui/material';
|
||||
import { FeatureMetricsContent } from './FeatureMetricsContent/FeatureMetricsContent';
|
||||
import { useQueryStringNumberState } from 'hooks/useQueryStringNumberState';
|
||||
import { useQueryStringState } from 'hooks/useQueryStringState';
|
||||
import { FeatureMetricsChips } from './FeatureMetricsChips/FeatureMetricsChips';
|
||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { usePageTitle } from 'hooks/usePageTitle';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
import {
|
||||
ArrayParam,
|
||||
NumberParam,
|
||||
StringParam,
|
||||
useQueryParams,
|
||||
withDefault,
|
||||
} from 'use-query-params';
|
||||
|
||||
export const FeatureMetrics = () => {
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
@ -23,8 +28,18 @@ export const FeatureMetrics = () => {
|
||||
const applications = useFeatureMetricsApplications(featureId);
|
||||
usePageTitle('Metrics');
|
||||
|
||||
const [hoursBack = FEATURE_METRIC_HOURS_BACK_DEFAULT, setHoursBack] =
|
||||
useQueryStringNumberState('hoursBack');
|
||||
const defaultEnvironment = Array.from(environments)[0];
|
||||
const defaultApplication = Array.from(applications)[0];
|
||||
const [query, setQuery] = useQueryParams({
|
||||
environment: withDefault(StringParam, defaultEnvironment),
|
||||
applications: withDefault(ArrayParam, [defaultApplication]),
|
||||
hoursBack: withDefault(NumberParam, FEATURE_METRIC_HOURS_BACK_DEFAULT),
|
||||
});
|
||||
const { environment: selectedEnvironment, hoursBack } = query;
|
||||
const selectedApplications = query.applications.filter(
|
||||
(item) => item !== null,
|
||||
) as string[];
|
||||
|
||||
const { featureMetrics } = useFeatureMetricsRaw(featureId, hoursBack);
|
||||
|
||||
// Keep a cache of the fetched metrics so that we can
|
||||
@ -37,18 +52,15 @@ export const FeatureMetrics = () => {
|
||||
featureMetrics && setCachedMetrics(featureMetrics);
|
||||
}, [featureMetrics]);
|
||||
|
||||
const defaultEnvironment = Array.from(environments)[0];
|
||||
const defaultApplication = Array.from(applications)[0];
|
||||
const [environment = defaultEnvironment, setEnvironment] =
|
||||
useQueryStringState('environment');
|
||||
const [application = defaultApplication, setApplication] =
|
||||
useQueryStringState('application');
|
||||
|
||||
const filteredMetrics = useMemo(() => {
|
||||
return cachedMetrics
|
||||
?.filter((metric) => metric.environment === environment)
|
||||
.filter((metric) => metric.appName === application);
|
||||
}, [cachedMetrics, environment, application]);
|
||||
?.filter((metric) => selectedEnvironment === metric.environment)
|
||||
.filter((metric) => selectedApplications.includes(metric.appName));
|
||||
}, [
|
||||
cachedMetrics,
|
||||
selectedEnvironment,
|
||||
JSON.stringify(selectedApplications),
|
||||
]);
|
||||
|
||||
if (!filteredMetrics) {
|
||||
return null;
|
||||
@ -64,8 +76,10 @@ export const FeatureMetrics = () => {
|
||||
<FeatureMetricsChips
|
||||
title='Environments'
|
||||
values={environments}
|
||||
value={environment}
|
||||
setValue={setEnvironment}
|
||||
selectedValues={[selectedEnvironment]}
|
||||
toggleValue={(value) => {
|
||||
setQuery({ environment: value });
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
@ -77,8 +91,24 @@ export const FeatureMetrics = () => {
|
||||
<FeatureMetricsChips
|
||||
title='Applications'
|
||||
values={applications}
|
||||
value={application}
|
||||
setValue={setApplication}
|
||||
selectedValues={selectedApplications}
|
||||
toggleValue={(value) => {
|
||||
if (selectedApplications.includes(value)) {
|
||||
setQuery({
|
||||
applications:
|
||||
selectedApplications.filter(
|
||||
(app) => app !== value,
|
||||
),
|
||||
});
|
||||
} else {
|
||||
setQuery({
|
||||
applications: [
|
||||
...selectedApplications,
|
||||
value,
|
||||
],
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
@ -86,7 +116,7 @@ export const FeatureMetrics = () => {
|
||||
<Grid item xs={12} md={2}>
|
||||
<FeatureMetricsHours
|
||||
hoursBack={hoursBack}
|
||||
setHoursBack={setHoursBack}
|
||||
setHoursBack={(value) => setQuery({ hoursBack: value })}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
@ -5,8 +5,8 @@ import { focusable } from 'themes/themeStyles';
|
||||
interface IFeatureMetricsChipsProps {
|
||||
title: string;
|
||||
values: Set<string>;
|
||||
value?: string;
|
||||
setValue: (value: string) => void;
|
||||
selectedValues: string[];
|
||||
toggleValue: (value: string) => void;
|
||||
}
|
||||
|
||||
const StyledTitle = styled('h2')(({ theme }) => ({
|
||||
@ -39,13 +39,11 @@ const StyledItem = styled('li')(({ theme }) => ({
|
||||
export const FeatureMetricsChips = ({
|
||||
title,
|
||||
values,
|
||||
value,
|
||||
setValue,
|
||||
selectedValues,
|
||||
toggleValue,
|
||||
}: IFeatureMetricsChipsProps) => {
|
||||
const onClick = (value: string) => () => {
|
||||
if (values.has(value)) {
|
||||
setValue(value);
|
||||
}
|
||||
toggleValue(value);
|
||||
};
|
||||
|
||||
const sortedValues = useMemo(() => {
|
||||
@ -63,7 +61,7 @@ export const FeatureMetricsChips = ({
|
||||
<Chip
|
||||
label={val}
|
||||
onClick={onClick(val)}
|
||||
aria-pressed={val === value}
|
||||
aria-pressed={selectedValues?.includes(val)}
|
||||
sx={focusable}
|
||||
/>
|
||||
</StyledItem>
|
||||
|
@ -1,19 +0,0 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useQueryStringState } from './useQueryStringState';
|
||||
|
||||
// Store a number in the query string. Call setState to update the query string.
|
||||
export const useQueryStringNumberState = (
|
||||
key: string,
|
||||
): [number | undefined, (value: number) => void] => {
|
||||
const [value, setValue] = useQueryStringState(key);
|
||||
|
||||
const setState = useCallback(
|
||||
(value: number) => setValue(String(value)),
|
||||
[setValue],
|
||||
);
|
||||
|
||||
return [
|
||||
Number.isFinite(Number(value)) ? Number(value) : undefined,
|
||||
setState,
|
||||
];
|
||||
};
|
@ -1,25 +0,0 @@
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
// Store a value in the query string. Call setState to update the query string.
|
||||
export const useQueryStringState = (
|
||||
key: string,
|
||||
): [string | undefined, (value: string) => void] => {
|
||||
const { search } = window.location;
|
||||
const navigate = useNavigate();
|
||||
|
||||
const params = useMemo(() => {
|
||||
return new URLSearchParams(search);
|
||||
}, [search]);
|
||||
|
||||
const setState = useCallback(
|
||||
(value: string) => {
|
||||
const next = new URLSearchParams(search);
|
||||
next.set(key, value);
|
||||
navigate({ search: next.toString() }, { replace: true });
|
||||
},
|
||||
[key, search, navigate],
|
||||
);
|
||||
|
||||
return [params.get(key) || undefined, setState];
|
||||
};
|
Loading…
Reference in New Issue
Block a user