Skip to content

Commit

Permalink
feat: move timeRange to AppStore (parseablehq#384)
Browse files Browse the repository at this point in the history
  • Loading branch information
praveen5959 authored Dec 6, 2024
1 parent 6af2086 commit 59161e2
Show file tree
Hide file tree
Showing 23 changed files with 229 additions and 215 deletions.
4 changes: 4 additions & 0 deletions src/components/Header/RefreshInterval.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ import { REFRESH_INTERVALS } from '@/constants/timeConstants';
import classes from './styles/LogQuery.module.css';
import { useLogsStore, logsStoreReducers } from '@/pages/Stream/providers/LogsProvider';
import _ from 'lodash';
import { appStoreReducers, useAppStore } from '@/layouts/MainLayout/providers/AppProvider';

const { setRefreshInterval, getCleanStoreForRefetch } = logsStoreReducers;
const { syncTimeRange } = appStoreReducers;
const RefreshInterval: FC = () => {
const [, setAppStore] = useAppStore((_store) => null);
const [refreshInterval, setLogsStore] = useLogsStore((store) => store.refreshInterval);
const Icon = useMemo(() => (refreshInterval ? IconRefresh : IconRefreshOff), [refreshInterval]);
const timerRef = useRef<NodeJS.Timer | null>(null);
Expand All @@ -34,6 +37,7 @@ const RefreshInterval: FC = () => {
clearIntervalInstance();
if (refreshInterval !== null) {
const intervalId = setInterval(() => {
setAppStore(syncTimeRange);
setLogsStore(getCleanStoreForRefetch);
}, refreshInterval);
timerRef.current = intervalId;
Expand Down
4 changes: 4 additions & 0 deletions src/components/Header/RefreshNow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@ import { IconReload } from '@tabler/icons-react';
import { useCallback, type FC } from 'react';
import { useLogsStore, logsStoreReducers } from '@/pages/Stream/providers/LogsProvider';
import IconButton from '../Button/IconButton';
import { appStoreReducers, useAppStore } from '@/layouts/MainLayout/providers/AppProvider';

const { getCleanStoreForRefetch } = logsStoreReducers;
const { syncTimeRange } = appStoreReducers;

const renderRefreshIcon = () => <IconReload size={px('1rem')} stroke={1.5} />;

const RefreshNow: FC = () => {
const [, setLogsStore] = useLogsStore((_store) => null);
const [, setAppStore] = useAppStore((_store) => null);

const onRefresh = useCallback(() => {
setAppStore((store) => syncTimeRange(store));
setLogsStore((store) => getCleanStoreForRefetch(store));
}, []);
return <IconButton size={38} renderIcon={renderRefreshIcon} onClick={onRefresh} tooltipLabel="Refresh now" />;
Expand Down
33 changes: 21 additions & 12 deletions src/components/Header/TimeRange.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import { Fragment, useCallback, useMemo, useRef, useState } from 'react';
import { FIXED_DURATIONS } from '@/constants/timeConstants';
import classes from './styles/LogQuery.module.css';
import { useOuterClick } from '@/hooks/useOuterClick';
import { logsStoreReducers, useLogsStore } from '@/pages/Stream/providers/LogsProvider';
import _ from 'lodash';
import timeRangeUtils from '@/utils/timeRangeUtils';
import { appStoreReducers, useAppStore } from '@/layouts/MainLayout/providers/AppProvider';
import { logsStoreReducers, useLogsStore } from '@/pages/Stream/providers/LogsProvider';

const {getRelativeStartAndEndDate} = timeRangeUtils
const { setTimeRange, setshiftInterval } = logsStoreReducers;
const { getRelativeStartAndEndDate } = timeRangeUtils;
const { setTimeRange, setshiftInterval } = appStoreReducers;
const { getCleanStoreForRefetch } = logsStoreReducers;
export type FixedDuration = (typeof FIXED_DURATIONS)[number];

const { timeRangeContainer, fixedRangeBtn, fixedRangeBtnSelected, customRangeContainer, shiftIntervalContainer } =
Expand All @@ -40,7 +42,8 @@ const RelativeTimeIntervals = (props: {
};

const TimeRange: FC = () => {
const [timeRange, setLogsStore] = useLogsStore((store) => store.timeRange);
const [timeRange, setAppStore] = useAppStore((store) => store.timeRange);
const [, setLogStore] = useLogsStore((_store) => null);
const { label, shiftInterval, interval, startTime, endTime, type } = timeRange;
const handleOuterClick = useCallback((event: any) => {
const targetClassNames: string[] = event.target?.classList || [];
Expand Down Expand Up @@ -70,16 +73,18 @@ const TimeRange: FC = () => {
}, []);

const onDurationSelect = (duration: FixedDuration) => {
const {startTime, endTime} = getRelativeStartAndEndDate(duration);
setLogsStore((store) => setTimeRange(store, { startTime, endTime, type: 'fixed' }));
const { startTime, endTime } = getRelativeStartAndEndDate(duration);
setLogStore((store) => getCleanStoreForRefetch(store));
setAppStore((store) => setTimeRange(store, { startTime, endTime, type: 'fixed' }));
setOpened(false);
};

const resetToRelative = useCallback(() => {
const now = dayjs().startOf('minute');
const startTime = now.subtract(FIXED_DURATIONS[0].milliseconds, 'milliseconds');
const endTime = now;
setLogsStore((store) => setTimeRange(store, { startTime, endTime, type: 'fixed' }));
setLogStore((store) => getCleanStoreForRefetch(store));
setAppStore((store) => setTimeRange(store, { startTime, endTime, type: 'fixed' }));
setOpened(false);
}, []);

Expand All @@ -93,7 +98,7 @@ const TimeRange: FC = () => {

const onSetShiftInterval = useCallback((val: number | string) => {
if (typeof val === 'number') {
setLogsStore((store) => setshiftInterval(store, val));
setAppStore((store) => setshiftInterval(store, val));
setShowTick(false); // Hide the tick when editing starts again
debouncedShowTick(); // Show the tick after the user stops typing
}
Expand All @@ -104,13 +109,15 @@ const TimeRange: FC = () => {
if (direction === 'left') {
const newStartTime = new Date(startTime.getTime() - changeInMs);
const newEndTime = new Date(endTime.getTime() - changeInMs);
setLogsStore((store) =>
setLogStore((store) => getCleanStoreForRefetch(store));
setAppStore((store) =>
setTimeRange(store, { startTime: dayjs(newStartTime), endTime: dayjs(newEndTime), type: 'custom' }),
);
} else {
const newStartTime = new Date(startTime.getTime() + changeInMs);
const newEndTime = new Date(endTime.getTime() + changeInMs);
setLogsStore((store) =>
setLogStore((store) => getCleanStoreForRefetch(store));
setAppStore((store) =>
setTimeRange(store, { startTime: dayjs(newStartTime), endTime: dayjs(newEndTime), type: 'custom' }),
);
}
Expand Down Expand Up @@ -197,7 +204,8 @@ function isDateInRange(startDate: Date, endDate: Date, currentDate: Date) {
}

const CustomTimeRange: FC<CustomTimeRangeProps> = ({ setOpened, resetToRelative }) => {
const [{ startTime: startTimeFromStore, endTime: endTimeFromStore, type }, setLogsStore] = useLogsStore(
const [, setLogStore] = useLogsStore((_store) => null);
const [{ startTime: startTimeFromStore, endTime: endTimeFromStore, type }, setAppStore] = useAppStore(
(store) => store.timeRange,
);

Expand Down Expand Up @@ -229,7 +237,8 @@ const CustomTimeRange: FC<CustomTimeRangeProps> = ({ setOpened, resetToRelative
};

const onApply = () => {
setLogsStore((store) =>
setLogStore((store) => getCleanStoreForRefetch(store));
setAppStore((store) =>
setTimeRange(store, {
type: 'custom',
startTime: dayjs(localSelectedRange.startTime).startOf('minute'),
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useGetLogStreamSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@ export const useGetStreamSchema = (opts: { streamName: string }) => {
isError,
isLoading,
errorMessage,
isRefetching
isRefetching,
};
};
2 changes: 1 addition & 1 deletion src/hooks/useQueryLogs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ export const useQueryLogs = () => {
const [currentStream] = useAppStore((store) => store.currentStream);
const timePartitionColumn = _.get(streamInfo, 'time_partition', 'p_timestamp');
const { refetch: refetchSchema } = useGetStreamSchema({ streamName: currentStream || '' });
const [timeRange] = useAppStore((store) => store.timeRange);
const [
{
timeRange,
tableOpts: { currentOffset, instantSearchValue },
custQuerySearchState,
},
Expand Down
3 changes: 2 additions & 1 deletion src/hooks/useQueryResult.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ export const useFetchCount = () => {
const [currentStream] = useAppStore((store) => store.currentStream);
const { setTotalCount } = logsStoreReducers;
const [custQuerySearchState] = useLogsStore((store) => store.custQuerySearchState);
const [timeRange, setLogsStore] = useLogsStore((store) => store.timeRange);
const [timeRange] = useAppStore((store) => store.timeRange);
const [, setLogsStore] = useLogsStore((_store) => null);
const { isQuerySearchActive, custSearchQuery, activeMode } = custQuerySearchState;
const [appliedQuery] = useFilterStore((store) => store.appliedQuery);

Expand Down
106 changes: 106 additions & 0 deletions src/layouts/MainLayout/providers/AppProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ import { AboutData } from '@/@types/parseable/api/about';
import _ from 'lodash';
import { AxiosResponse } from 'axios';
import { SavedFilterType } from '@/@types/parseable/api/savedFilters';
import { FIXED_DURATIONS, FixedDuration } from '@/constants/timeConstants';
import dayjs, { Dayjs } from 'dayjs';
import timeRangeUtils from '@/utils/timeRangeUtils';

const { makeTimeRangeLabel } = timeRangeUtils;

export const DEFAULT_FIXED_DURATIONS = FIXED_DURATIONS[0];

export type UserRoles = {
[roleName: string]: {
Expand All @@ -17,7 +24,35 @@ export type UserRoles = {

type ReducerOutput = Partial<AppStore>;

export type TimeRange = {
startTime: Date;
endTime: Date;
type: 'fixed' | 'custom';
label: string;
interval: number;
shiftInterval: number;
};

const getDefaultTimeRange = (duration: FixedDuration = DEFAULT_FIXED_DURATIONS) => {
const now = dayjs().startOf('minute');
const { milliseconds } = duration;

const startTime = now.subtract(milliseconds, 'milliseconds');
const endTime = now;
const label = makeTimeRangeLabel(startTime.toDate(), endTime.toDate());

return {
startTime: startTime.toDate(),
endTime: now.toDate(),
type: 'fixed' as const,
label,
interval: milliseconds,
shiftInterval: 1,
};
};

type AppStore = {
timeRange: TimeRange;
maximized: boolean;
helpModalOpen: boolean;
createStreamModalOpen: boolean;
Expand All @@ -34,19 +69,27 @@ type AppStore = {
};

type AppStoreReducers = {
setTimeRange: (
store: AppStore,
payload: { startTime: dayjs.Dayjs; endTime: dayjs.Dayjs; type: 'fixed' | 'custom' },
) => ReducerOutput;
toggleMaximize: (store: AppStore) => ReducerOutput;
toggleHelpModal: (store: AppStore, val?: boolean) => ReducerOutput;
changeStream: (store: AppStore, stream: string) => ReducerOutput;
setUserRoles: (store: AppStore, roles: UserRoles | null) => ReducerOutput;
setshiftInterval: (store: AppStore, interval: number) => ReducerOutput;
syncTimeRange: (store: AppStore) => ReducerOutput;
setUserSpecificStreams: (store: AppStore, userSpecficStreams: LogStreamData | null) => ReducerOutput;
setUserAccessMap: (store: AppStore, accessRoles: string[] | null) => ReducerOutput;
setStreamSpecificUserAccess: (store: AppStore, streamSpecificUserAccess: string[] | null) => ReducerOutput;
setInstanceConfig: (store: AppStore, instanceConfig: AboutData) => ReducerOutput;
toggleCreateStreamModal: (store: AppStore, val?: boolean) => ReducerOutput;
setSavedFilters: (store: AppStore, savedFilters: AxiosResponse<SavedFilterType[]>) => ReducerOutput;
applyQueryWithResetTime: (store: AppStore, timeRangePayload: { from: string; to: string } | null) => ReducerOutput;
};

const initialState: AppStore = {
timeRange: getDefaultTimeRange(),
maximized: false,
helpModalOpen: false,
currentStream: null,
Expand Down Expand Up @@ -89,6 +132,65 @@ function getHTTPContext() {
return window.isSecureContext;
}
// reducers
const syncTimeRange = (store: AppStore) => {
const { timeRange } = store;
const duration = _.find(FIXED_DURATIONS, (duration) => duration.milliseconds === timeRange.interval);
const updatedTimeRange = { timeRange: getDefaultTimeRange(duration) };
return {
...updatedTimeRange,
};
};

const setTimeRange = (
store: AppStore,
payload: { startTime: dayjs.Dayjs; endTime: Dayjs; type: 'fixed' | 'custom' },
) => {
const { startTime, endTime, type } = payload;
const label = makeTimeRangeLabel(startTime.toDate(), endTime.toDate());
const interval = endTime.diff(startTime, 'milliseconds');
return {
timeRange: { ...store.timeRange, startTime: startTime.toDate(), endTime: endTime.toDate(), label, interval, type },
};
};

const setshiftInterval = (store: AppStore, interval: number) => {
const { timeRange } = store;
return {
timeRange: {
...timeRange,
shiftInterval: interval,
},
};
};

const applyQueryWithResetTime = (store: AppStore, timeRangePayload: { from: string; to: string } | null) => {
const { timeRange } = store;

const updatedTimeRange = (() => {
if (!timeRangePayload) {
return { timeRange };
} else {
const startTime = dayjs(timeRangePayload.from);
const endTime = dayjs(timeRangePayload.to);
const label = makeTimeRangeLabel(startTime.toDate(), endTime.toDate());
const interval = endTime.diff(startTime, 'milliseconds');
return {
timeRange: {
...store.timeRange,
startTime: startTime.toDate(),
endTime: endTime.toDate(),
label,
interval,
type: 'custom' as const, // always
},
};
}
})();

return {
...updatedTimeRange,
};
};

const toggleMaximize = (store: AppStore) => {
return { maximized: !store.maximized };
Expand Down Expand Up @@ -146,6 +248,10 @@ const appStoreReducers: AppStoreReducers = {
setInstanceConfig,
toggleCreateStreamModal,
setSavedFilters,
setTimeRange,
setshiftInterval,
syncTimeRange,
applyQueryWithResetTime,
};

export { AppProvider, useAppStore, appStoreReducers };
6 changes: 3 additions & 3 deletions src/pages/Dashboards/CreateDashboardModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { useCallback, useEffect } from 'react';
import _ from 'lodash';
import { useDashboardsQuery } from '@/hooks/useDashboards';
import { useForm } from '@mantine/form';
import { useLogsStore } from '../Stream/providers/LogsProvider';
import timeRangeUtils from '@/utils/timeRangeUtils';
import { Tile } from '@/@types/parseable/api/dashboards';
import { useAppStore } from '@/layouts/MainLayout/providers/AppProvider';

const { toggleCreateDashboardModal, toggleEditDashboardModal } = dashboardsStoreReducers;
const { makeTimeRangeOptions, getDefaultTimeRangeOption } = timeRangeUtils;
Expand All @@ -29,7 +29,7 @@ const useDashboardForm = (opts: FormOpts) => {
initialValues: opts,
validate: {
name: (val) => (_.isEmpty(val) ? 'Name cannot be empty' : null),
description: (_val) => (null),
description: (_val) => null,
},
validateInputOnChange: true,
validateInputOnBlur: true,
Expand All @@ -53,7 +53,7 @@ const defaultOpts = {
const CreateDashboardModal = () => {
const [createMode, setDashbaordsStore] = useDashboardsStore((store) => store.createDashboardModalOpen);
const [editMode] = useDashboardsStore((store) => store.editDashboardModalOpen);
const [timeRange] = useLogsStore((store) => store.timeRange);
const [timeRange] = useAppStore((store) => store.timeRange);
const [activeDashboard] = useDashboardsStore((store) => store.activeDashboard);
const timeRangeOptions = makeTimeRangeOptions({
selected: editMode && activeDashboard ? activeDashboard.time_filter : null,
Expand Down
3 changes: 1 addition & 2 deletions src/pages/Dashboards/CreateTileForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import {
} from '@/@types/parseable/api/dashboards';
import { CodeHighlight } from '@mantine/code-highlight';
import { sanitiseSqlString } from '@/utils/sanitiseSqlString';
import { useLogsStore } from '../Stream/providers/LogsProvider';
import dayjs from 'dayjs';
import TimeRange from '@/components/Header/TimeRange';
import { colors, isCircularChart, isGraph, normalizeGraphColorConfig } from './Charts';
Expand Down Expand Up @@ -291,7 +290,7 @@ const Query = (props: { form: TileFormType; onChangeValue: (key: string, value:
const [fields, setFields] = useState<Field[]>([]);
const [initialHeight, setInitialHeight] = useState(0);
const [dashboards] = useDashboardsStore((store) => store.dashboards);
const [timeRange] = useLogsStore((store) => store.timeRange);
const [timeRange] = useAppStore((store) => store.timeRange);
const [appliedFilterQuery, setLogsStore] = useFilterStore((store) => store.appliedFilterQuery);
const [aiQuery, setAiQuery] = useState('');
const [userSpecificStreams] = useAppStore((store) => store.userSpecificStreams);
Expand Down
5 changes: 3 additions & 2 deletions src/pages/Dashboards/Tile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ import { Tile as TileType, TileQueryResponse } from '@/@types/parseable/api/dash
import { sanitiseSqlString } from '@/utils/sanitiseSqlString';
import Table from './Table';
import { downloadDataAsCSV, downloadDataAsJson, exportJson } from '@/utils/exportHelpers';
import { makeExportData, useLogsStore } from '../Stream/providers/LogsProvider';
import { makeExportData } from '../Stream/providers/LogsProvider';
import { getRandomUnitTypeForChart, getUnitTypeByKey } from './utils';
import { useAppStore } from '@/layouts/MainLayout/providers/AppProvider';

const ParseableLogo = () => (
<div className="png-export-parseable-logo" style={{ display: 'none', height: '100%' }}>
Expand Down Expand Up @@ -257,7 +258,7 @@ function TileControls(props: { tile: TileType; data: TileQueryResponse }) {
}

const Tile = (props: { id: string }) => {
const [timeRange] = useLogsStore((store) => store.timeRange);
const [timeRange] = useAppStore((store) => store.timeRange);
const [tilesData] = useDashboardsStore((store) => store.tilesData);
const tileData = _.get(tilesData, props.id, { records: [], fields: [] });
const [activeDashboard] = useDashboardsStore((store) => store.activeDashboard);
Expand Down
Loading

0 comments on commit 59161e2

Please sign in to comment.