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 { IFeatureMetricsRaw } from 'interfaces/featureToggle';
|
||||||
import { Grid } from '@mui/material';
|
import { Grid } from '@mui/material';
|
||||||
import { FeatureMetricsContent } from './FeatureMetricsContent/FeatureMetricsContent';
|
import { FeatureMetricsContent } from './FeatureMetricsContent/FeatureMetricsContent';
|
||||||
import { useQueryStringNumberState } from 'hooks/useQueryStringNumberState';
|
|
||||||
import { useQueryStringState } from 'hooks/useQueryStringState';
|
|
||||||
import { FeatureMetricsChips } from './FeatureMetricsChips/FeatureMetricsChips';
|
import { FeatureMetricsChips } from './FeatureMetricsChips/FeatureMetricsChips';
|
||||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { usePageTitle } from 'hooks/usePageTitle';
|
import { usePageTitle } from 'hooks/usePageTitle';
|
||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
|
import {
|
||||||
|
ArrayParam,
|
||||||
|
NumberParam,
|
||||||
|
StringParam,
|
||||||
|
useQueryParams,
|
||||||
|
withDefault,
|
||||||
|
} from 'use-query-params';
|
||||||
|
|
||||||
export const FeatureMetrics = () => {
|
export const FeatureMetrics = () => {
|
||||||
const projectId = useRequiredPathParam('projectId');
|
const projectId = useRequiredPathParam('projectId');
|
||||||
@ -23,8 +28,18 @@ export const FeatureMetrics = () => {
|
|||||||
const applications = useFeatureMetricsApplications(featureId);
|
const applications = useFeatureMetricsApplications(featureId);
|
||||||
usePageTitle('Metrics');
|
usePageTitle('Metrics');
|
||||||
|
|
||||||
const [hoursBack = FEATURE_METRIC_HOURS_BACK_DEFAULT, setHoursBack] =
|
const defaultEnvironment = Array.from(environments)[0];
|
||||||
useQueryStringNumberState('hoursBack');
|
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);
|
const { featureMetrics } = useFeatureMetricsRaw(featureId, hoursBack);
|
||||||
|
|
||||||
// Keep a cache of the fetched metrics so that we can
|
// Keep a cache of the fetched metrics so that we can
|
||||||
@ -37,18 +52,15 @@ export const FeatureMetrics = () => {
|
|||||||
featureMetrics && setCachedMetrics(featureMetrics);
|
featureMetrics && setCachedMetrics(featureMetrics);
|
||||||
}, [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(() => {
|
const filteredMetrics = useMemo(() => {
|
||||||
return cachedMetrics
|
return cachedMetrics
|
||||||
?.filter((metric) => metric.environment === environment)
|
?.filter((metric) => selectedEnvironment === metric.environment)
|
||||||
.filter((metric) => metric.appName === application);
|
.filter((metric) => selectedApplications.includes(metric.appName));
|
||||||
}, [cachedMetrics, environment, application]);
|
}, [
|
||||||
|
cachedMetrics,
|
||||||
|
selectedEnvironment,
|
||||||
|
JSON.stringify(selectedApplications),
|
||||||
|
]);
|
||||||
|
|
||||||
if (!filteredMetrics) {
|
if (!filteredMetrics) {
|
||||||
return null;
|
return null;
|
||||||
@ -64,8 +76,10 @@ export const FeatureMetrics = () => {
|
|||||||
<FeatureMetricsChips
|
<FeatureMetricsChips
|
||||||
title='Environments'
|
title='Environments'
|
||||||
values={environments}
|
values={environments}
|
||||||
value={environment}
|
selectedValues={[selectedEnvironment]}
|
||||||
setValue={setEnvironment}
|
toggleValue={(value) => {
|
||||||
|
setQuery({ environment: value });
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -77,8 +91,24 @@ export const FeatureMetrics = () => {
|
|||||||
<FeatureMetricsChips
|
<FeatureMetricsChips
|
||||||
title='Applications'
|
title='Applications'
|
||||||
values={applications}
|
values={applications}
|
||||||
value={application}
|
selectedValues={selectedApplications}
|
||||||
setValue={setApplication}
|
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}>
|
<Grid item xs={12} md={2}>
|
||||||
<FeatureMetricsHours
|
<FeatureMetricsHours
|
||||||
hoursBack={hoursBack}
|
hoursBack={hoursBack}
|
||||||
setHoursBack={setHoursBack}
|
setHoursBack={(value) => setQuery({ hoursBack: value })}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -5,8 +5,8 @@ import { focusable } from 'themes/themeStyles';
|
|||||||
interface IFeatureMetricsChipsProps {
|
interface IFeatureMetricsChipsProps {
|
||||||
title: string;
|
title: string;
|
||||||
values: Set<string>;
|
values: Set<string>;
|
||||||
value?: string;
|
selectedValues: string[];
|
||||||
setValue: (value: string) => void;
|
toggleValue: (value: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const StyledTitle = styled('h2')(({ theme }) => ({
|
const StyledTitle = styled('h2')(({ theme }) => ({
|
||||||
@ -39,13 +39,11 @@ const StyledItem = styled('li')(({ theme }) => ({
|
|||||||
export const FeatureMetricsChips = ({
|
export const FeatureMetricsChips = ({
|
||||||
title,
|
title,
|
||||||
values,
|
values,
|
||||||
value,
|
selectedValues,
|
||||||
setValue,
|
toggleValue,
|
||||||
}: IFeatureMetricsChipsProps) => {
|
}: IFeatureMetricsChipsProps) => {
|
||||||
const onClick = (value: string) => () => {
|
const onClick = (value: string) => () => {
|
||||||
if (values.has(value)) {
|
toggleValue(value);
|
||||||
setValue(value);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const sortedValues = useMemo(() => {
|
const sortedValues = useMemo(() => {
|
||||||
@ -63,7 +61,7 @@ export const FeatureMetricsChips = ({
|
|||||||
<Chip
|
<Chip
|
||||||
label={val}
|
label={val}
|
||||||
onClick={onClick(val)}
|
onClick={onClick(val)}
|
||||||
aria-pressed={val === value}
|
aria-pressed={selectedValues?.includes(val)}
|
||||||
sx={focusable}
|
sx={focusable}
|
||||||
/>
|
/>
|
||||||
</StyledItem>
|
</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