mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: rename impact metrics fields to match prometheus and grafana (#10616)
This commit is contained in:
		
							parent
							
								
									6a8a6e2373
								
							
						
					
					
						commit
						4a00792f1e
					
				@ -123,10 +123,10 @@ export const ChartConfigModal: FC<ChartConfigModalProps> = ({
 | 
			
		||||
                        <Box sx={(theme) => ({ padding: theme.spacing(1) })}>
 | 
			
		||||
                            <ImpactMetricsChart
 | 
			
		||||
                                key={screenBreakpoint ? 'small' : 'large'}
 | 
			
		||||
                                selectedSeries={formData.selectedSeries}
 | 
			
		||||
                                selectedRange={formData.selectedRange}
 | 
			
		||||
                                selectedLabels={formData.selectedLabels}
 | 
			
		||||
                                beginAtZero={formData.beginAtZero}
 | 
			
		||||
                                metricName={formData.metricName}
 | 
			
		||||
                                timeRange={formData.timeRange}
 | 
			
		||||
                                labelSelectors={formData.labelSelectors}
 | 
			
		||||
                                yAxisMin={formData.yAxisMin}
 | 
			
		||||
                                aggregationMode={formData.aggregationMode}
 | 
			
		||||
                                isPreview
 | 
			
		||||
                            />
 | 
			
		||||
@ -136,8 +136,8 @@ export const ChartConfigModal: FC<ChartConfigModalProps> = ({
 | 
			
		||||
 | 
			
		||||
                {currentAvailableLabels ? (
 | 
			
		||||
                    <LabelsFilter
 | 
			
		||||
                        selectedLabels={formData.selectedLabels}
 | 
			
		||||
                        onChange={actions.setSelectedLabels}
 | 
			
		||||
                        labelSelectors={formData.labelSelectors}
 | 
			
		||||
                        onChange={actions.setLabelSelectors}
 | 
			
		||||
                        availableLabels={currentAvailableLabels}
 | 
			
		||||
                    />
 | 
			
		||||
                ) : null}
 | 
			
		||||
 | 
			
		||||
@ -12,9 +12,9 @@ export type ImpactMetricsControlsProps = {
 | 
			
		||||
    actions: Pick<
 | 
			
		||||
        ChartFormState['actions'],
 | 
			
		||||
        | 'handleSeriesChange'
 | 
			
		||||
        | 'setSelectedRange'
 | 
			
		||||
        | 'setBeginAtZero'
 | 
			
		||||
        | 'setSelectedLabels'
 | 
			
		||||
        | 'setTimeRange'
 | 
			
		||||
        | 'setYAxisMin'
 | 
			
		||||
        | 'setLabelSelectors'
 | 
			
		||||
        | 'setAggregationMode'
 | 
			
		||||
    >;
 | 
			
		||||
    metricSeries: (ImpactMetricsSeries & { name: string })[];
 | 
			
		||||
@ -43,34 +43,36 @@ export const ImpactMetricsControls: FC<ImpactMetricsControlsProps> = ({
 | 
			
		||||
            </Typography>
 | 
			
		||||
 | 
			
		||||
            <SeriesSelector
 | 
			
		||||
                value={formData.selectedSeries}
 | 
			
		||||
                value={formData.metricName}
 | 
			
		||||
                onChange={actions.handleSeriesChange}
 | 
			
		||||
                options={metricSeries}
 | 
			
		||||
                loading={loading}
 | 
			
		||||
            />
 | 
			
		||||
 | 
			
		||||
            {formData.selectedSeries ? (
 | 
			
		||||
            {formData.metricName ? (
 | 
			
		||||
                <>
 | 
			
		||||
                    <RangeSelector
 | 
			
		||||
                        value={formData.selectedRange}
 | 
			
		||||
                        onChange={actions.setSelectedRange}
 | 
			
		||||
                        value={formData.timeRange}
 | 
			
		||||
                        onChange={actions.setTimeRange}
 | 
			
		||||
                    />
 | 
			
		||||
                    <ModeSelector
 | 
			
		||||
                        value={formData.aggregationMode}
 | 
			
		||||
                        onChange={actions.setAggregationMode}
 | 
			
		||||
                        seriesType={getMetricType(formData.selectedSeries)!}
 | 
			
		||||
                        seriesType={getMetricType(formData.metricName)!}
 | 
			
		||||
                    />
 | 
			
		||||
                </>
 | 
			
		||||
            ) : null}
 | 
			
		||||
        </Box>
 | 
			
		||||
        {formData.selectedSeries ? (
 | 
			
		||||
        {formData.metricName ? (
 | 
			
		||||
            <FormControlLabel
 | 
			
		||||
                sx={(theme) => ({ margin: theme.spacing(1.5, 0) })}
 | 
			
		||||
                control={
 | 
			
		||||
                    <Checkbox
 | 
			
		||||
                        checked={formData.beginAtZero}
 | 
			
		||||
                        checked={formData.yAxisMin === 'zero'}
 | 
			
		||||
                        onChange={(e) =>
 | 
			
		||||
                            actions.setBeginAtZero(e.target.checked)
 | 
			
		||||
                            actions.setYAxisMin(
 | 
			
		||||
                                e.target.checked ? 'zero' : 'auto',
 | 
			
		||||
                            )
 | 
			
		||||
                        }
 | 
			
		||||
                    />
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@ -4,18 +4,18 @@ import type { ImpactMetricsLabels } from 'hooks/api/getters/useImpactMetricsData
 | 
			
		||||
import { LabelFilterItem } from './LabelFilterItem/LabelFilterItem.tsx';
 | 
			
		||||
 | 
			
		||||
export type LabelsFilterProps = {
 | 
			
		||||
    selectedLabels: Record<string, string[]>;
 | 
			
		||||
    labelSelectors: Record<string, string[]>;
 | 
			
		||||
    onChange: (labels: Record<string, string[]>) => void;
 | 
			
		||||
    availableLabels: ImpactMetricsLabels;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const LabelsFilter: FC<LabelsFilterProps> = ({
 | 
			
		||||
    selectedLabels,
 | 
			
		||||
    labelSelectors,
 | 
			
		||||
    onChange,
 | 
			
		||||
    availableLabels,
 | 
			
		||||
}) => {
 | 
			
		||||
    const handleLabelChange = (labelKey: string, values: string[]) => {
 | 
			
		||||
        const newLabels = { ...selectedLabels };
 | 
			
		||||
        const newLabels = { ...labelSelectors };
 | 
			
		||||
        if (values.length === 0) {
 | 
			
		||||
            delete newLabels[labelKey];
 | 
			
		||||
        } else {
 | 
			
		||||
@ -25,7 +25,7 @@ export const LabelsFilter: FC<LabelsFilterProps> = ({
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const handleAllToggle = (labelKey: string, checked: boolean) => {
 | 
			
		||||
        const newLabels = { ...selectedLabels };
 | 
			
		||||
        const newLabels = { ...labelSelectors };
 | 
			
		||||
        if (checked) {
 | 
			
		||||
            newLabels[labelKey] = ['*'];
 | 
			
		||||
        } else {
 | 
			
		||||
@ -53,7 +53,7 @@ export const LabelsFilter: FC<LabelsFilterProps> = ({
 | 
			
		||||
                }}
 | 
			
		||||
            >
 | 
			
		||||
                <Typography variant='subtitle2'>Filter by labels</Typography>
 | 
			
		||||
                {Object.keys(selectedLabels).length > 0 && (
 | 
			
		||||
                {Object.keys(labelSelectors).length > 0 && (
 | 
			
		||||
                    <Chip
 | 
			
		||||
                        label='Clear all'
 | 
			
		||||
                        size='small'
 | 
			
		||||
@ -75,7 +75,7 @@ export const LabelsFilter: FC<LabelsFilterProps> = ({
 | 
			
		||||
                {Object.entries(availableLabels)
 | 
			
		||||
                    .sort()
 | 
			
		||||
                    .map(([labelKey, values]) => {
 | 
			
		||||
                        const currentSelection = selectedLabels[labelKey] || [];
 | 
			
		||||
                        const currentSelection = labelSelectors[labelKey] || [];
 | 
			
		||||
 | 
			
		||||
                        return (
 | 
			
		||||
                            <Box
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,7 @@ const getConfigDescription = (config: DisplayChartConfig): string => {
 | 
			
		||||
        parts.push(`${config.displayName}`);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    parts.push(`last ${config.selectedRange}`);
 | 
			
		||||
    parts.push(`last ${config.timeRange}`);
 | 
			
		||||
 | 
			
		||||
    if (config.aggregationMode === 'rps') {
 | 
			
		||||
        parts.push('rate per second');
 | 
			
		||||
@ -35,7 +35,7 @@ const getConfigDescription = (config: DisplayChartConfig): string => {
 | 
			
		||||
        parts.push('sum');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const labelCount = Object.keys(config.selectedLabels).length;
 | 
			
		||||
    const labelCount = Object.keys(config.labelSelectors).length;
 | 
			
		||||
    if (labelCount > 0) {
 | 
			
		||||
        parts.push(`${labelCount} filter${labelCount > 1 ? 's' : ''}`);
 | 
			
		||||
    }
 | 
			
		||||
@ -147,10 +147,10 @@ export const ChartItem: FC<ChartItemProps> = ({
 | 
			
		||||
        <StyledChartContent>
 | 
			
		||||
            <StyledImpactChartContainer>
 | 
			
		||||
                <ImpactMetricsChart
 | 
			
		||||
                    selectedSeries={config.selectedSeries}
 | 
			
		||||
                    selectedRange={config.selectedRange}
 | 
			
		||||
                    selectedLabels={config.selectedLabels}
 | 
			
		||||
                    beginAtZero={config.beginAtZero}
 | 
			
		||||
                    metricName={config.metricName}
 | 
			
		||||
                    timeRange={config.timeRange}
 | 
			
		||||
                    labelSelectors={config.labelSelectors}
 | 
			
		||||
                    yAxisMin={config.yAxisMin}
 | 
			
		||||
                    aggregationMode={config.aggregationMode}
 | 
			
		||||
                    aspectRatio={1.5}
 | 
			
		||||
                    overrideOptions={{ maintainAspectRatio: false }}
 | 
			
		||||
 | 
			
		||||
@ -13,10 +13,10 @@ import { useChartData } from './hooks/useChartData.ts';
 | 
			
		||||
import type { AggregationMode } from './types.ts';
 | 
			
		||||
 | 
			
		||||
type ImpactMetricsChartProps = {
 | 
			
		||||
    selectedSeries: string;
 | 
			
		||||
    selectedRange: 'hour' | 'day' | 'week' | 'month';
 | 
			
		||||
    selectedLabels: Record<string, string[]>;
 | 
			
		||||
    beginAtZero: boolean;
 | 
			
		||||
    metricName: string;
 | 
			
		||||
    timeRange: 'hour' | 'day' | 'week' | 'month';
 | 
			
		||||
    labelSelectors: Record<string, string[]>;
 | 
			
		||||
    yAxisMin: 'auto' | 'zero';
 | 
			
		||||
    aggregationMode?: AggregationMode;
 | 
			
		||||
    aspectRatio?: number;
 | 
			
		||||
    overrideOptions?: Record<string, unknown>;
 | 
			
		||||
@ -27,10 +27,10 @@ type ImpactMetricsChartProps = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const ImpactMetricsChart: FC<ImpactMetricsChartProps> = ({
 | 
			
		||||
    selectedSeries,
 | 
			
		||||
    selectedRange,
 | 
			
		||||
    selectedLabels,
 | 
			
		||||
    beginAtZero,
 | 
			
		||||
    metricName,
 | 
			
		||||
    timeRange,
 | 
			
		||||
    labelSelectors,
 | 
			
		||||
    yAxisMin,
 | 
			
		||||
    aggregationMode,
 | 
			
		||||
    aspectRatio,
 | 
			
		||||
    overrideOptions = {},
 | 
			
		||||
@ -44,14 +44,14 @@ export const ImpactMetricsChart: FC<ImpactMetricsChartProps> = ({
 | 
			
		||||
        loading: dataLoading,
 | 
			
		||||
        error: dataError,
 | 
			
		||||
    } = useImpactMetricsData(
 | 
			
		||||
        selectedSeries
 | 
			
		||||
        metricName
 | 
			
		||||
            ? {
 | 
			
		||||
                  series: selectedSeries,
 | 
			
		||||
                  range: selectedRange,
 | 
			
		||||
                  series: metricName,
 | 
			
		||||
                  range: timeRange,
 | 
			
		||||
                  aggregationMode,
 | 
			
		||||
                  labels:
 | 
			
		||||
                      Object.keys(selectedLabels).length > 0
 | 
			
		||||
                          ? selectedLabels
 | 
			
		||||
                      Object.keys(labelSelectors).length > 0
 | 
			
		||||
                          ? labelSelectors
 | 
			
		||||
                          : undefined,
 | 
			
		||||
              }
 | 
			
		||||
            : undefined,
 | 
			
		||||
@ -66,7 +66,7 @@ export const ImpactMetricsChart: FC<ImpactMetricsChartProps> = ({
 | 
			
		||||
 | 
			
		||||
    const hasError = !!dataError;
 | 
			
		||||
    const isLoading = dataLoading;
 | 
			
		||||
    const shouldShowPlaceholder = !selectedSeries || isLoading || hasError;
 | 
			
		||||
    const shouldShowPlaceholder = !metricName || isLoading || hasError;
 | 
			
		||||
    const notEnoughData = useMemo(
 | 
			
		||||
        () =>
 | 
			
		||||
            !isLoading &&
 | 
			
		||||
@ -81,7 +81,7 @@ export const ImpactMetricsChart: FC<ImpactMetricsChartProps> = ({
 | 
			
		||||
        : undefined;
 | 
			
		||||
    const maxTime = end ? fromUnixTime(Number.parseInt(end, 10)) : undefined;
 | 
			
		||||
 | 
			
		||||
    const placeholder = selectedSeries ? (
 | 
			
		||||
    const placeholder = metricName ? (
 | 
			
		||||
        <NotEnoughData description={emptyDataDescription} />
 | 
			
		||||
    ) : noSeriesPlaceholder ? (
 | 
			
		||||
        noSeriesPlaceholder
 | 
			
		||||
@ -92,7 +92,7 @@ export const ImpactMetricsChart: FC<ImpactMetricsChartProps> = ({
 | 
			
		||||
        />
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const hasManyLabels = Object.keys(selectedLabels).length > 0;
 | 
			
		||||
    const hasManyLabels = Object.keys(labelSelectors).length > 0;
 | 
			
		||||
 | 
			
		||||
    const cover = notEnoughData ? placeholder : isLoading;
 | 
			
		||||
 | 
			
		||||
@ -106,10 +106,10 @@ export const ImpactMetricsChart: FC<ImpactMetricsChartProps> = ({
 | 
			
		||||
                      min: minTime?.getTime(),
 | 
			
		||||
                      max: maxTime?.getTime(),
 | 
			
		||||
                      time: {
 | 
			
		||||
                          unit: getTimeUnit(selectedRange),
 | 
			
		||||
                          unit: getTimeUnit(timeRange),
 | 
			
		||||
                          displayFormats: {
 | 
			
		||||
                              [getTimeUnit(selectedRange)]:
 | 
			
		||||
                                  getDisplayFormat(selectedRange),
 | 
			
		||||
                              [getTimeUnit(timeRange)]:
 | 
			
		||||
                                  getDisplayFormat(timeRange),
 | 
			
		||||
                          },
 | 
			
		||||
                          tooltipFormat: 'PPpp',
 | 
			
		||||
                      },
 | 
			
		||||
@ -120,7 +120,7 @@ export const ImpactMetricsChart: FC<ImpactMetricsChartProps> = ({
 | 
			
		||||
                      },
 | 
			
		||||
                  },
 | 
			
		||||
                  y: {
 | 
			
		||||
                      beginAtZero,
 | 
			
		||||
                      beginAtZero: yAxisMin === 'zero',
 | 
			
		||||
                      title: {
 | 
			
		||||
                          display: aggregationMode === 'rps',
 | 
			
		||||
                          text:
 | 
			
		||||
 | 
			
		||||
@ -12,19 +12,19 @@ type UseChartConfigParams = {
 | 
			
		||||
export type ChartFormState = {
 | 
			
		||||
    formData: {
 | 
			
		||||
        title: string;
 | 
			
		||||
        selectedSeries: string;
 | 
			
		||||
        selectedRange: 'hour' | 'day' | 'week' | 'month';
 | 
			
		||||
        beginAtZero: boolean;
 | 
			
		||||
        metricName: string;
 | 
			
		||||
        timeRange: 'hour' | 'day' | 'week' | 'month';
 | 
			
		||||
        yAxisMin: 'auto' | 'zero';
 | 
			
		||||
        aggregationMode: AggregationMode;
 | 
			
		||||
        selectedLabels: Record<string, string[]>;
 | 
			
		||||
        labelSelectors: Record<string, string[]>;
 | 
			
		||||
    };
 | 
			
		||||
    actions: {
 | 
			
		||||
        setTitle: (title: string) => void;
 | 
			
		||||
        setSelectedSeries: (series: string) => void;
 | 
			
		||||
        setSelectedRange: (range: 'hour' | 'day' | 'week' | 'month') => void;
 | 
			
		||||
        setBeginAtZero: (beginAtZero: boolean) => void;
 | 
			
		||||
        setMetricName: (series: string) => void;
 | 
			
		||||
        setTimeRange: (range: 'hour' | 'day' | 'week' | 'month') => void;
 | 
			
		||||
        setYAxisMin: (yAxisMin: 'auto' | 'zero') => void;
 | 
			
		||||
        setAggregationMode: (mode: AggregationMode) => void;
 | 
			
		||||
        setSelectedLabels: (labels: Record<string, string[]>) => void;
 | 
			
		||||
        setLabelSelectors: (labels: Record<string, string[]>) => void;
 | 
			
		||||
        handleSeriesChange: (series: string) => void;
 | 
			
		||||
        getConfigToSave: () => Omit<ChartConfig, 'id'>;
 | 
			
		||||
    };
 | 
			
		||||
@ -37,20 +37,18 @@ export const useChartFormState = ({
 | 
			
		||||
    initialConfig,
 | 
			
		||||
}: UseChartConfigParams): ChartFormState => {
 | 
			
		||||
    const [title, setTitle] = useState(initialConfig?.title || '');
 | 
			
		||||
    const [selectedSeries, setSelectedSeries] = useState(
 | 
			
		||||
        initialConfig?.selectedSeries || '',
 | 
			
		||||
    const [metricName, setMetricName] = useState(
 | 
			
		||||
        initialConfig?.metricName || '',
 | 
			
		||||
    );
 | 
			
		||||
    const [selectedRange, setSelectedRange] = useState<
 | 
			
		||||
    const [timeRange, setTimeRange] = useState<
 | 
			
		||||
        'hour' | 'day' | 'week' | 'month'
 | 
			
		||||
    >(initialConfig?.selectedRange || 'day');
 | 
			
		||||
    const [beginAtZero, setBeginAtZero] = useState(
 | 
			
		||||
        initialConfig?.beginAtZero || false,
 | 
			
		||||
    );
 | 
			
		||||
    const [selectedLabels, setSelectedLabels] = useState<
 | 
			
		||||
    >(initialConfig?.timeRange || 'day');
 | 
			
		||||
    const [yAxisMin, setYAxisMin] = useState(initialConfig?.yAxisMin || 'auto');
 | 
			
		||||
    const [labelSelectors, setLabelSelectors] = useState<
 | 
			
		||||
        Record<string, string[]>
 | 
			
		||||
    >(initialConfig?.selectedLabels || {});
 | 
			
		||||
    >(initialConfig?.labelSelectors || {});
 | 
			
		||||
    const [aggregationMode, setAggregationMode] = useState<AggregationMode>(
 | 
			
		||||
        (initialConfig?.aggregationMode || getMetricType(selectedSeries)) ===
 | 
			
		||||
        (initialConfig?.aggregationMode || getMetricType(metricName)) ===
 | 
			
		||||
            'counter'
 | 
			
		||||
            ? 'count'
 | 
			
		||||
            : 'avg',
 | 
			
		||||
@ -59,10 +57,10 @@ export const useChartFormState = ({
 | 
			
		||||
    const {
 | 
			
		||||
        data: { labels: currentAvailableLabels },
 | 
			
		||||
    } = useImpactMetricsData(
 | 
			
		||||
        selectedSeries
 | 
			
		||||
        metricName
 | 
			
		||||
            ? {
 | 
			
		||||
                  series: selectedSeries,
 | 
			
		||||
                  range: selectedRange,
 | 
			
		||||
                  series: metricName,
 | 
			
		||||
                  range: timeRange,
 | 
			
		||||
                  aggregationMode,
 | 
			
		||||
              }
 | 
			
		||||
            : undefined,
 | 
			
		||||
@ -71,29 +69,29 @@ export const useChartFormState = ({
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        if (open && initialConfig) {
 | 
			
		||||
            setTitle(initialConfig.title || '');
 | 
			
		||||
            setSelectedSeries(initialConfig.selectedSeries);
 | 
			
		||||
            setSelectedRange(initialConfig.selectedRange);
 | 
			
		||||
            setBeginAtZero(initialConfig.beginAtZero);
 | 
			
		||||
            setSelectedLabels(initialConfig.selectedLabels);
 | 
			
		||||
            setMetricName(initialConfig.metricName);
 | 
			
		||||
            setTimeRange(initialConfig.timeRange);
 | 
			
		||||
            setYAxisMin(initialConfig.yAxisMin);
 | 
			
		||||
            setLabelSelectors(initialConfig.labelSelectors);
 | 
			
		||||
            setAggregationMode(
 | 
			
		||||
                initialConfig.aggregationMode ||
 | 
			
		||||
                    (getMetricType(initialConfig.selectedSeries) === 'counter'
 | 
			
		||||
                    (getMetricType(initialConfig.metricName) === 'counter'
 | 
			
		||||
                        ? 'count'
 | 
			
		||||
                        : 'avg'),
 | 
			
		||||
            );
 | 
			
		||||
        } else if (open && !initialConfig) {
 | 
			
		||||
            setTitle('');
 | 
			
		||||
            setSelectedSeries('');
 | 
			
		||||
            setSelectedRange('day');
 | 
			
		||||
            setBeginAtZero(false);
 | 
			
		||||
            setSelectedLabels({});
 | 
			
		||||
            setMetricName('');
 | 
			
		||||
            setTimeRange('day');
 | 
			
		||||
            setYAxisMin('auto');
 | 
			
		||||
            setLabelSelectors({});
 | 
			
		||||
            setAggregationMode('count');
 | 
			
		||||
        }
 | 
			
		||||
    }, [open, initialConfig]);
 | 
			
		||||
 | 
			
		||||
    const handleSeriesChange = (series: string) => {
 | 
			
		||||
        setSelectedSeries(series);
 | 
			
		||||
        setSelectedLabels({});
 | 
			
		||||
        setMetricName(series);
 | 
			
		||||
        setLabelSelectors({});
 | 
			
		||||
        const metric = getMetricType(series);
 | 
			
		||||
        if (metric === 'counter') {
 | 
			
		||||
            setAggregationMode('count');
 | 
			
		||||
@ -104,31 +102,31 @@ export const useChartFormState = ({
 | 
			
		||||
 | 
			
		||||
    const getConfigToSave = (): Omit<ChartConfig, 'id'> => ({
 | 
			
		||||
        title: title || undefined,
 | 
			
		||||
        selectedSeries,
 | 
			
		||||
        selectedRange,
 | 
			
		||||
        beginAtZero,
 | 
			
		||||
        selectedLabels,
 | 
			
		||||
        metricName,
 | 
			
		||||
        timeRange,
 | 
			
		||||
        yAxisMin,
 | 
			
		||||
        labelSelectors,
 | 
			
		||||
        aggregationMode,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const isValid = selectedSeries.length > 0;
 | 
			
		||||
    const isValid = metricName.length > 0;
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        formData: {
 | 
			
		||||
            title,
 | 
			
		||||
            selectedSeries,
 | 
			
		||||
            selectedRange,
 | 
			
		||||
            beginAtZero,
 | 
			
		||||
            metricName,
 | 
			
		||||
            timeRange,
 | 
			
		||||
            yAxisMin,
 | 
			
		||||
            aggregationMode,
 | 
			
		||||
            selectedLabels,
 | 
			
		||||
            labelSelectors,
 | 
			
		||||
        },
 | 
			
		||||
        actions: {
 | 
			
		||||
            setTitle,
 | 
			
		||||
            setSelectedSeries,
 | 
			
		||||
            setSelectedRange,
 | 
			
		||||
            setBeginAtZero,
 | 
			
		||||
            setMetricName,
 | 
			
		||||
            setTimeRange,
 | 
			
		||||
            setYAxisMin,
 | 
			
		||||
            setAggregationMode,
 | 
			
		||||
            setSelectedLabels,
 | 
			
		||||
            setLabelSelectors,
 | 
			
		||||
            handleSeriesChange,
 | 
			
		||||
            getConfigToSave,
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
@ -34,11 +34,11 @@ const TestComponent: FC<{
 | 
			
		||||
                    data-testid='add-chart'
 | 
			
		||||
                    onClick={() =>
 | 
			
		||||
                        addChart({
 | 
			
		||||
                            selectedSeries: 'test-series',
 | 
			
		||||
                            selectedRange: 'day',
 | 
			
		||||
                            beginAtZero: true,
 | 
			
		||||
                            metricName: 'test-series',
 | 
			
		||||
                            timeRange: 'day',
 | 
			
		||||
                            yAxisMin: 'zero',
 | 
			
		||||
                            aggregationMode: 'count',
 | 
			
		||||
                            selectedLabels: {},
 | 
			
		||||
                            labelSelectors: {},
 | 
			
		||||
                            title: 'Test Chart',
 | 
			
		||||
                        })
 | 
			
		||||
                    }
 | 
			
		||||
@ -76,11 +76,11 @@ const mockSettings: ImpactMetricsState = {
 | 
			
		||||
    charts: [
 | 
			
		||||
        {
 | 
			
		||||
            id: 'test-chart',
 | 
			
		||||
            selectedSeries: 'test-series',
 | 
			
		||||
            selectedRange: 'day' as const,
 | 
			
		||||
            beginAtZero: true,
 | 
			
		||||
            metricName: 'test-series',
 | 
			
		||||
            timeRange: 'day' as const,
 | 
			
		||||
            yAxisMin: 'zero',
 | 
			
		||||
            aggregationMode: 'count',
 | 
			
		||||
            selectedLabels: {},
 | 
			
		||||
            labelSelectors: {},
 | 
			
		||||
            title: 'Test Chart',
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
@ -179,11 +179,11 @@ describe('useImpactMetricsState', () => {
 | 
			
		||||
                charts: [
 | 
			
		||||
                    {
 | 
			
		||||
                        id: 'new-chart-id',
 | 
			
		||||
                        selectedSeries: 'test-series',
 | 
			
		||||
                        selectedRange: 'day',
 | 
			
		||||
                        beginAtZero: true,
 | 
			
		||||
                        metricName: 'test-series',
 | 
			
		||||
                        timeRange: 'day',
 | 
			
		||||
                        yAxisMin: 'zero',
 | 
			
		||||
                        mode: 'count',
 | 
			
		||||
                        selectedLabels: {},
 | 
			
		||||
                        labelSelectors: {},
 | 
			
		||||
                        title: 'Test Chart',
 | 
			
		||||
                    },
 | 
			
		||||
                ],
 | 
			
		||||
@ -212,11 +212,11 @@ describe('useImpactMetricsState', () => {
 | 
			
		||||
                charts: [
 | 
			
		||||
                    {
 | 
			
		||||
                        id: 'new-chart-id',
 | 
			
		||||
                        selectedSeries: 'test-series',
 | 
			
		||||
                        selectedRange: 'day',
 | 
			
		||||
                        beginAtZero: true,
 | 
			
		||||
                        metricName: 'test-series',
 | 
			
		||||
                        timeRange: 'day',
 | 
			
		||||
                        yAxisMin: 'zero',
 | 
			
		||||
                        mode: 'count',
 | 
			
		||||
                        selectedLabels: {},
 | 
			
		||||
                        labelSelectors: {},
 | 
			
		||||
                        title: 'Test Chart',
 | 
			
		||||
                    },
 | 
			
		||||
                ],
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,10 @@
 | 
			
		||||
export type ChartConfig = {
 | 
			
		||||
    id: string;
 | 
			
		||||
    selectedSeries: string; // e.g. unleash_counter_my_metric
 | 
			
		||||
    selectedRange: 'hour' | 'day' | 'week' | 'month';
 | 
			
		||||
    beginAtZero: boolean;
 | 
			
		||||
    metricName: string; // e.g. unleash_counter_my_metric
 | 
			
		||||
    timeRange: 'hour' | 'day' | 'week' | 'month';
 | 
			
		||||
    yAxisMin: 'auto' | 'zero';
 | 
			
		||||
    aggregationMode: AggregationMode;
 | 
			
		||||
    selectedLabels: Record<string, string[]>;
 | 
			
		||||
    labelSelectors: Record<string, string[]>;
 | 
			
		||||
    title?: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
export const getTimeUnit = (selectedRange: string) => {
 | 
			
		||||
    switch (selectedRange) {
 | 
			
		||||
export const getTimeUnit = (timeRange: string) => {
 | 
			
		||||
    switch (timeRange) {
 | 
			
		||||
        case 'hour':
 | 
			
		||||
            return 'minute';
 | 
			
		||||
        case 'day':
 | 
			
		||||
@ -13,8 +13,8 @@ export const getTimeUnit = (selectedRange: string) => {
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const getDisplayFormat = (selectedRange: string) => {
 | 
			
		||||
    switch (selectedRange) {
 | 
			
		||||
export const getDisplayFormat = (timeRange: string) => {
 | 
			
		||||
    switch (timeRange) {
 | 
			
		||||
        case 'hour':
 | 
			
		||||
            return 'HH:mm';
 | 
			
		||||
        case 'day':
 | 
			
		||||
 | 
			
		||||
@ -4,8 +4,9 @@
 | 
			
		||||
 * See `gen:api` script in package.json
 | 
			
		||||
 */
 | 
			
		||||
import type { CreateImpactMetricsConfigSchemaAggregationMode } from './createImpactMetricsConfigSchemaAggregationMode.js';
 | 
			
		||||
import type { CreateImpactMetricsConfigSchemaSelectedLabels } from './createImpactMetricsConfigSchemaSelectedLabels.js';
 | 
			
		||||
import type { CreateImpactMetricsConfigSchemaSelectedRange } from './createImpactMetricsConfigSchemaSelectedRange.js';
 | 
			
		||||
import type { CreateImpactMetricsConfigSchemaLabelSelectors } from './createImpactMetricsConfigSchemaLabelSelectors.js';
 | 
			
		||||
import type { CreateImpactMetricsConfigSchemaTimeRange } from './createImpactMetricsConfigSchemaTimeRange.js';
 | 
			
		||||
import type { CreateImpactMetricsConfigSchemaYAxisMin } from './createImpactMetricsConfigSchemaYAxisMin.js';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Describes the configuration for a single impact metric chart.
 | 
			
		||||
@ -13,22 +14,24 @@ import type { CreateImpactMetricsConfigSchemaSelectedRange } from './createImpac
 | 
			
		||||
export interface CreateImpactMetricsConfigSchema {
 | 
			
		||||
    /** The aggregation mode for the metric data. */
 | 
			
		||||
    aggregationMode: CreateImpactMetricsConfigSchemaAggregationMode;
 | 
			
		||||
    /** Whether the chart should begin at zero on the y-axis. */
 | 
			
		||||
    beginAtZero: boolean;
 | 
			
		||||
    /**
 | 
			
		||||
     * Optional feature name that this impact metric is associated with.
 | 
			
		||||
     * @nullable
 | 
			
		||||
     */
 | 
			
		||||
    feature?: string | null;
 | 
			
		||||
    /** The unique ULID identifier for this impact metric configuration. Generated automatically if not provided. */
 | 
			
		||||
    id?: string;
 | 
			
		||||
    /** The selected labels and their values for filtering the metric data. */
 | 
			
		||||
    selectedLabels: CreateImpactMetricsConfigSchemaSelectedLabels;
 | 
			
		||||
    /** The time range for the metric data. */
 | 
			
		||||
    selectedRange: CreateImpactMetricsConfigSchemaSelectedRange;
 | 
			
		||||
    labelSelectors: CreateImpactMetricsConfigSchemaLabelSelectors;
 | 
			
		||||
    /** The Prometheus metric series to display. It includes both unleash prefix and metric type and display name */
 | 
			
		||||
    selectedSeries: string;
 | 
			
		||||
    metricName: string;
 | 
			
		||||
    /** The time range for the metric data. */
 | 
			
		||||
    timeRange: CreateImpactMetricsConfigSchemaTimeRange;
 | 
			
		||||
    /**
 | 
			
		||||
     * Optional title for the impact metric chart.
 | 
			
		||||
     */
 | 
			
		||||
    title?: string;
 | 
			
		||||
    /** Whether the chart should begin at zero on the y-axis. */
 | 
			
		||||
    yAxisMin: CreateImpactMetricsConfigSchemaYAxisMin;
 | 
			
		||||
    [key: string]: unknown;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,6 @@
 | 
			
		||||
/**
 | 
			
		||||
 * The selected labels and their values for filtering the metric data.
 | 
			
		||||
 */
 | 
			
		||||
export type CreateImpactMetricsConfigSchemaSelectedLabels = {
 | 
			
		||||
export type CreateImpactMetricsConfigSchemaLabelSelectors = {
 | 
			
		||||
    [key: string]: string[];
 | 
			
		||||
};
 | 
			
		||||
@ -7,11 +7,11 @@
 | 
			
		||||
/**
 | 
			
		||||
 * The time range for the metric data.
 | 
			
		||||
 */
 | 
			
		||||
export type CreateImpactMetricsConfigSchemaSelectedRange =
 | 
			
		||||
    (typeof CreateImpactMetricsConfigSchemaSelectedRange)[keyof typeof CreateImpactMetricsConfigSchemaSelectedRange];
 | 
			
		||||
export type CreateImpactMetricsConfigSchemaTimeRange =
 | 
			
		||||
    (typeof CreateImpactMetricsConfigSchemaTimeRange)[keyof typeof CreateImpactMetricsConfigSchemaTimeRange];
 | 
			
		||||
 | 
			
		||||
// eslint-disable-next-line @typescript-eslint/no-redeclare
 | 
			
		||||
export const CreateImpactMetricsConfigSchemaSelectedRange = {
 | 
			
		||||
export const CreateImpactMetricsConfigSchemaTimeRange = {
 | 
			
		||||
    hour: 'hour',
 | 
			
		||||
    day: 'day',
 | 
			
		||||
    week: 'week',
 | 
			
		||||
@ -0,0 +1,17 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Generated by Orval
 | 
			
		||||
 * Do not edit manually.
 | 
			
		||||
 * See `gen:api` script in package.json
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Whether the chart should begin at zero on the y-axis.
 | 
			
		||||
 */
 | 
			
		||||
export type CreateImpactMetricsConfigSchemaYAxisMin =
 | 
			
		||||
    (typeof CreateImpactMetricsConfigSchemaYAxisMin)[keyof typeof CreateImpactMetricsConfigSchemaYAxisMin];
 | 
			
		||||
 | 
			
		||||
// eslint-disable-next-line @typescript-eslint/no-redeclare
 | 
			
		||||
export const CreateImpactMetricsConfigSchemaYAxisMin = {
 | 
			
		||||
    auto: 'auto',
 | 
			
		||||
    zero: 'zero',
 | 
			
		||||
} as const;
 | 
			
		||||
@ -4,9 +4,10 @@
 | 
			
		||||
 * See `gen:api` script in package.json
 | 
			
		||||
 */
 | 
			
		||||
import type { ImpactMetricsConfigSchemaAggregationMode } from './impactMetricsConfigSchemaAggregationMode.js';
 | 
			
		||||
import type { ImpactMetricsConfigSchemaSelectedLabels } from './impactMetricsConfigSchemaSelectedLabels.js';
 | 
			
		||||
import type { ImpactMetricsConfigSchemaSelectedRange } from './impactMetricsConfigSchemaSelectedRange.js';
 | 
			
		||||
import type { ImpactMetricsConfigSchemaLabelSelectors } from './impactMetricsConfigSchemaLabelSelectors.js';
 | 
			
		||||
import type { ImpactMetricsConfigSchemaTimeRange } from './impactMetricsConfigSchemaTimeRange.js';
 | 
			
		||||
import type { ImpactMetricsConfigSchemaType } from './impactMetricsConfigSchemaType.js';
 | 
			
		||||
import type { ImpactMetricsConfigSchemaYAxisMin } from './impactMetricsConfigSchemaYAxisMin.js';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Describes the configuration for a single impact metric chart.
 | 
			
		||||
@ -14,8 +15,6 @@ import type { ImpactMetricsConfigSchemaType } from './impactMetricsConfigSchemaT
 | 
			
		||||
export interface ImpactMetricsConfigSchema {
 | 
			
		||||
    /** The aggregation mode for the metric data. */
 | 
			
		||||
    aggregationMode: ImpactMetricsConfigSchemaAggregationMode;
 | 
			
		||||
    /** Whether the chart should begin at zero on the y-axis. */
 | 
			
		||||
    beginAtZero: boolean;
 | 
			
		||||
    /** The human readable display name of the impact metric */
 | 
			
		||||
    displayName: string;
 | 
			
		||||
    /**
 | 
			
		||||
@ -26,15 +25,17 @@ export interface ImpactMetricsConfigSchema {
 | 
			
		||||
    /** The unique ULID identifier for this impact metric configuration. Generated automatically if not provided. */
 | 
			
		||||
    id: string;
 | 
			
		||||
    /** The selected labels and their values for filtering the metric data. */
 | 
			
		||||
    selectedLabels: ImpactMetricsConfigSchemaSelectedLabels;
 | 
			
		||||
    /** The time range for the metric data. */
 | 
			
		||||
    selectedRange: ImpactMetricsConfigSchemaSelectedRange;
 | 
			
		||||
    labelSelectors: ImpactMetricsConfigSchemaLabelSelectors;
 | 
			
		||||
    /** The Prometheus metric series to display. It includes both unleash prefix and metric type and display name */
 | 
			
		||||
    selectedSeries: string;
 | 
			
		||||
    metricName: string;
 | 
			
		||||
    /** The time range for the metric data. */
 | 
			
		||||
    timeRange: ImpactMetricsConfigSchemaTimeRange;
 | 
			
		||||
    /**
 | 
			
		||||
     * Optional title for the impact metric chart.
 | 
			
		||||
     */
 | 
			
		||||
    title?: string;
 | 
			
		||||
    /** The type of metric */
 | 
			
		||||
    type: ImpactMetricsConfigSchemaType;
 | 
			
		||||
    /** Whether the chart should begin at zero on the y-axis. */
 | 
			
		||||
    yAxisMin: ImpactMetricsConfigSchemaYAxisMin;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,6 @@
 | 
			
		||||
/**
 | 
			
		||||
 * The selected labels and their values for filtering the metric data.
 | 
			
		||||
 */
 | 
			
		||||
export type ImpactMetricsConfigSchemaSelectedLabels = {
 | 
			
		||||
export type ImpactMetricsConfigSchemaLabelSelectors = {
 | 
			
		||||
    [key: string]: string[];
 | 
			
		||||
};
 | 
			
		||||
@ -7,11 +7,11 @@
 | 
			
		||||
/**
 | 
			
		||||
 * The time range for the metric data.
 | 
			
		||||
 */
 | 
			
		||||
export type ImpactMetricsConfigSchemaSelectedRange =
 | 
			
		||||
    (typeof ImpactMetricsConfigSchemaSelectedRange)[keyof typeof ImpactMetricsConfigSchemaSelectedRange];
 | 
			
		||||
export type ImpactMetricsConfigSchemaTimeRange =
 | 
			
		||||
    (typeof ImpactMetricsConfigSchemaTimeRange)[keyof typeof ImpactMetricsConfigSchemaTimeRange];
 | 
			
		||||
 | 
			
		||||
// eslint-disable-next-line @typescript-eslint/no-redeclare
 | 
			
		||||
export const ImpactMetricsConfigSchemaSelectedRange = {
 | 
			
		||||
export const ImpactMetricsConfigSchemaTimeRange = {
 | 
			
		||||
    hour: 'hour',
 | 
			
		||||
    day: 'day',
 | 
			
		||||
    week: 'week',
 | 
			
		||||
@ -0,0 +1,17 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Generated by Orval
 | 
			
		||||
 * Do not edit manually.
 | 
			
		||||
 * See `gen:api` script in package.json
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Whether the chart should begin at zero on the y-axis.
 | 
			
		||||
 */
 | 
			
		||||
export type ImpactMetricsConfigSchemaYAxisMin =
 | 
			
		||||
    (typeof ImpactMetricsConfigSchemaYAxisMin)[keyof typeof ImpactMetricsConfigSchemaYAxisMin];
 | 
			
		||||
 | 
			
		||||
// eslint-disable-next-line @typescript-eslint/no-redeclare
 | 
			
		||||
export const ImpactMetricsConfigSchemaYAxisMin = {
 | 
			
		||||
    auto: 'auto',
 | 
			
		||||
    zero: 'zero',
 | 
			
		||||
} as const;
 | 
			
		||||
@ -428,8 +428,9 @@ export * from './createGroupSchemaUsersItem.js';
 | 
			
		||||
export * from './createGroupSchemaUsersItemUser.js';
 | 
			
		||||
export * from './createImpactMetricsConfigSchema.js';
 | 
			
		||||
export * from './createImpactMetricsConfigSchemaAggregationMode.js';
 | 
			
		||||
export * from './createImpactMetricsConfigSchemaSelectedLabels.js';
 | 
			
		||||
export * from './createImpactMetricsConfigSchemaSelectedRange.js';
 | 
			
		||||
export * from './createImpactMetricsConfigSchemaLabelSelectors.js';
 | 
			
		||||
export * from './createImpactMetricsConfigSchemaTimeRange.js';
 | 
			
		||||
export * from './createImpactMetricsConfigSchemaYAxisMin.js';
 | 
			
		||||
export * from './createInvitedUserSchema.js';
 | 
			
		||||
export * from './createPat401.js';
 | 
			
		||||
export * from './createPat403.js';
 | 
			
		||||
@ -894,9 +895,10 @@ export * from './idsSchema.js';
 | 
			
		||||
export * from './impactMetricsConfigListSchema.js';
 | 
			
		||||
export * from './impactMetricsConfigSchema.js';
 | 
			
		||||
export * from './impactMetricsConfigSchemaAggregationMode.js';
 | 
			
		||||
export * from './impactMetricsConfigSchemaSelectedLabels.js';
 | 
			
		||||
export * from './impactMetricsConfigSchemaSelectedRange.js';
 | 
			
		||||
export * from './impactMetricsConfigSchemaLabelSelectors.js';
 | 
			
		||||
export * from './impactMetricsConfigSchemaTimeRange.js';
 | 
			
		||||
export * from './impactMetricsConfigSchemaType.js';
 | 
			
		||||
export * from './impactMetricsConfigSchemaYAxisMin.js';
 | 
			
		||||
export * from './impactMetricsSchema.js';
 | 
			
		||||
export * from './impactMetricsSchemaSamplesItem.js';
 | 
			
		||||
export * from './impactMetricsSchemaSamplesItemLabels.js';
 | 
			
		||||
@ -1374,6 +1376,8 @@ export * from './uncomplete401.js';
 | 
			
		||||
export * from './uncomplete403.js';
 | 
			
		||||
export * from './uncomplete404.js';
 | 
			
		||||
export * from './unknownFlagSchema.js';
 | 
			
		||||
export * from './unknownFlagSchemaReportsItem.js';
 | 
			
		||||
export * from './unknownFlagSchemaReportsItemEnvironmentsItem.js';
 | 
			
		||||
export * from './unknownFlagsResponseSchema.js';
 | 
			
		||||
export * from './unsubscribeEmailSubscription401.js';
 | 
			
		||||
export * from './unsubscribeEmailSubscription404.js';
 | 
			
		||||
 | 
			
		||||
@ -3,22 +3,21 @@
 | 
			
		||||
 * Do not edit manually.
 | 
			
		||||
 * See `gen:api` script in package.json
 | 
			
		||||
 */
 | 
			
		||||
import type { UnknownFlagSchemaReportsItem } from './unknownFlagSchemaReportsItem.js';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * An unknown flag report
 | 
			
		||||
 */
 | 
			
		||||
export interface UnknownFlagSchema {
 | 
			
		||||
    /** The name of the application that reported the unknown flag. */
 | 
			
		||||
    appName: string;
 | 
			
		||||
    /** The environment in which the unknown flag was reported. */
 | 
			
		||||
    environment: string;
 | 
			
		||||
    /**
 | 
			
		||||
     * The date and time when the last event for the unknown flag name occurred, if any.
 | 
			
		||||
     * @nullable
 | 
			
		||||
     */
 | 
			
		||||
    lastEventAt?: string | null;
 | 
			
		||||
    /** The date and time when the unknown flag was last reported. */
 | 
			
		||||
    lastSeenAt: string;
 | 
			
		||||
    /** The name of the unknown flag. */
 | 
			
		||||
    name: string;
 | 
			
		||||
    /** The date and time when the unknown flag was reported. */
 | 
			
		||||
    seenAt: string;
 | 
			
		||||
    /** The list of reports for this unknown flag. */
 | 
			
		||||
    reports?: UnknownFlagSchemaReportsItem[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										13
									
								
								frontend/src/openapi/models/unknownFlagSchemaReportsItem.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								frontend/src/openapi/models/unknownFlagSchemaReportsItem.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Generated by Orval
 | 
			
		||||
 * Do not edit manually.
 | 
			
		||||
 * See `gen:api` script in package.json
 | 
			
		||||
 */
 | 
			
		||||
import type { UnknownFlagSchemaReportsItemEnvironmentsItem } from './unknownFlagSchemaReportsItemEnvironmentsItem.js';
 | 
			
		||||
 | 
			
		||||
export type UnknownFlagSchemaReportsItem = {
 | 
			
		||||
    /** The name of the application that reported the unknown flag. */
 | 
			
		||||
    appName: string;
 | 
			
		||||
    /** The list of environments where this application reported the unknown flag. */
 | 
			
		||||
    environments: UnknownFlagSchemaReportsItemEnvironmentsItem[];
 | 
			
		||||
};
 | 
			
		||||
@ -0,0 +1,12 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Generated by Orval
 | 
			
		||||
 * Do not edit manually.
 | 
			
		||||
 * See `gen:api` script in package.json
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
export type UnknownFlagSchemaReportsItemEnvironmentsItem = {
 | 
			
		||||
    /** The environment in which the unknown flag was reported. */
 | 
			
		||||
    environment: string;
 | 
			
		||||
    /** The date and time when the unknown flag was last seen in this environment. */
 | 
			
		||||
    seenAt: string;
 | 
			
		||||
};
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user