mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02: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