Compare commits

...

4 Commits

Author SHA1 Message Date
SagarRajput-7
f1473d289c feat: added enum - TimeFormat 2024-09-08 19:42:55 +05:30
SagarRajput-7
77bc5bfc26 feat: fixed styles and added timeseries condition 2024-09-08 18:48:03 +05:30
SagarRajput-7
b57c59e655 feat: added 24-12H time format toggle 2024-09-08 17:44:15 +05:30
SagarRajput-7
1d2de85acc feat: added Option to toggle 24h time format in dashboard timeseries panels 2024-09-08 17:30:20 +05:30
16 changed files with 205 additions and 19 deletions

View File

@@ -27,6 +27,51 @@
}
}
.time-preference {
display: grid;
grid-template-columns: 1fr max-content;
.time-format-toggle {
margin-left: 4px;
.ant-radio-button-wrapper-checked {
color: #fff;
background: var(--bg-slate-400);
border-color: var(--bg-slate-400);
&:hover {
color: #fff;
background: var(--bg-slate-400);
border-color: var(--bg-slate-400);
&::before {
background-color: var(--bg-slate-400);
}
}
&:focus {
color: #fff;
background: var(--bg-slate-400);
border-color: var(--bg-slate-400);
}
}
.ant-radio-button-wrapper {
border-color: var(--bg-slate-400);
color: #fff;
&:hover,
&:focus {
color: #fff;
border-color: var(--bg-slate-400);
}
&::before {
background-color: var(--bg-slate-400);
}
}
}
}
.lightMode {
.time-selection-target {
border: 1px solid var(--bg-vanilla-300);

View File

@@ -1,19 +1,44 @@
import './TimePreference.styles.scss';
import { DownOutlined } from '@ant-design/icons';
import { Button, Dropdown, Typography } from 'antd';
import { Button, Dropdown, Radio, Typography } from 'antd';
import { PANEL_TYPES } from 'constants/queryBuilder';
import TimeItems, {
timePreferance,
timePreferenceType,
} from 'container/NewWidget/RightContainer/timeItems';
import { Globe } from 'lucide-react';
import { Dispatch, SetStateAction, useCallback, useMemo } from 'react';
import { TimeFormat } from 'utils/timeUtils';
import { menuItems } from './config';
function TimeFormatToggle(props: TimeFormatToggleProps): JSX.Element {
const { timeFormat, setTimeFormat } = props;
const handleToggle = (e: any): void => {
setTimeFormat?.(e.target.value);
};
return (
<Radio.Group
onChange={handleToggle}
value={timeFormat || TimeFormat.TWELVE_HOUR}
buttonStyle="solid"
className="time-format-toggle"
>
<Radio.Button value="24H">24H</Radio.Button>
<Radio.Button value="12H">12H</Radio.Button>
</Radio.Group>
);
}
function TimePreference({
setSelectedTime,
selectedTime,
setTimeFormat,
timeFormat,
panelType,
}: TimePreferenceDropDownProps): JSX.Element {
const timeMenuItemOnChangeHandler = useCallback(
(event: TimeMenuItemOnChangeHandlerEvent) => {
@@ -34,22 +59,27 @@ function TimePreference({
);
return (
<Dropdown
menu={menu}
rootClassName="time-selection-menu"
className="time-selection-target"
trigger={['click']}
>
<Button>
<div className="button-selected-text">
<Globe size={14} />
<Typography.Text className="selected-value">
{selectedTime.name}
</Typography.Text>
</div>
<DownOutlined />
</Button>
</Dropdown>
<div className="time-preference">
<Dropdown
menu={menu}
rootClassName="time-selection-menu"
className="time-selection-target"
trigger={['click']}
>
<Button>
<div className="button-selected-text">
<Globe size={14} />
<Typography.Text className="selected-value">
{selectedTime.name}
</Typography.Text>
</div>
<DownOutlined />
</Button>
</Dropdown>
{panelType === PANEL_TYPES.TIME_SERIES && (
<TimeFormatToggle timeFormat={timeFormat} setTimeFormat={setTimeFormat} />
)}
</div>
);
}
@@ -60,6 +90,28 @@ interface TimeMenuItemOnChangeHandlerEvent {
interface TimePreferenceDropDownProps {
setSelectedTime: Dispatch<SetStateAction<timePreferance>>;
selectedTime: timePreferance;
panelType: PANEL_TYPES;
timeFormat?: TimeFormat.TWENTY_FOUR_HOUR | TimeFormat.TWELVE_HOUR;
setTimeFormat?: Dispatch<
SetStateAction<TimeFormat.TWENTY_FOUR_HOUR | TimeFormat.TWELVE_HOUR>
>;
}
interface TimeFormatToggleProps {
timeFormat?: TimeFormat.TWENTY_FOUR_HOUR | TimeFormat.TWELVE_HOUR;
setTimeFormat?: Dispatch<
SetStateAction<TimeFormat.TWENTY_FOUR_HOUR | TimeFormat.TWELVE_HOUR>
>;
}
TimePreference.defaultProps = {
setTimeFormat: undefined,
timeFormat: TimeFormat.TWELVE_HOUR,
};
TimeFormatToggle.defaultProps = {
setTimeFormat: undefined,
timeFormat: TimeFormat.TWELVE_HOUR,
};
export default TimePreference;

View File

@@ -30,6 +30,7 @@ import { AppState } from 'store/reducers';
import { GlobalReducer } from 'types/reducer/globalTime';
import { getGraphType } from 'utils/getGraphType';
import { getSortedSeriesData } from 'utils/getSortedSeriesData';
import { TimeFormat } from 'utils/timeUtils';
import { getLocalStorageGraphVisibilityState } from '../utils';
import { PANEL_TYPES_VS_FULL_VIEW_TABLE } from './contants';
@@ -172,6 +173,10 @@ function FullView({
const isListView = widget.panelTypes === PANEL_TYPES.LIST;
const [timeFormat, setTimeFormat] = useState<
TimeFormat.TWENTY_FOUR_HOUR | TimeFormat.TWELVE_HOUR
>(TimeFormat.TWELVE_HOUR);
if (response.isLoading && widget.panelTypes !== PANEL_TYPES.LIST) {
return <Spinner height="100%" size="large" tip="Loading..." />;
}
@@ -187,6 +192,9 @@ function FullView({
<TimePreference
selectedTime={selectedTime}
setSelectedTime={setSelectedTime}
timeFormat={timeFormat}
setTimeFormat={setTimeFormat}
panelType={widget.panelTypes}
/>
<Button
style={{
@@ -226,6 +234,7 @@ function FullView({
graphVisibility={graphsVisibilityStates}
onDragSelect={onDragSelect}
tableProcessedDataRef={tableProcessedDataRef}
timeFormat={timeFormat}
/>
</GraphContainer>
</div>

View File

@@ -13,6 +13,7 @@ function WidgetGraphContainer({
setRequestData,
selectedWidget,
isLoadingPanelData,
timeFormat,
}: WidgetGraphContainerProps): JSX.Element {
if (queryResponse.data && selectedGraph === PANEL_TYPES.BAR) {
const sortedSeriesData = getSortedSeriesData(
@@ -76,6 +77,7 @@ function WidgetGraphContainer({
queryResponse={queryResponse}
setRequestData={setRequestData}
selectedGraph={selectedGraph}
timeFormat={timeFormat}
/>
);
}

View File

@@ -22,12 +22,14 @@ import { UpdateTimeInterval } from 'store/actions';
import { SuccessResponse } from 'types/api';
import { Widgets } from 'types/api/dashboard/getAll';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import { TimeFormat } from 'utils/timeUtils';
function WidgetGraph({
selectedWidget,
queryResponse,
setRequestData,
selectedGraph,
timeFormat,
}: WidgetGraphProps): JSX.Element {
const graphRef = useRef<HTMLDivElement>(null);
const dispatch = useDispatch();
@@ -109,6 +111,7 @@ function WidgetGraph({
setRequestData={setRequestData}
onDragSelect={onDragSelect}
selectedGraph={selectedGraph}
timeFormat={timeFormat}
/>
</div>
);
@@ -122,6 +125,11 @@ interface WidgetGraphProps {
>;
setRequestData: Dispatch<SetStateAction<GetQueryResultsProps>>;
selectedGraph: PANEL_TYPES;
timeFormat?: TimeFormat.TWENTY_FOUR_HOUR | TimeFormat.TWELVE_HOUR;
}
WidgetGraph.defaultProps = {
timeFormat: TimeFormat.TWELVE_HOUR,
};
export default WidgetGraph;

View File

@@ -18,6 +18,7 @@ function WidgetGraph({
setRequestData,
selectedWidget,
isLoadingPanelData,
timeFormat,
}: WidgetGraphContainerProps): JSX.Element {
const { currentQuery } = useQueryBuilder();
@@ -49,6 +50,7 @@ function WidgetGraph({
queryResponse={queryResponse}
setRequestData={setRequestData}
selectedWidget={selectedWidget}
timeFormat={timeFormat}
/>
</Container>
);

View File

@@ -27,6 +27,7 @@ function LeftContainer({
requestData,
setRequestData,
isLoadingPanelData,
timeFormat,
}: WidgetGraphProps): JSX.Element {
const { stagedQuery } = useQueryBuilder();
const { selectedDashboard } = useDashboard();
@@ -57,6 +58,7 @@ function LeftContainer({
setRequestData={setRequestData}
selectedWidget={selectedWidget}
isLoadingPanelData={isLoadingPanelData}
timeFormat={timeFormat}
/>
<QueryContainer className="query-section-left-container">
<QuerySection selectedGraph={selectedGraph} queryResponse={queryResponse} />

View File

@@ -20,6 +20,7 @@ import {
} from 'react';
import { ColumnUnit, Widgets } from 'types/api/dashboard/getAll';
import { DataSource } from 'types/common/queryBuilder';
import { TimeFormat } from 'utils/timeUtils';
import { ColumnUnitSelector } from './ColumnUnitSelector/ColumnUnitSelector';
import {
@@ -71,6 +72,8 @@ function RightContainer({
setSoftMin,
columnUnits,
setColumnUnits,
setTimeFormat,
timeFormat,
}: RightContainerProps): JSX.Element {
const onChangeHandler = useCallback(
(setFunc: Dispatch<SetStateAction<string>>, value: string) => {
@@ -196,6 +199,9 @@ function RightContainer({
selectedTime,
setSelectedTime,
}}
timeFormat={timeFormat}
setTimeFormat={setTimeFormat}
panelType={selectedGraph}
/>
</>
)}
@@ -350,10 +356,16 @@ interface RightContainerProps {
setColumnUnits: Dispatch<SetStateAction<ColumnUnit>>;
setSoftMin: Dispatch<SetStateAction<number | null>>;
setSoftMax: Dispatch<SetStateAction<number | null>>;
timeFormat?: TimeFormat.TWENTY_FOUR_HOUR | TimeFormat.TWELVE_HOUR;
setTimeFormat?: Dispatch<
SetStateAction<TimeFormat.TWENTY_FOUR_HOUR | TimeFormat.TWELVE_HOUR>
>;
}
RightContainer.defaultProps = {
selectedWidget: undefined,
setTimeFormat: undefined,
timeFormat: TimeFormat.TWELVE_HOUR,
};
export default RightContainer;

View File

@@ -42,6 +42,7 @@ import { DataSource } from 'types/common/queryBuilder';
import AppReducer from 'types/reducer/app';
import { GlobalReducer } from 'types/reducer/globalTime';
import { getGraphType, getGraphTypeForFormat } from 'utils/getGraphType';
import { TimeFormat } from 'utils/timeUtils';
import LeftContainer from './LeftContainer';
import QueryTypeTag from './LeftContainer/QueryTypeTag';
@@ -270,6 +271,10 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
enum: selectedWidget?.timePreferance || 'GLOBAL_TIME',
});
const [timeFormat, setTimeFormat] = useState<
TimeFormat.TWENTY_FOUR_HOUR | TimeFormat.TWELVE_HOUR
>(TimeFormat.TWELVE_HOUR);
const updateDashboardMutation = useUpdateDashboard();
const { afterWidgets, preWidgets } = useMemo(() => {
@@ -652,6 +657,7 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
requestData={requestData}
setRequestData={setRequestData}
isLoadingPanelData={isLoadingPanelData}
timeFormat={timeFormat}
/>
)}
</OverlayScrollbar>
@@ -695,6 +701,8 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
setSoftMin={setSoftMin}
softMax={softMax}
setSoftMax={setSoftMax}
setTimeFormat={setTimeFormat}
timeFormat={timeFormat}
/>
</OverlayScrollbar>
</RightContainerWrapper>

View File

@@ -5,6 +5,7 @@ import { UseQueryResult } from 'react-query';
import { SuccessResponse } from 'types/api';
import { Widgets } from 'types/api/dashboard/getAll';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import { TimeFormat } from 'utils/timeUtils';
import { timePreferance } from './RightContainer/timeItems';
@@ -27,6 +28,7 @@ export interface WidgetGraphProps {
requestData: GetQueryResultsProps;
setRequestData: Dispatch<SetStateAction<GetQueryResultsProps>>;
isLoadingPanelData: boolean;
timeFormat?: TimeFormat.TWENTY_FOUR_HOUR | TimeFormat.TWELVE_HOUR;
}
export type WidgetGraphContainerProps = {
@@ -38,4 +40,5 @@ export type WidgetGraphContainerProps = {
selectedGraph: PANEL_TYPES;
selectedWidget: Widgets;
isLoadingPanelData: boolean;
timeFormat?: TimeFormat.TWENTY_FOUR_HOUR | TimeFormat.TWELVE_HOUR;
};

View File

@@ -16,6 +16,7 @@ function PanelWrapper({
selectedGraph,
tableProcessedDataRef,
customTooltipElement,
timeFormat,
}: PanelWrapperProps): JSX.Element {
const Component = PanelTypeVsPanelWrapper[
selectedGraph || widget.panelTypes
@@ -39,6 +40,7 @@ function PanelWrapper({
selectedGraph={selectedGraph}
tableProcessedDataRef={tableProcessedDataRef}
customTooltipElement={customTooltipElement}
timeFormat={timeFormat}
/>
);
}

View File

@@ -31,6 +31,7 @@ function UplotPanelWrapper({
onDragSelect,
selectedGraph,
customTooltipElement,
timeFormat,
}: PanelWrapperProps): JSX.Element {
const { toScrollWidgetId, setToScrollWidgetId } = useDashboard();
const isDarkMode = useIsDarkMode();
@@ -128,6 +129,7 @@ function UplotPanelWrapper({
hiddenGraph,
setHiddenGraph,
customTooltipElement,
timeFormat,
}),
[
widget?.id,
@@ -150,6 +152,7 @@ function UplotPanelWrapper({
currentQuery,
hiddenGraph,
customTooltipElement,
timeFormat,
],
);

View File

@@ -7,6 +7,7 @@ import { UseQueryResult } from 'react-query';
import { SuccessResponse } from 'types/api';
import { Widgets } from 'types/api/dashboard/getAll';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import { TimeFormat } from 'utils/timeUtils';
export type PanelWrapperProps = {
queryResponse: UseQueryResult<
@@ -24,6 +25,7 @@ export type PanelWrapperProps = {
selectedGraph?: PANEL_TYPES;
tableProcessedDataRef?: React.MutableRefObject<RowData[]>;
customTooltipElement?: HTMLDivElement;
timeFormat?: TimeFormat.TWENTY_FOUR_HOUR | TimeFormat.TWELVE_HOUR;
};
export type TooltipData = {

View File

@@ -16,6 +16,7 @@ import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import { Query } from 'types/api/queryBuilder/queryBuilderData';
import { QueryData, QueryDataV3 } from 'types/api/widgets/getQuery';
import uPlot from 'uplot';
import { TimeFormat } from 'utils/timeUtils';
import onClickPlugin, { OnClickPluginOpts } from './plugins/onClickPlugin';
import tooltipPlugin from './plugins/tooltipPlugin';
@@ -54,6 +55,7 @@ export interface GetUPlotChartOptions {
}>
>;
customTooltipElement?: HTMLDivElement;
timeFormat?: TimeFormat.TWENTY_FOUR_HOUR | TimeFormat.TWELVE_HOUR;
}
/** the function converts series A , series B , series C to
@@ -156,6 +158,7 @@ export const getUPlotChartOptions = ({
hiddenGraph,
setHiddenGraph,
customTooltipElement,
timeFormat,
}: GetUPlotChartOptions): uPlot.Options => {
const timeScaleProps = getXAxisScale(minTimeScale, maxTimeScale);
@@ -355,6 +358,6 @@ export const getUPlotChartOptions = ({
hiddenGraph,
isDarkMode,
}),
axes: getAxes(isDarkMode, yAxisUnit),
axes: getAxes(isDarkMode, yAxisUnit, timeFormat),
};
};

View File

@@ -1,10 +1,32 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
// @ts-nocheck
import { getToolTipValue } from 'components/Graph/yAxisConfig';
import { TimeFormat } from 'utils/timeUtils';
import getGridColor from './getGridColor';
const getAxes = (isDarkMode: boolean, yAxisUnit?: string): any => [
function format24HourTime(ts, includeDate = false): string {
const date = new Date(ts * 1000);
const hours = date.getHours().toString().padStart(2, '0');
const minutes = date.getMinutes().toString().padStart(2, '0');
const timeStr = `${hours}:${minutes}`;
if (includeDate) {
const day = date.getDate().toString().padStart(2, '0');
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const year = date.getFullYear();
const dateStr = `${month}/${day}/${year}`;
return `${timeStr}\n${dateStr}`;
}
return timeStr;
}
const getAxes = (
isDarkMode: boolean,
yAxisUnit?: string,
timeFormat?: TimeFormat.TWENTY_FOUR_HOUR | TimeFormat.TWELVE_HOUR,
): any => [
{
stroke: isDarkMode ? 'white' : 'black', // Color of the axis line
grid: {
@@ -18,6 +40,12 @@ const getAxes = (isDarkMode: boolean, yAxisUnit?: string): any => [
show: true,
},
gap: 5,
...(timeFormat === TimeFormat.TWENTY_FOUR_HOUR
? {
values: (_self, ticks): any =>
ticks.map((ts, i) => format24HourTime(ts, i % 5 === 0)), // Include date every 5th tick
}
: {}),
},
{
stroke: isDarkMode ? 'white' : 'black', // Color of the axis line

View File

@@ -125,3 +125,8 @@ export const epochToTimeString = (epochMs: number): string => {
};
return date.toLocaleTimeString('en-US', options);
};
export enum TimeFormat {
TWENTY_FOUR_HOUR = '24H',
TWELVE_HOUR = '12H',
}