1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-08-23 13:46:45 +02:00

update chart form

This commit is contained in:
Tymoteusz Czech 2025-07-01 17:07:27 +02:00
parent 41e0c3a653
commit 3d40c7b31d
No known key found for this signature in database
GPG Key ID: 133555230D88D75F
4 changed files with 164 additions and 120 deletions

View File

@ -1,5 +1,4 @@
import type { FC } from 'react';
import { useState, useEffect } from 'react';
import {
Dialog,
DialogTitle,
@ -12,7 +11,7 @@ import {
} from '@mui/material';
import { ImpactMetricsControls } from './ImpactMetricsControls/ImpactMetricsControls.tsx';
import { ImpactMetricsChartPreview } from './ImpactMetricsChartPreview.tsx';
import { useImpactMetricsData } from 'hooks/api/getters/useImpactMetricsData/useImpactMetricsData';
import { useChartFormState } from './hooks/useChartFormState.ts';
import type { ChartConfig } from './types.ts';
import type { ImpactMetricsSeries } from 'hooks/api/getters/useImpactMetricsMetadata/useImpactMetricsMetadata';
@ -58,67 +57,19 @@ export const ChartConfigModal: FC<ChartConfigModalProps> = ({
metricSeries,
loading = false,
}) => {
const [title, setTitle] = useState(initialConfig?.title || '');
const [selectedSeries, setSelectedSeries] = useState(
initialConfig?.selectedSeries || '',
);
const [selectedRange, setSelectedRange] = useState<
'hour' | 'day' | 'week' | 'month'
>(initialConfig?.selectedRange || 'day');
const [beginAtZero, setBeginAtZero] = useState(
initialConfig?.beginAtZero || false,
);
const [selectedLabels, setSelectedLabels] = useState<
Record<string, string[]>
>(initialConfig?.selectedLabels || {});
const {
data: { labels: currentAvailableLabels },
} = useImpactMetricsData(
selectedSeries
? {
series: selectedSeries,
range: selectedRange,
}
: undefined,
);
useEffect(() => {
if (open && initialConfig) {
setTitle(initialConfig.title || '');
setSelectedSeries(initialConfig.selectedSeries);
setSelectedRange(initialConfig.selectedRange);
setBeginAtZero(initialConfig.beginAtZero);
setSelectedLabels(initialConfig.selectedLabels);
} else if (open && !initialConfig) {
setTitle('');
setSelectedSeries('');
setSelectedRange('day');
setBeginAtZero(false);
setSelectedLabels({});
}
}, [open, initialConfig]);
const { formData, actions, isValid, currentAvailableLabels } =
useChartFormState({
open,
initialConfig,
});
const handleSave = () => {
if (!selectedSeries) return;
if (!isValid) return;
onSave({
title: title || undefined,
selectedSeries,
selectedRange,
beginAtZero,
selectedLabels,
});
onSave(actions.getConfigToSave());
onClose();
};
const handleSeriesChange = (series: string) => {
setSelectedSeries(series);
setSelectedLabels({});
};
const isValid = selectedSeries.length > 0;
return (
<Dialog
open={open}
@ -148,33 +99,27 @@ export const ChartConfigModal: FC<ChartConfigModalProps> = ({
<StyledConfigPanel>
<TextField
label='Chart Title (optional)'
value={title}
onChange={(e) => setTitle(e.target.value)}
value={formData.title}
onChange={(e) => actions.setTitle(e.target.value)}
fullWidth
variant='outlined'
size='small'
/>
<ImpactMetricsControls
selectedSeries={selectedSeries}
onSeriesChange={handleSeriesChange}
selectedRange={selectedRange}
onRangeChange={setSelectedRange}
beginAtZero={beginAtZero}
onBeginAtZeroChange={setBeginAtZero}
formData={formData}
actions={actions}
metricSeries={metricSeries}
loading={loading}
selectedLabels={selectedLabels}
onLabelsChange={setSelectedLabels}
availableLabels={currentAvailableLabels}
/>
</StyledConfigPanel>
<StyledPreviewPanel>
<ImpactMetricsChartPreview
selectedSeries={selectedSeries}
selectedRange={selectedRange}
selectedLabels={selectedLabels}
beginAtZero={beginAtZero}
selectedSeries={formData.selectedSeries}
selectedRange={formData.selectedRange}
selectedLabels={formData.selectedLabels}
beginAtZero={formData.beginAtZero}
/>
</StyledPreviewPanel>
</Box>

View File

@ -1,29 +1,33 @@
import type { FC } from 'react';
import { Box, Typography } from '@mui/material';
import { Box, Typography, FormControlLabel, Checkbox } from '@mui/material';
import type { ImpactMetricsSeries } from 'hooks/api/getters/useImpactMetricsMetadata/useImpactMetricsMetadata';
import type { ImpactMetricsLabels } from 'hooks/api/getters/useImpactMetricsData/useImpactMetricsData';
import { SeriesSelector } from './components/SeriesSelector.tsx';
import { RangeSelector, type TimeRange } from './components/RangeSelector.tsx';
import { BeginAtZeroToggle } from './components/BeginAtZeroToggle.tsx';
import { RangeSelector } from './components/RangeSelector.tsx';
import { LabelsFilter } from './components/LabelsFilter.tsx';
import type { ChartFormState } from '../hooks/useChartFormState.ts';
export type ImpactMetricsControlsProps = {
selectedSeries: string;
onSeriesChange: (series: string) => void;
selectedRange: TimeRange;
onRangeChange: (range: TimeRange) => void;
beginAtZero: boolean;
onBeginAtZeroChange: (beginAtZero: boolean) => void;
formData: ChartFormState['formData'];
actions: Pick<
ChartFormState['actions'],
| 'handleSeriesChange'
| 'setSelectedRange'
| 'setBeginAtZero'
| 'setSelectedLabels'
>;
metricSeries: (ImpactMetricsSeries & { name: string })[];
loading?: boolean;
selectedLabels: Record<string, string[]>;
onLabelsChange: (labels: Record<string, string[]>) => void;
availableLabels?: ImpactMetricsLabels;
};
export const ImpactMetricsControls: FC<ImpactMetricsControlsProps> = (
props,
) => (
export const ImpactMetricsControls: FC<ImpactMetricsControlsProps> = ({
formData,
actions,
metricSeries,
loading,
availableLabels,
}) => (
<Box
sx={(theme) => ({
display: 'flex',
@ -39,27 +43,32 @@ export const ImpactMetricsControls: FC<ImpactMetricsControlsProps> = (
</Typography>
<SeriesSelector
value={props.selectedSeries}
onChange={props.onSeriesChange}
options={props.metricSeries}
loading={props.loading}
value={formData.selectedSeries}
onChange={actions.handleSeriesChange}
options={metricSeries}
loading={loading}
/>
<RangeSelector
value={props.selectedRange}
onChange={props.onRangeChange}
value={formData.selectedRange}
onChange={actions.setSelectedRange}
/>
<BeginAtZeroToggle
value={props.beginAtZero}
onChange={props.onBeginAtZeroChange}
<FormControlLabel
control={
<Checkbox
checked={formData.beginAtZero}
onChange={(e) => actions.setBeginAtZero(e.target.checked)}
/>
}
label='Begin at zero'
/>
{props.availableLabels && (
{availableLabels && (
<LabelsFilter
selectedLabels={props.selectedLabels}
onChange={props.onLabelsChange}
availableLabels={props.availableLabels}
selectedLabels={formData.selectedLabels}
onChange={actions.setSelectedLabels}
availableLabels={availableLabels}
/>
)}
</Box>

View File

@ -1,22 +0,0 @@
import type { FC } from 'react';
import { FormControlLabel, Checkbox } from '@mui/material';
export type BeginAtZeroToggleProps = {
value: boolean;
onChange: (beginAtZero: boolean) => void;
};
export const BeginAtZeroToggle: FC<BeginAtZeroToggleProps> = ({
value,
onChange,
}) => (
<FormControlLabel
control={
<Checkbox
checked={value}
onChange={(e) => onChange(e.target.checked)}
/>
}
label='Begin at zero'
/>
);

View File

@ -0,0 +1,112 @@
import { useState, useEffect } from 'react';
import { useImpactMetricsData } from 'hooks/api/getters/useImpactMetricsData/useImpactMetricsData';
import type { ChartConfig } from '../types.ts';
import type { ImpactMetricsLabels } from 'hooks/api/getters/useImpactMetricsData/useImpactMetricsData';
type UseChartConfigParams = {
open: boolean;
initialConfig?: ChartConfig;
};
export type ChartFormState = {
formData: {
title: string;
selectedSeries: string;
selectedRange: 'hour' | 'day' | 'week' | 'month';
beginAtZero: boolean;
selectedLabels: Record<string, string[]>;
};
actions: {
setTitle: (title: string) => void;
setSelectedSeries: (series: string) => void;
setSelectedRange: (range: 'hour' | 'day' | 'week' | 'month') => void;
setBeginAtZero: (beginAtZero: boolean) => void;
setSelectedLabels: (labels: Record<string, string[]>) => void;
handleSeriesChange: (series: string) => void;
getConfigToSave: () => Omit<ChartConfig, 'id'>;
};
isValid: boolean;
currentAvailableLabels: ImpactMetricsLabels | undefined;
};
export const useChartFormState = ({
open,
initialConfig,
}: UseChartConfigParams): ChartFormState => {
const [title, setTitle] = useState(initialConfig?.title || '');
const [selectedSeries, setSelectedSeries] = useState(
initialConfig?.selectedSeries || '',
);
const [selectedRange, setSelectedRange] = useState<
'hour' | 'day' | 'week' | 'month'
>(initialConfig?.selectedRange || 'day');
const [beginAtZero, setBeginAtZero] = useState(
initialConfig?.beginAtZero || false,
);
const [selectedLabels, setSelectedLabels] = useState<
Record<string, string[]>
>(initialConfig?.selectedLabels || {});
const {
data: { labels: currentAvailableLabels },
} = useImpactMetricsData(
selectedSeries
? {
series: selectedSeries,
range: selectedRange,
}
: undefined,
);
useEffect(() => {
if (open && initialConfig) {
setTitle(initialConfig.title || '');
setSelectedSeries(initialConfig.selectedSeries);
setSelectedRange(initialConfig.selectedRange);
setBeginAtZero(initialConfig.beginAtZero);
setSelectedLabels(initialConfig.selectedLabels);
} else if (open && !initialConfig) {
setTitle('');
setSelectedSeries('');
setSelectedRange('day');
setBeginAtZero(false);
setSelectedLabels({});
}
}, [open, initialConfig]);
const handleSeriesChange = (series: string) => {
setSelectedSeries(series);
setSelectedLabels({});
};
const getConfigToSave = (): Omit<ChartConfig, 'id'> => ({
title: title || undefined,
selectedSeries,
selectedRange,
beginAtZero,
selectedLabels,
});
const isValid = selectedSeries.length > 0;
return {
formData: {
title,
selectedSeries,
selectedRange,
beginAtZero,
selectedLabels,
},
actions: {
setTitle,
setSelectedSeries,
setSelectedRange,
setBeginAtZero,
setSelectedLabels,
handleSeriesChange,
getConfigToSave,
},
isValid,
currentAvailableLabels,
};
};