mirror of
https://github.com/SigNoz/signoz.git
synced 2025-12-18 07:56:56 +00:00
feat: date picker v2 (#8886)
* feat: date picker v2 * feat: custom date time range history * feat: light mode updates and interaction fixes * fix: improve usability * chore: add calendar, input and popover to transformIgnorePatterns * chore: add date-fns to transformIgnorePatterns * chore: add @signozhq/button to transformIgnorePatterns * feat: update css variables
This commit is contained in:
parent
514bceca34
commit
2a5fb9fd6f
@ -26,7 +26,7 @@ const config: Config.InitialOptions = {
|
|||||||
'^.+\\.(js|jsx)$': 'babel-jest',
|
'^.+\\.(js|jsx)$': 'babel-jest',
|
||||||
},
|
},
|
||||||
transformIgnorePatterns: [
|
transformIgnorePatterns: [
|
||||||
'node_modules/(?!(lodash-es|react-dnd|core-dnd|@react-dnd|dnd-core|react-dnd-html5-backend|axios|@signozhq/design-tokens|d3-interpolate|d3-color|api|@codemirror|@lezer|@marijn)/)',
|
'node_modules/(?!(lodash-es|react-dnd|core-dnd|@react-dnd|dnd-core|react-dnd-html5-backend|axios|@signozhq/design-tokens|@signozhq/calendar|@signozhq/input|@signozhq/popover|@signozhq/button|date-fns|d3-interpolate|d3-color|api|@codemirror|@lezer|@marijn)/)',
|
||||||
],
|
],
|
||||||
setupFilesAfterEnv: ['<rootDir>jest.setup.ts'],
|
setupFilesAfterEnv: ['<rootDir>jest.setup.ts'],
|
||||||
testPathIgnorePatterns: ['/node_modules/', '/public/'],
|
testPathIgnorePatterns: ['/node_modules/', '/public/'],
|
||||||
|
|||||||
@ -43,11 +43,14 @@
|
|||||||
"@radix-ui/react-tooltip": "1.0.7",
|
"@radix-ui/react-tooltip": "1.0.7",
|
||||||
"@sentry/react": "8.41.0",
|
"@sentry/react": "8.41.0",
|
||||||
"@sentry/webpack-plugin": "2.22.6",
|
"@sentry/webpack-plugin": "2.22.6",
|
||||||
|
"@signozhq/calendar": "0.0.0",
|
||||||
"@signozhq/design-tokens": "1.1.4",
|
"@signozhq/design-tokens": "1.1.4",
|
||||||
|
"@signozhq/input": "0.0.2",
|
||||||
|
"@signozhq/popover": "0.0.0",
|
||||||
"@tanstack/react-table": "8.20.6",
|
"@tanstack/react-table": "8.20.6",
|
||||||
"@tanstack/react-virtual": "3.11.2",
|
"@tanstack/react-virtual": "3.11.2",
|
||||||
"@uiw/codemirror-theme-github": "4.24.1",
|
|
||||||
"@uiw/codemirror-theme-copilot": "4.23.11",
|
"@uiw/codemirror-theme-copilot": "4.23.11",
|
||||||
|
"@uiw/codemirror-theme-github": "4.24.1",
|
||||||
"@uiw/react-codemirror": "4.23.10",
|
"@uiw/react-codemirror": "4.23.10",
|
||||||
"@uiw/react-md-editor": "3.23.5",
|
"@uiw/react-md-editor": "3.23.5",
|
||||||
"@visx/group": "3.3.0",
|
"@visx/group": "3.3.0",
|
||||||
|
|||||||
@ -1,6 +1,16 @@
|
|||||||
.custom-time-picker {
|
.custom-time-picker {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
|
.timeSelection-input {
|
||||||
|
&:hover {
|
||||||
|
border-color: #1d212d !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-input-suffix {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.time-options-container {
|
.time-options-container {
|
||||||
@ -135,6 +145,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
color: var(--bg-vanilla-400);
|
color: var(--bg-vanilla-400);
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
|
|
||||||
.timezone {
|
.timezone {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -163,6 +174,27 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.time-input-suffix-icon-badge {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0 4px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: rgba(171, 189, 255, 0.04);
|
||||||
|
color: var(--bg-vanilla-100);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 16px;
|
||||||
|
letter-spacing: -0.06px;
|
||||||
|
cursor: pointer;
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba(171, 189, 255, 0.08);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.lightMode {
|
.lightMode {
|
||||||
.date-time-popover__footer {
|
.date-time-popover__footer {
|
||||||
border-color: var(--bg-vanilla-400);
|
border-color: var(--bg-vanilla-400);
|
||||||
@ -180,8 +212,26 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.custom-time-picker {
|
||||||
|
.timeSelection-input {
|
||||||
|
&:hover {
|
||||||
|
border-color: var(--bg-vanilla-300) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.timezone-badge {
|
.timezone-badge {
|
||||||
color: var(--bg-ink-100);
|
color: var(--bg-ink-100);
|
||||||
background: rgb(179 179 179 / 15%);
|
background: rgb(179 179 179 / 15%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.time-input-suffix-icon-badge {
|
||||||
|
color: var(--bg-ink-100);
|
||||||
|
background: rgb(179 179 179 / 15%);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgb(179 179 179 / 20%);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,13 +5,12 @@ import './CustomTimePicker.styles.scss';
|
|||||||
import { Input, Popover, Tooltip, Typography } from 'antd';
|
import { Input, Popover, Tooltip, Typography } from 'antd';
|
||||||
import logEvent from 'api/common/logEvent';
|
import logEvent from 'api/common/logEvent';
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
|
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
|
||||||
import { DateTimeRangeType } from 'container/TopNav/CustomDateTimeModal';
|
import { DateTimeRangeType } from 'container/TopNav/CustomDateTimeModal';
|
||||||
import {
|
import {
|
||||||
CustomTimeType,
|
|
||||||
FixedDurationSuggestionOptions,
|
FixedDurationSuggestionOptions,
|
||||||
Options,
|
Options,
|
||||||
RelativeDurationSuggestionOptions,
|
RelativeDurationSuggestionOptions,
|
||||||
Time,
|
|
||||||
} from 'container/TopNav/DateTimeSelectionV2/config';
|
} from 'container/TopNav/DateTimeSelectionV2/config';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { isValidTimeFormat } from 'lib/getMinMax';
|
import { isValidTimeFormat } from 'lib/getMinMax';
|
||||||
@ -28,7 +27,10 @@ import {
|
|||||||
useMemo,
|
useMemo,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
|
import { AppState } from 'store/reducers';
|
||||||
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
import { popupContainer } from 'utils/selectPopupContainer';
|
import { popupContainer } from 'utils/selectPopupContainer';
|
||||||
|
|
||||||
import CustomTimePickerPopoverContent from './CustomTimePickerPopoverContent';
|
import CustomTimePickerPopoverContent from './CustomTimePickerPopoverContent';
|
||||||
@ -58,10 +60,6 @@ interface CustomTimePickerProps {
|
|||||||
setCustomDTPickerVisible?: Dispatch<SetStateAction<boolean>>;
|
setCustomDTPickerVisible?: Dispatch<SetStateAction<boolean>>;
|
||||||
onCustomDateHandler?: (dateTimeRange: DateTimeRangeType) => void;
|
onCustomDateHandler?: (dateTimeRange: DateTimeRangeType) => void;
|
||||||
handleGoLive?: () => void;
|
handleGoLive?: () => void;
|
||||||
onTimeChange?: (
|
|
||||||
interval: Time | CustomTimeType,
|
|
||||||
dateTimeRange?: [number, number],
|
|
||||||
) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function CustomTimePicker({
|
function CustomTimePicker({
|
||||||
@ -79,13 +77,16 @@ function CustomTimePicker({
|
|||||||
setCustomDTPickerVisible,
|
setCustomDTPickerVisible,
|
||||||
onCustomDateHandler,
|
onCustomDateHandler,
|
||||||
handleGoLive,
|
handleGoLive,
|
||||||
onTimeChange,
|
|
||||||
}: CustomTimePickerProps): JSX.Element {
|
}: CustomTimePickerProps): JSX.Element {
|
||||||
const [
|
const [
|
||||||
selectedTimePlaceholderValue,
|
selectedTimePlaceholderValue,
|
||||||
setSelectedTimePlaceholderValue,
|
setSelectedTimePlaceholderValue,
|
||||||
] = useState('Select / Enter Time Range');
|
] = useState('Select / Enter Time Range');
|
||||||
|
|
||||||
|
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
|
||||||
|
(state) => state.globalTime,
|
||||||
|
);
|
||||||
|
|
||||||
const [inputValue, setInputValue] = useState('');
|
const [inputValue, setInputValue] = useState('');
|
||||||
const [inputStatus, setInputStatus] = useState<'' | 'error' | 'success'>('');
|
const [inputStatus, setInputStatus] = useState<'' | 'error' | 'success'>('');
|
||||||
const [inputErrorMessage, setInputErrorMessage] = useState<string | null>(
|
const [inputErrorMessage, setInputErrorMessage] = useState<string | null>(
|
||||||
@ -256,6 +257,11 @@ function CustomTimePicker({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSelect = (label: string, value: string): void => {
|
const handleSelect = (label: string, value: string): void => {
|
||||||
|
if (label === 'Custom') {
|
||||||
|
setCustomDTPickerVisible?.(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
onSelect(value);
|
onSelect(value);
|
||||||
setSelectedTimePlaceholderValue(label);
|
setSelectedTimePlaceholderValue(label);
|
||||||
setInputStatus('');
|
setInputStatus('');
|
||||||
@ -318,84 +324,105 @@ function CustomTimePicker({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getTooltipTitle = (): string => {
|
||||||
|
if (selectedTime === 'custom' && inputValue === '' && !open) {
|
||||||
|
return `${dayjs(minTime / 1000_000)
|
||||||
|
.tz(timezone.value)
|
||||||
|
.format(DATE_TIME_FORMATS.DD_MMM_YYYY_HH_MM_SS)} - ${dayjs(
|
||||||
|
maxTime / 1000_000,
|
||||||
|
)
|
||||||
|
.tz(timezone.value)
|
||||||
|
.format(DATE_TIME_FORMATS.DD_MMM_YYYY_HH_MM_SS)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="custom-time-picker">
|
<div className="custom-time-picker">
|
||||||
<Popover
|
<Tooltip title={getTooltipTitle()} placement="top">
|
||||||
className={cx(
|
<Popover
|
||||||
'timeSelection-input-container',
|
className={cx(
|
||||||
selectedTime === 'custom' && inputValue === '' ? 'custom-time' : '',
|
'timeSelection-input-container',
|
||||||
)}
|
selectedTime === 'custom' && inputValue === '' ? 'custom-time' : '',
|
||||||
placement="bottomRight"
|
)}
|
||||||
getPopupContainer={popupContainer}
|
placement="bottomRight"
|
||||||
rootClassName="date-time-root"
|
getPopupContainer={popupContainer}
|
||||||
content={
|
rootClassName="date-time-root"
|
||||||
newPopover ? (
|
content={
|
||||||
<CustomTimePickerPopoverContent
|
newPopover ? (
|
||||||
setIsOpen={setOpen}
|
<CustomTimePickerPopoverContent
|
||||||
customDateTimeVisible={defaultTo(customDateTimeVisible, false)}
|
setIsOpen={setOpen}
|
||||||
setCustomDTPickerVisible={defaultTo(setCustomDTPickerVisible, noop)}
|
customDateTimeVisible={defaultTo(customDateTimeVisible, false)}
|
||||||
onCustomDateHandler={defaultTo(onCustomDateHandler, noop)}
|
setCustomDTPickerVisible={defaultTo(setCustomDTPickerVisible, noop)}
|
||||||
onSelectHandler={handleSelect}
|
onCustomDateHandler={defaultTo(onCustomDateHandler, noop)}
|
||||||
handleGoLive={defaultTo(handleGoLive, noop)}
|
onSelectHandler={handleSelect}
|
||||||
options={items}
|
handleGoLive={defaultTo(handleGoLive, noop)}
|
||||||
selectedTime={selectedTime}
|
options={items}
|
||||||
activeView={activeView}
|
selectedTime={selectedTime}
|
||||||
setActiveView={setActiveView}
|
activeView={activeView}
|
||||||
setIsOpenedFromFooter={setIsOpenedFromFooter}
|
setActiveView={setActiveView}
|
||||||
isOpenedFromFooter={isOpenedFromFooter}
|
setIsOpenedFromFooter={setIsOpenedFromFooter}
|
||||||
onTimeChange={onTimeChange}
|
isOpenedFromFooter={isOpenedFromFooter}
|
||||||
/>
|
/>
|
||||||
) : (
|
|
||||||
content
|
|
||||||
)
|
|
||||||
}
|
|
||||||
arrow={false}
|
|
||||||
trigger="click"
|
|
||||||
open={open}
|
|
||||||
onOpenChange={handleOpenChange}
|
|
||||||
style={{
|
|
||||||
padding: 0,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
className="timeSelection-input"
|
|
||||||
type="text"
|
|
||||||
status={inputValue && inputStatus === 'error' ? 'error' : ''}
|
|
||||||
placeholder={
|
|
||||||
isInputFocused
|
|
||||||
? 'Time Format (1m or 2h or 3d or 4w)'
|
|
||||||
: selectedTimePlaceholderValue
|
|
||||||
}
|
|
||||||
value={inputValue}
|
|
||||||
onFocus={handleFocus}
|
|
||||||
onBlur={handleBlur}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
data-1p-ignore
|
|
||||||
prefix={
|
|
||||||
inputValue && inputStatus === 'success' ? (
|
|
||||||
<CheckCircle size={14} color="#51E7A8" />
|
|
||||||
) : (
|
) : (
|
||||||
<Tooltip title="Enter time in format (e.g., 1m, 2h, 3d, 4w)">
|
content
|
||||||
<Clock size={14} />
|
|
||||||
</Tooltip>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
suffix={
|
arrow={false}
|
||||||
<>
|
trigger="click"
|
||||||
{!!isTimezoneOverridden && activeTimezoneOffset && (
|
open={open}
|
||||||
<div className="timezone-badge" onClick={handleTimezoneHintClick}>
|
onOpenChange={handleOpenChange}
|
||||||
<span>{activeTimezoneOffset}</span>
|
style={{
|
||||||
</div>
|
padding: 0,
|
||||||
)}
|
}}
|
||||||
<ChevronDown
|
>
|
||||||
size={14}
|
<Input
|
||||||
onClick={(): void => handleViewChange('datetime')}
|
className="timeSelection-input"
|
||||||
/>
|
type="text"
|
||||||
</>
|
status={inputValue && inputStatus === 'error' ? 'error' : ''}
|
||||||
}
|
placeholder={
|
||||||
/>
|
isInputFocused
|
||||||
</Popover>
|
? 'Time Format (1m or 2h or 3d or 4w)'
|
||||||
|
: selectedTimePlaceholderValue
|
||||||
|
}
|
||||||
|
value={inputValue}
|
||||||
|
onFocus={handleFocus}
|
||||||
|
onClick={handleFocus}
|
||||||
|
onBlur={handleBlur}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
data-1p-ignore
|
||||||
|
prefix={
|
||||||
|
<div className="time-input-prefix">
|
||||||
|
{inputValue && inputStatus === 'success' ? (
|
||||||
|
<CheckCircle size={14} color="#51E7A8" />
|
||||||
|
) : (
|
||||||
|
<Tooltip title="Enter time in format (e.g., 1m, 2h, 3d, 4w)">
|
||||||
|
<Clock size={14} className="cursor-pointer" />
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
suffix={
|
||||||
|
<div className="time-input-suffix">
|
||||||
|
{!!isTimezoneOverridden && activeTimezoneOffset && (
|
||||||
|
<div className="timezone-badge" onClick={handleTimezoneHintClick}>
|
||||||
|
<span>{activeTimezoneOffset}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<ChevronDown
|
||||||
|
size={14}
|
||||||
|
className="cursor-pointer time-input-suffix-icon-badge"
|
||||||
|
onClick={(e): void => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleViewChange('datetime');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Popover>
|
||||||
|
</Tooltip>
|
||||||
{inputStatus === 'error' && inputErrorMessage && (
|
{inputStatus === 'error' && inputErrorMessage && (
|
||||||
<Typography.Title level={5} className="valid-format-error">
|
<Typography.Title level={5} className="valid-format-error">
|
||||||
{inputErrorMessage}
|
{inputErrorMessage}
|
||||||
@ -414,5 +441,4 @@ CustomTimePicker.defaultProps = {
|
|||||||
onCustomDateHandler: noop,
|
onCustomDateHandler: noop,
|
||||||
handleGoLive: noop,
|
handleGoLive: noop,
|
||||||
onCustomTimeStatusUpdate: noop,
|
onCustomTimeStatusUpdate: noop,
|
||||||
onTimeChange: undefined,
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4,21 +4,22 @@ import { Color } from '@signozhq/design-tokens';
|
|||||||
import { Button } from 'antd';
|
import { Button } from 'antd';
|
||||||
import logEvent from 'api/common/logEvent';
|
import logEvent from 'api/common/logEvent';
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
|
import DatePickerV2 from 'components/DatePickerV2/DatePickerV2';
|
||||||
|
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import { DateTimeRangeType } from 'container/TopNav/CustomDateTimeModal';
|
import { DateTimeRangeType } from 'container/TopNav/CustomDateTimeModal';
|
||||||
import {
|
import {
|
||||||
CustomTimeType,
|
|
||||||
LexicalContext,
|
LexicalContext,
|
||||||
Option,
|
Option,
|
||||||
RelativeDurationSuggestionOptions,
|
RelativeDurationSuggestionOptions,
|
||||||
Time,
|
|
||||||
} from 'container/TopNav/DateTimeSelectionV2/config';
|
} from 'container/TopNav/DateTimeSelectionV2/config';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
import { Clock, PenLine } from 'lucide-react';
|
import { Clock, PenLine } from 'lucide-react';
|
||||||
import { useTimezone } from 'providers/Timezone';
|
import { useTimezone } from 'providers/Timezone';
|
||||||
import { Dispatch, SetStateAction, useMemo } from 'react';
|
import { Dispatch, SetStateAction, useEffect, useMemo, useState } from 'react';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
|
import { getCustomTimeRanges } from 'utils/customTimeRangeUtils';
|
||||||
|
|
||||||
import RangePickerModal from './RangePickerModal';
|
|
||||||
import TimezonePicker from './TimezonePicker';
|
import TimezonePicker from './TimezonePicker';
|
||||||
|
|
||||||
interface CustomTimePickerPopoverContentProps {
|
interface CustomTimePickerPopoverContentProps {
|
||||||
@ -37,10 +38,14 @@ interface CustomTimePickerPopoverContentProps {
|
|||||||
setActiveView: Dispatch<SetStateAction<'datetime' | 'timezone'>>;
|
setActiveView: Dispatch<SetStateAction<'datetime' | 'timezone'>>;
|
||||||
isOpenedFromFooter: boolean;
|
isOpenedFromFooter: boolean;
|
||||||
setIsOpenedFromFooter: Dispatch<SetStateAction<boolean>>;
|
setIsOpenedFromFooter: Dispatch<SetStateAction<boolean>>;
|
||||||
onTimeChange?: (
|
}
|
||||||
interval: Time | CustomTimeType,
|
|
||||||
dateTimeRange?: [number, number],
|
interface RecentlyUsedDateTimeRange {
|
||||||
) => void;
|
label: string;
|
||||||
|
value: number;
|
||||||
|
timestamp: number;
|
||||||
|
from: string;
|
||||||
|
to: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
@ -57,16 +62,42 @@ function CustomTimePickerPopoverContent({
|
|||||||
setActiveView,
|
setActiveView,
|
||||||
isOpenedFromFooter,
|
isOpenedFromFooter,
|
||||||
setIsOpenedFromFooter,
|
setIsOpenedFromFooter,
|
||||||
onTimeChange,
|
|
||||||
}: CustomTimePickerPopoverContentProps): JSX.Element {
|
}: CustomTimePickerPopoverContentProps): JSX.Element {
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
|
|
||||||
const isLogsExplorerPage = useMemo(() => pathname === ROUTES.LOGS_EXPLORER, [
|
const isLogsExplorerPage = useMemo(() => pathname === ROUTES.LOGS_EXPLORER, [
|
||||||
pathname,
|
pathname,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const { timezone } = useTimezone();
|
const { timezone } = useTimezone();
|
||||||
const activeTimezoneOffset = timezone.offset;
|
const activeTimezoneOffset = timezone.offset;
|
||||||
|
|
||||||
|
const [recentlyUsedTimeRanges, setRecentlyUsedTimeRanges] = useState<
|
||||||
|
RecentlyUsedDateTimeRange[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!customDateTimeVisible) {
|
||||||
|
const customTimeRanges = getCustomTimeRanges();
|
||||||
|
|
||||||
|
const formattedCustomTimeRanges: RecentlyUsedDateTimeRange[] = customTimeRanges.map(
|
||||||
|
(range) => ({
|
||||||
|
label: `${dayjs(range.from)
|
||||||
|
.tz(timezone.value)
|
||||||
|
.format(DATE_TIME_FORMATS.DD_MMM_YYYY_HH_MM_SS)} - ${dayjs(range.to)
|
||||||
|
.tz(timezone.value)
|
||||||
|
.format(DATE_TIME_FORMATS.DD_MMM_YYYY_HH_MM_SS)}`,
|
||||||
|
from: range.from,
|
||||||
|
to: range.to,
|
||||||
|
value: range.timestamp,
|
||||||
|
timestamp: range.timestamp,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
setRecentlyUsedTimeRanges(formattedCustomTimeRanges);
|
||||||
|
}
|
||||||
|
}, [customDateTimeVisible, timezone.value]);
|
||||||
|
|
||||||
function getTimeChips(options: Option[]): JSX.Element {
|
function getTimeChips(options: Option[]): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<div className="relative-date-time-section">
|
<div className="relative-date-time-section">
|
||||||
@ -112,50 +143,77 @@ function CustomTimePickerPopoverContent({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="date-time-popover">
|
<div className="date-time-popover">
|
||||||
<div className="date-time-options">
|
{!customDateTimeVisible && (
|
||||||
{isLogsExplorerPage && (
|
<div className="date-time-options">
|
||||||
<Button className="data-time-live" type="text" onClick={handleGoLive}>
|
{isLogsExplorerPage && (
|
||||||
Live
|
<Button className="data-time-live" type="text" onClick={handleGoLive}>
|
||||||
</Button>
|
Live
|
||||||
)}
|
</Button>
|
||||||
{options.map((option) => (
|
)}
|
||||||
<Button
|
{options.map((option) => (
|
||||||
type="text"
|
<Button
|
||||||
key={option.label + option.value}
|
type="text"
|
||||||
onClick={(): void => {
|
key={option.label + option.value}
|
||||||
onSelectHandler(option.label, option.value);
|
onClick={(): void => {
|
||||||
}}
|
onSelectHandler(option.label, option.value);
|
||||||
className={cx(
|
}}
|
||||||
'date-time-options-btn',
|
className={cx(
|
||||||
customDateTimeVisible
|
'date-time-options-btn',
|
||||||
? option.value === 'custom' && 'active'
|
customDateTimeVisible
|
||||||
: selectedTime === option.value && 'active',
|
? option.value === 'custom' && 'active'
|
||||||
)}
|
: selectedTime === option.value && 'active',
|
||||||
>
|
)}
|
||||||
{option.label}
|
>
|
||||||
</Button>
|
{option.label}
|
||||||
))}
|
</Button>
|
||||||
</div>
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={cx(
|
className={cx(
|
||||||
'relative-date-time',
|
'relative-date-time',
|
||||||
selectedTime === 'custom' || customDateTimeVisible
|
customDateTimeVisible ? 'date-picker' : 'relative-times',
|
||||||
? 'date-picker'
|
|
||||||
: 'relative-times',
|
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{selectedTime === 'custom' || customDateTimeVisible ? (
|
{customDateTimeVisible ? (
|
||||||
<RangePickerModal
|
<DatePickerV2
|
||||||
setCustomDTPickerVisible={setCustomDTPickerVisible}
|
onSetCustomDTPickerVisible={setCustomDTPickerVisible}
|
||||||
setIsOpen={setIsOpen}
|
setIsOpen={setIsOpen}
|
||||||
onCustomDateHandler={onCustomDateHandler}
|
onCustomDateHandler={onCustomDateHandler}
|
||||||
selectedTime={selectedTime}
|
|
||||||
onTimeChange={onTimeChange}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="relative-times-container">
|
<div className="time-selector-container">
|
||||||
<div className="time-heading">RELATIVE TIMES</div>
|
<div className="relative-times-container">
|
||||||
<div>{getTimeChips(RelativeDurationSuggestionOptions)}</div>
|
<div className="time-heading">RELATIVE TIMES</div>
|
||||||
|
<div>{getTimeChips(RelativeDurationSuggestionOptions)}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="recently-used-container">
|
||||||
|
<div className="time-heading">RECENTLY USED</div>
|
||||||
|
<div className="recently-used-range">
|
||||||
|
{recentlyUsedTimeRanges.map((range: RecentlyUsedDateTimeRange) => (
|
||||||
|
<div
|
||||||
|
className="recently-used-range-item"
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
onKeyDown={(e): void => {
|
||||||
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
|
onCustomDateHandler([dayjs(range.from), dayjs(range.to)]);
|
||||||
|
setIsOpen(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
key={range.value}
|
||||||
|
onClick={(): void => {
|
||||||
|
onCustomDateHandler([dayjs(range.from), dayjs(range.to)]);
|
||||||
|
setIsOpen(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{range.label}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -189,8 +247,4 @@ function CustomTimePickerPopoverContent({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomTimePickerPopoverContent.defaultProps = {
|
|
||||||
onTimeChange: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CustomTimePickerPopoverContent;
|
export default CustomTimePickerPopoverContent;
|
||||||
|
|||||||
114
frontend/src/components/DatePickerV2/DatePickerV2.styles.scss
Normal file
114
frontend/src/components/DatePickerV2/DatePickerV2.styles.scss
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
.date-picker-v2-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-date-time-picker-v2 {
|
||||||
|
padding: 12px;
|
||||||
|
|
||||||
|
.periscope-calendar {
|
||||||
|
border-radius: 4px;
|
||||||
|
border: none !important;
|
||||||
|
background: none !important;
|
||||||
|
padding: 8px 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.periscope-calendar-day {
|
||||||
|
background: none !important;
|
||||||
|
|
||||||
|
&.periscope-calendar-today {
|
||||||
|
&.text-accent-foreground {
|
||||||
|
color: var(--bg-vanilla-100) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--bg-robin-500) !important;
|
||||||
|
color: var(--bg-vanilla-100) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-time-selector {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 16px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.time-input {
|
||||||
|
border-radius: 4px;
|
||||||
|
border: none !important;
|
||||||
|
background: none !important;
|
||||||
|
padding: 8px 4px !important;
|
||||||
|
color: var(--bg-vanilla-100) !important;
|
||||||
|
|
||||||
|
&::-webkit-calendar-picker-indicator {
|
||||||
|
display: none !important;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border: none !important;
|
||||||
|
outline: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
border: none !important;
|
||||||
|
outline: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-date-time-picker-footer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 16px;
|
||||||
|
|
||||||
|
.next-btn {
|
||||||
|
width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-btn {
|
||||||
|
width: 80px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.invalid-date-range-tooltip {
|
||||||
|
.ant-tooltip-inner {
|
||||||
|
color: var(--bg-sakura-500) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.lightMode {
|
||||||
|
.custom-date-time-picker-v2 {
|
||||||
|
.periscope-calendar-day {
|
||||||
|
&.periscope-calendar-today {
|
||||||
|
&.text-accent-foreground {
|
||||||
|
color: var(--bg-ink-500) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--bg-robin-500) !important;
|
||||||
|
color: var(--bg-ink-500) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-time-selector {
|
||||||
|
.time-input {
|
||||||
|
color: var(--bg-ink-500) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
311
frontend/src/components/DatePickerV2/DatePickerV2.tsx
Normal file
311
frontend/src/components/DatePickerV2/DatePickerV2.tsx
Normal file
@ -0,0 +1,311 @@
|
|||||||
|
import './DatePickerV2.styles.scss';
|
||||||
|
|
||||||
|
import { Calendar } from '@signozhq/calendar';
|
||||||
|
import { Input } from '@signozhq/input';
|
||||||
|
import { Button, Tooltip } from 'antd';
|
||||||
|
import cx from 'classnames';
|
||||||
|
import { DateTimeRangeType } from 'container/TopNav/CustomDateTimeModal';
|
||||||
|
import { LexicalContext } from 'container/TopNav/DateTimeSelectionV2/config';
|
||||||
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
|
import { CornerUpLeft, MoveRight } from 'lucide-react';
|
||||||
|
import { useTimezone } from 'providers/Timezone';
|
||||||
|
import { useRef, useState } from 'react';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { AppState } from 'store/reducers';
|
||||||
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
import { addCustomTimeRange } from 'utils/customTimeRangeUtils';
|
||||||
|
|
||||||
|
function DatePickerV2({
|
||||||
|
onSetCustomDTPickerVisible,
|
||||||
|
setIsOpen,
|
||||||
|
onCustomDateHandler,
|
||||||
|
}: {
|
||||||
|
onSetCustomDTPickerVisible: (visible: boolean) => void;
|
||||||
|
setIsOpen: (isOpen: boolean) => void;
|
||||||
|
onCustomDateHandler: (
|
||||||
|
dateTimeRange: DateTimeRangeType,
|
||||||
|
lexicalContext?: LexicalContext,
|
||||||
|
) => void;
|
||||||
|
}): JSX.Element {
|
||||||
|
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
|
||||||
|
(state) => state.globalTime,
|
||||||
|
);
|
||||||
|
|
||||||
|
const timeInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
const { timezone } = useTimezone();
|
||||||
|
|
||||||
|
const [selectedDateTimeFor, setSelectedDateTimeFor] = useState<'to' | 'from'>(
|
||||||
|
'from',
|
||||||
|
);
|
||||||
|
|
||||||
|
const [selectedFromDateTime, setSelectedFromDateTime] = useState<Dayjs | null>(
|
||||||
|
dayjs(minTime / 1000_000).tz(timezone.value),
|
||||||
|
);
|
||||||
|
|
||||||
|
const [selectedToDateTime, setSelectedToDateTime] = useState<Dayjs | null>(
|
||||||
|
dayjs(maxTime / 1000_000).tz(timezone.value),
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleNext = (): void => {
|
||||||
|
if (selectedDateTimeFor === 'to') {
|
||||||
|
onCustomDateHandler([selectedFromDateTime, selectedToDateTime]);
|
||||||
|
|
||||||
|
addCustomTimeRange([selectedFromDateTime, selectedToDateTime]);
|
||||||
|
|
||||||
|
setIsOpen(false);
|
||||||
|
onSetCustomDTPickerVisible(false);
|
||||||
|
setSelectedDateTimeFor('from');
|
||||||
|
} else {
|
||||||
|
setSelectedDateTimeFor('to');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDateChange = (date: Date | undefined): void => {
|
||||||
|
if (!date) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedDateTimeFor === 'from') {
|
||||||
|
const prevFromDateTime = selectedFromDateTime;
|
||||||
|
|
||||||
|
const newDate = dayjs(date);
|
||||||
|
|
||||||
|
const updatedFromDateTime = prevFromDateTime
|
||||||
|
? prevFromDateTime
|
||||||
|
.year(newDate.year())
|
||||||
|
.month(newDate.month())
|
||||||
|
.date(newDate.date())
|
||||||
|
: dayjs(date).tz(timezone.value);
|
||||||
|
|
||||||
|
setSelectedFromDateTime(updatedFromDateTime);
|
||||||
|
} else {
|
||||||
|
// eslint-disable-next-line sonarjs/no-identical-functions
|
||||||
|
setSelectedToDateTime((prev) => {
|
||||||
|
const newDate = dayjs(date);
|
||||||
|
|
||||||
|
// Update only the date part, keeping time from existing state
|
||||||
|
return prev
|
||||||
|
? prev.year(newDate.year()).month(newDate.month()).date(newDate.date())
|
||||||
|
: dayjs(date).tz(timezone.value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// focus the time input
|
||||||
|
timeInputRef?.current?.focus();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTimeChange = (time: string): void => {
|
||||||
|
// time should have format HH:mm:ss
|
||||||
|
if (!/^\d{2}:\d{2}:\d{2}$/.test(time)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedDateTimeFor === 'from') {
|
||||||
|
setSelectedFromDateTime((prev) => {
|
||||||
|
if (prev) {
|
||||||
|
return prev
|
||||||
|
.set('hour', parseInt(time.split(':')[0], 10))
|
||||||
|
.set('minute', parseInt(time.split(':')[1], 10))
|
||||||
|
.set('second', parseInt(time.split(':')[2], 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
return prev;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (selectedDateTimeFor === 'to') {
|
||||||
|
// eslint-disable-next-line sonarjs/no-identical-functions
|
||||||
|
setSelectedToDateTime((prev) => {
|
||||||
|
if (prev) {
|
||||||
|
return prev
|
||||||
|
.set('hour', parseInt(time.split(':')[0], 10))
|
||||||
|
.set('minute', parseInt(time.split(':')[1], 10))
|
||||||
|
.set('second', parseInt(time.split(':')[2], 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
return prev;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDefaultMonth = (): Date => {
|
||||||
|
let defaultDate = null;
|
||||||
|
|
||||||
|
if (selectedDateTimeFor === 'from') {
|
||||||
|
defaultDate = selectedFromDateTime?.toDate();
|
||||||
|
} else if (selectedDateTimeFor === 'to') {
|
||||||
|
defaultDate = selectedToDateTime?.toDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultDate ?? new Date();
|
||||||
|
};
|
||||||
|
|
||||||
|
const isValidRange = (): boolean => {
|
||||||
|
if (selectedDateTimeFor === 'to') {
|
||||||
|
return selectedToDateTime?.isAfter(selectedFromDateTime) ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBack = (): void => {
|
||||||
|
setSelectedDateTimeFor('from');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleHideCustomDTPicker = (): void => {
|
||||||
|
onSetCustomDTPickerVisible(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSelectDateTimeFor = (selectedDateTimeFor: 'to' | 'from'): void => {
|
||||||
|
setSelectedDateTimeFor(selectedDateTimeFor);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="date-picker-v2-container">
|
||||||
|
<div className="date-time-custom-options-container">
|
||||||
|
<div
|
||||||
|
className="back-btn"
|
||||||
|
onClick={handleHideCustomDTPicker}
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
onKeyDown={(e): void => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
handleHideCustomDTPicker();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CornerUpLeft size={16} />
|
||||||
|
<span>Back</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="date-time-custom-options">
|
||||||
|
<div
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
onKeyDown={(e): void => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
handleSelectDateTimeFor('from');
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className={cx(
|
||||||
|
'date-time-custom-option-from',
|
||||||
|
selectedDateTimeFor === 'from' && 'active',
|
||||||
|
)}
|
||||||
|
onClick={(): void => {
|
||||||
|
handleSelectDateTimeFor('from');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="date-time-custom-option-from-title">FROM</div>
|
||||||
|
<div className="date-time-custom-option-from-value">
|
||||||
|
{selectedFromDateTime?.format('YYYY-MM-DD HH:mm:ss')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
onKeyDown={(e): void => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
handleSelectDateTimeFor('to');
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className={cx(
|
||||||
|
'date-time-custom-option-to',
|
||||||
|
selectedDateTimeFor === 'to' && 'active',
|
||||||
|
)}
|
||||||
|
onClick={(): void => {
|
||||||
|
handleSelectDateTimeFor('to');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="date-time-custom-option-to-title">TO</div>
|
||||||
|
<div className="date-time-custom-option-to-value">
|
||||||
|
{selectedToDateTime?.format('YYYY-MM-DD HH:mm:ss')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="custom-date-time-picker-v2">
|
||||||
|
<Calendar
|
||||||
|
mode="single"
|
||||||
|
required
|
||||||
|
selected={
|
||||||
|
selectedDateTimeFor === 'from'
|
||||||
|
? selectedFromDateTime?.toDate()
|
||||||
|
: selectedToDateTime?.toDate()
|
||||||
|
}
|
||||||
|
key={selectedDateTimeFor + selectedDateTimeFor}
|
||||||
|
onSelect={handleDateChange}
|
||||||
|
defaultMonth={getDefaultMonth()}
|
||||||
|
disabled={(current): boolean => {
|
||||||
|
if (selectedDateTimeFor === 'to') {
|
||||||
|
// disable dates after today and before selectedFromDateTime
|
||||||
|
const currentDay = dayjs(current);
|
||||||
|
return currentDay.isAfter(dayjs()) || false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedDateTimeFor === 'from') {
|
||||||
|
// disable dates after selectedToDateTime
|
||||||
|
|
||||||
|
return dayjs(current).isAfter(dayjs()) || false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}}
|
||||||
|
className="rounded-md border"
|
||||||
|
navLayout="after"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="custom-time-selector">
|
||||||
|
<label className="text-xs font-normal block" htmlFor="time-picker">
|
||||||
|
Timestamp
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<MoveRight size={16} />
|
||||||
|
|
||||||
|
<div className="time-input-container">
|
||||||
|
<Input
|
||||||
|
type="time"
|
||||||
|
ref={timeInputRef}
|
||||||
|
className="time-input"
|
||||||
|
value={
|
||||||
|
selectedDateTimeFor === 'from'
|
||||||
|
? selectedFromDateTime?.format('HH:mm:ss')
|
||||||
|
: selectedToDateTime?.format('HH:mm:ss')
|
||||||
|
}
|
||||||
|
onChange={(e): void => handleTimeChange(e.target.value)}
|
||||||
|
step="1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="custom-date-time-picker-footer">
|
||||||
|
{selectedDateTimeFor === 'to' && (
|
||||||
|
<Button
|
||||||
|
className="periscope-btn secondary clear-btn"
|
||||||
|
type="default"
|
||||||
|
onClick={handleBack}
|
||||||
|
>
|
||||||
|
Back
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Tooltip
|
||||||
|
title={
|
||||||
|
!isValidRange() ? 'Invalid range: TO date should be after FROM date' : ''
|
||||||
|
}
|
||||||
|
overlayClassName="invalid-date-range-tooltip"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
className="periscope-btn primary next-btn"
|
||||||
|
type="primary"
|
||||||
|
onClick={handleNext}
|
||||||
|
disabled={!isValidRange()}
|
||||||
|
>
|
||||||
|
{selectedDateTimeFor === 'from' ? 'Next' : 'Apply'}
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DatePickerV2;
|
||||||
@ -39,6 +39,8 @@ export const DATE_TIME_FORMATS = {
|
|||||||
MONTH_DATETIME_SECONDS: 'MMM DD YYYY HH:mm:ss',
|
MONTH_DATETIME_SECONDS: 'MMM DD YYYY HH:mm:ss',
|
||||||
MONTH_DATETIME_FULL: 'MMMM DD, YYYY HH:mm',
|
MONTH_DATETIME_FULL: 'MMMM DD, YYYY HH:mm',
|
||||||
MONTH_DATETIME_FULL_SECONDS: 'MMM DD, YYYY, HH:mm:ss',
|
MONTH_DATETIME_FULL_SECONDS: 'MMM DD, YYYY, HH:mm:ss',
|
||||||
|
DD_MMM_YYYY_HH_MM: 'DD MMM YYYY, HH:mm',
|
||||||
|
DD_MMM_YYYY_HH_MM_SS: 'DD MMM YYYY, HH:mm:ss',
|
||||||
|
|
||||||
// Ordinal formats (1st, 2nd, 3rd, etc)
|
// Ordinal formats (1st, 2nd, 3rd, etc)
|
||||||
ORDINAL_DATE: 'Do MMM YYYY',
|
ORDINAL_DATE: 'Do MMM YYYY',
|
||||||
|
|||||||
@ -32,4 +32,5 @@ export enum LOCALSTORAGE {
|
|||||||
BANNER_DISMISSED = 'BANNER_DISMISSED',
|
BANNER_DISMISSED = 'BANNER_DISMISSED',
|
||||||
QUICK_FILTERS_SETTINGS_ANNOUNCEMENT = 'QUICK_FILTERS_SETTINGS_ANNOUNCEMENT',
|
QUICK_FILTERS_SETTINGS_ANNOUNCEMENT = 'QUICK_FILTERS_SETTINGS_ANNOUNCEMENT',
|
||||||
FUNNEL_STEPS = 'FUNNEL_STEPS',
|
FUNNEL_STEPS = 'FUNNEL_STEPS',
|
||||||
|
LAST_USED_CUSTOM_TIME_RANGES = 'LAST_USED_CUSTOM_TIME_RANGES',
|
||||||
}
|
}
|
||||||
|
|||||||
@ -54,7 +54,7 @@
|
|||||||
border: none;
|
border: none;
|
||||||
|
|
||||||
&.active-tab {
|
&.active-tab {
|
||||||
background-color: #1d212d;
|
background-color: var(--bg-slate-400);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -146,20 +146,20 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
width: 224px;
|
width: 224px;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
border-right: 1px solid #1d212d;
|
border-right: 1px solid var(--bg-slate-400);
|
||||||
|
|
||||||
.data-time-live {
|
.data-time-live {
|
||||||
border-bottom: 1px solid #1d212d;
|
border-bottom: 1px solid var(--bg-slate-400);
|
||||||
text-align: start;
|
text-align: start;
|
||||||
padding: 13.5px 14px;
|
padding: 13.5px 14px;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
color: var(--bg-vanilla-400, #c0c1c3);
|
color: var(--bg-vanilla-400);
|
||||||
font-size: 14px;
|
font-size: 13px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
letter-spacing: 0.14px;
|
letter-spacing: 0.14px;
|
||||||
border-bottom: 1px solid var(--bg-slate-400, #1d212d);
|
border-bottom: 1px solid var(--bg-slate-400);
|
||||||
}
|
}
|
||||||
|
|
||||||
.active {
|
.active {
|
||||||
@ -176,8 +176,8 @@
|
|||||||
text-align: start;
|
text-align: start;
|
||||||
padding: 8px 13px;
|
padding: 8px 13px;
|
||||||
height: 37px;
|
height: 37px;
|
||||||
color: var(--bg-vanilla-400, #c0c1c3);
|
color: var(--bg-vanilla-400);
|
||||||
font-size: 14px;
|
font-size: 13px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
@ -190,6 +190,80 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.date-time-custom-options-container {
|
||||||
|
display: flex;
|
||||||
|
width: 224px;
|
||||||
|
flex-direction: column;
|
||||||
|
border-right: 1px solid var(--bg-slate-400);
|
||||||
|
|
||||||
|
.back-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(171, 189, 255, 0.04);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-time-custom-options {
|
||||||
|
.date-time-custom-option-from,
|
||||||
|
.date-time-custom-option-to {
|
||||||
|
display: flex;
|
||||||
|
padding: 12px 14px;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-time-custom-option-from-title,
|
||||||
|
.date-time-custom-option-to-title {
|
||||||
|
color: var(--bg-slate-50);
|
||||||
|
font-variant-numeric: slashed-zero;
|
||||||
|
font-family: Inter;
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: normal;
|
||||||
|
letter-spacing: 0.12px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-time-custom-option-from-value,
|
||||||
|
.date-time-custom-option-to-value {
|
||||||
|
color: var(--bg-vanilla-400);
|
||||||
|
font-variant-numeric: slashed-zero;
|
||||||
|
font-family: Inter;
|
||||||
|
font-size: 13px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300;
|
||||||
|
line-height: normal;
|
||||||
|
letter-spacing: 0.14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: rgba(171, 189, 255, 0.04);
|
||||||
|
|
||||||
|
.date-time-custom-option-from-title,
|
||||||
|
.date-time-custom-option-to-title {
|
||||||
|
color: var(--bg-vanilla-400);
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-time-custom-option-from-value,
|
||||||
|
.date-time-custom-option-to-value {
|
||||||
|
color: var(--bg-vanilla-100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.relative-date-time {
|
.relative-date-time {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -197,12 +271,11 @@
|
|||||||
padding: 13px 14px;
|
padding: 13px 14px;
|
||||||
|
|
||||||
&.date-picker {
|
&.date-picker {
|
||||||
width: 480px;
|
padding: 0px;
|
||||||
height: 430px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.relative-times {
|
&.relative-times {
|
||||||
width: 320px;
|
width: 360px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.relative-date-time-section {
|
.relative-date-time-section {
|
||||||
@ -210,9 +283,45 @@
|
|||||||
gap: 6px;
|
gap: 6px;
|
||||||
flex-flow: wrap;
|
flex-flow: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.time-selector-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recently-used-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.recently-used-range {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.recently-used-range-item {
|
||||||
|
color: var(--bg-vanilla-400);
|
||||||
|
background-color: var(--bg-ink-200);
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 16px;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
text-align: left;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--bg-ink-100);
|
||||||
|
color: var(--bg-vanilla-100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.time-heading {
|
.time-heading {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
color: var(--bg-slate-200);
|
color: var(--bg-vanilla-300);
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@ -224,8 +333,8 @@
|
|||||||
.time-btns {
|
.time-btns {
|
||||||
color: var(--bg-vanilla-400);
|
color: var(--bg-vanilla-400);
|
||||||
background-color: #23262e;
|
background-color: #23262e;
|
||||||
font-size: 14px;
|
font-size: 12px;
|
||||||
line-height: 17px;
|
line-height: 16px;
|
||||||
letter-spacing: 0.04em;
|
letter-spacing: 0.04em;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
@ -288,6 +397,54 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.date-time-custom-options-container {
|
||||||
|
display: flex;
|
||||||
|
width: 224px;
|
||||||
|
flex-direction: column;
|
||||||
|
border-right: 1px solid var(--bg-vanilla-400);
|
||||||
|
|
||||||
|
.back-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--bg-vanilla-300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-time-custom-options {
|
||||||
|
.date-time-custom-option-from,
|
||||||
|
.date-time-custom-option-to {
|
||||||
|
.date-time-custom-option-from-title,
|
||||||
|
.date-time-custom-option-to-title {
|
||||||
|
color: var(--bg-slate-50);
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-time-custom-option-from-value,
|
||||||
|
.date-time-custom-option-to-value {
|
||||||
|
color: var(--bg-slate-400);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: var(--bg-vanilla-300);
|
||||||
|
|
||||||
|
.date-time-custom-option-from-title,
|
||||||
|
.date-time-custom-option-to-title {
|
||||||
|
color: var(--bg-slate-400);
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-time-custom-option-from-value,
|
||||||
|
.date-time-custom-option-to-value {
|
||||||
|
color: var(--bg-slate-400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.relative-date-time {
|
.relative-date-time {
|
||||||
.time-heading {
|
.time-heading {
|
||||||
color: var(--bg-vanilla-400);
|
color: var(--bg-vanilla-400);
|
||||||
@ -297,6 +454,20 @@
|
|||||||
background-color: var(--bg-vanilla-300);
|
background-color: var(--bg-vanilla-300);
|
||||||
color: var(--bg-slate-400);
|
color: var(--bg-slate-400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.recently-used-container {
|
||||||
|
.recently-used-range {
|
||||||
|
.recently-used-range-item {
|
||||||
|
color: var(--bg-slate-400);
|
||||||
|
background-color: var(--bg-vanilla-300);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--bg-vanilla-300);
|
||||||
|
color: var(--bg-slate-400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -248,7 +248,7 @@ function DateTimeSelection({
|
|||||||
timeInterval: Time | CustomTimeType = '15m',
|
timeInterval: Time | CustomTimeType = '15m',
|
||||||
): string | Time => {
|
): string | Time => {
|
||||||
if (startTime && endTime && timeInterval === 'custom') {
|
if (startTime && endTime && timeInterval === 'custom') {
|
||||||
const format = DATE_TIME_FORMATS.UK_DATETIME;
|
const format = DATE_TIME_FORMATS.UK_DATETIME_SECONDS;
|
||||||
|
|
||||||
const startString = startTime.format(format);
|
const startString = startTime.format(format);
|
||||||
const endString = endTime.format(format);
|
const endString = endTime.format(format);
|
||||||
@ -803,6 +803,17 @@ function DateTimeSelection({
|
|||||||
|
|
||||||
const { timezone } = useTimezone();
|
const { timezone } = useTimezone();
|
||||||
|
|
||||||
|
const getSelectedValue = (): string =>
|
||||||
|
getInputLabel(
|
||||||
|
dayjs(isModalTimeSelection ? modalStartTime : minTime / 1000000).tz(
|
||||||
|
timezone.value,
|
||||||
|
),
|
||||||
|
dayjs(isModalTimeSelection ? modalEndTime : maxTime / 1000000).tz(
|
||||||
|
timezone.value,
|
||||||
|
),
|
||||||
|
isModalTimeSelection ? modalSelectedInterval : selectedTime,
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="date-time-selector">
|
<div className="date-time-selector">
|
||||||
{showResetButton && selectedTime !== defaultRelativeTime && (
|
{showResetButton && selectedTime !== defaultRelativeTime && (
|
||||||
@ -859,15 +870,7 @@ function DateTimeSelection({
|
|||||||
onCustomTimeStatusUpdate={(isValid: boolean): void => {
|
onCustomTimeStatusUpdate={(isValid: boolean): void => {
|
||||||
setIsValidteRelativeTime(isValid);
|
setIsValidteRelativeTime(isValid);
|
||||||
}}
|
}}
|
||||||
selectedValue={getInputLabel(
|
selectedValue={getSelectedValue()}
|
||||||
dayjs(isModalTimeSelection ? modalStartTime : minTime / 1000000).tz(
|
|
||||||
timezone.value,
|
|
||||||
),
|
|
||||||
dayjs(isModalTimeSelection ? modalEndTime : maxTime / 1000000).tz(
|
|
||||||
timezone.value,
|
|
||||||
),
|
|
||||||
isModalTimeSelection ? modalSelectedInterval : selectedTime,
|
|
||||||
)}
|
|
||||||
data-testid="dropDown"
|
data-testid="dropDown"
|
||||||
items={options}
|
items={options}
|
||||||
newPopover
|
newPopover
|
||||||
@ -875,7 +878,6 @@ function DateTimeSelection({
|
|||||||
onCustomDateHandler={onCustomDateHandler}
|
onCustomDateHandler={onCustomDateHandler}
|
||||||
customDateTimeVisible={customDateTimeVisible}
|
customDateTimeVisible={customDateTimeVisible}
|
||||||
setCustomDTPickerVisible={setCustomDTPickerVisible}
|
setCustomDTPickerVisible={setCustomDTPickerVisible}
|
||||||
onTimeChange={onTimeChange}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{showAutoRefresh && selectedTime !== 'custom' && (
|
{showAutoRefresh && selectedTime !== 'custom' && (
|
||||||
|
|||||||
112
frontend/src/hooks/useCustomTimeRanges.ts
Normal file
112
frontend/src/hooks/useCustomTimeRanges.ts
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import { DateTimeRangeType } from 'container/TopNav/CustomDateTimeModal';
|
||||||
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
import {
|
||||||
|
addCustomTimeRange as addRange,
|
||||||
|
clearCustomTimeRanges as clearRanges,
|
||||||
|
CustomTimeRange,
|
||||||
|
getCustomTimeRanges as getRanges,
|
||||||
|
removeCustomTimeRange as removeRange,
|
||||||
|
} from 'utils/customTimeRangeUtils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom hook for managing the last 3 used custom time ranges
|
||||||
|
* @returns Object containing custom time ranges and management functions
|
||||||
|
*/
|
||||||
|
export const useCustomTimeRanges = (): {
|
||||||
|
customTimeRanges: CustomTimeRange[];
|
||||||
|
addCustomTimeRange: (dateTimeRange: DateTimeRangeType) => CustomTimeRange[];
|
||||||
|
removeCustomTimeRange: (timestamp: number) => CustomTimeRange[];
|
||||||
|
clearCustomTimeRanges: () => void;
|
||||||
|
isCustomTimeRangeExists: (dateTimeRange: DateTimeRangeType) => boolean;
|
||||||
|
refreshCustomTimeRanges: () => void;
|
||||||
|
} => {
|
||||||
|
const [customTimeRanges, setCustomTimeRanges] = useState<CustomTimeRange[]>(
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Always get the latest data from localStorage
|
||||||
|
const getLatestRanges = useCallback((): CustomTimeRange[] => getRanges(), []);
|
||||||
|
|
||||||
|
// Load initial data from localStorage
|
||||||
|
useEffect(() => {
|
||||||
|
const ranges = getLatestRanges();
|
||||||
|
setCustomTimeRanges(ranges);
|
||||||
|
}, [getLatestRanges]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new custom time range to the stored list
|
||||||
|
* @param dateTimeRange - The DateTimeRangeType from the time picker
|
||||||
|
* @returns The updated list of custom time ranges
|
||||||
|
*/
|
||||||
|
const addCustomTimeRange = useCallback(
|
||||||
|
(dateTimeRange: DateTimeRangeType): CustomTimeRange[] => {
|
||||||
|
const updatedRanges = addRange(dateTimeRange);
|
||||||
|
setCustomTimeRanges(updatedRanges);
|
||||||
|
return updatedRanges;
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a specific custom time range by its timestamp
|
||||||
|
* @param timestamp - The timestamp of the range to remove
|
||||||
|
* @returns The updated list of custom time ranges
|
||||||
|
*/
|
||||||
|
const removeCustomTimeRange = useCallback(
|
||||||
|
(timestamp: number): CustomTimeRange[] => {
|
||||||
|
const updatedRanges = removeRange(timestamp);
|
||||||
|
setCustomTimeRanges(updatedRanges);
|
||||||
|
return updatedRanges;
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all stored custom time ranges
|
||||||
|
*/
|
||||||
|
const clearCustomTimeRanges = useCallback((): void => {
|
||||||
|
clearRanges();
|
||||||
|
setCustomTimeRanges([]);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a time range already exists in the stored list
|
||||||
|
* @param dateTimeRange - The DateTimeRangeType to check
|
||||||
|
* @returns True if the range already exists
|
||||||
|
*/
|
||||||
|
const isCustomTimeRangeExists = useCallback(
|
||||||
|
(dateTimeRange: DateTimeRangeType): boolean => {
|
||||||
|
if (!dateTimeRange || !dateTimeRange[0] || !dateTimeRange[1]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [startTime, endTime] = dateTimeRange;
|
||||||
|
// Always fetch latest data from localStorage
|
||||||
|
const latestRanges = getLatestRanges();
|
||||||
|
return latestRanges.some(
|
||||||
|
(range) =>
|
||||||
|
range.from === startTime.toISOString() &&
|
||||||
|
range.to === endTime.toISOString(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[getLatestRanges],
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refreshes the custom time ranges from localStorage
|
||||||
|
* Useful when you want to sync with changes made outside the hook
|
||||||
|
*/
|
||||||
|
const refreshCustomTimeRanges = useCallback((): void => {
|
||||||
|
const ranges = getLatestRanges();
|
||||||
|
setCustomTimeRanges(ranges);
|
||||||
|
}, [getLatestRanges]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
customTimeRanges,
|
||||||
|
addCustomTimeRange,
|
||||||
|
removeCustomTimeRange,
|
||||||
|
clearCustomTimeRanges,
|
||||||
|
isCustomTimeRangeExists,
|
||||||
|
refreshCustomTimeRanges,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -755,3 +755,7 @@ notifications - 2050
|
|||||||
.service-map-container {
|
.service-map-container {
|
||||||
padding: 0px 8px;
|
padding: 0px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cursor-pointer {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|||||||
175
frontend/src/utils/customTimeRangeUtils.ts
Normal file
175
frontend/src/utils/customTimeRangeUtils.ts
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||||
|
import { DateTimeRangeType } from 'container/TopNav/CustomDateTimeModal';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
export interface CustomTimeRange {
|
||||||
|
from: string; // ISO string format
|
||||||
|
to: string; // ISO string format
|
||||||
|
timestamp: number; // When this range was created/used
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAX_STORED_RANGES = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the list of stored custom time ranges
|
||||||
|
* @returns Array of CustomTimeRange objects
|
||||||
|
*/
|
||||||
|
export const getCustomTimeRanges = (): CustomTimeRange[] => {
|
||||||
|
try {
|
||||||
|
const stored = localStorage.getItem(
|
||||||
|
LOCALSTORAGE.LAST_USED_CUSTOM_TIME_RANGES,
|
||||||
|
);
|
||||||
|
if (!stored) return [];
|
||||||
|
|
||||||
|
const parsed = JSON.parse(stored);
|
||||||
|
|
||||||
|
// Validate stored data
|
||||||
|
if (Array.isArray(parsed)) {
|
||||||
|
return parsed.filter((item) => item && item.from && item.to);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(
|
||||||
|
'Failed to retrieve custom time ranges from localStorage:',
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new custom time range to the stored list, maintaining only the last 3 used ranges
|
||||||
|
* @param dateTimeRange - The DateTimeRangeType from the time picker
|
||||||
|
* @returns The updated list of custom time ranges
|
||||||
|
*/
|
||||||
|
export const addCustomTimeRange = (
|
||||||
|
dateTimeRange: DateTimeRangeType,
|
||||||
|
): CustomTimeRange[] => {
|
||||||
|
if (!dateTimeRange || !dateTimeRange[0] || !dateTimeRange[1]) {
|
||||||
|
return getCustomTimeRanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
const [startTime, endTime] = dateTimeRange;
|
||||||
|
const now = dayjs();
|
||||||
|
|
||||||
|
const newRange: CustomTimeRange = {
|
||||||
|
from: startTime.toISOString(),
|
||||||
|
to: endTime.toISOString(),
|
||||||
|
timestamp: now.unix(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get existing ranges
|
||||||
|
const existingRanges = getCustomTimeRanges();
|
||||||
|
|
||||||
|
// Remove duplicate ranges (same start and end time)
|
||||||
|
const filteredRanges = existingRanges.filter(
|
||||||
|
(range) =>
|
||||||
|
range.from !== startTime.toISOString() || range.to !== endTime.toISOString(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add new range at the beginning (most recent)
|
||||||
|
const updatedRanges = [newRange, ...filteredRanges].slice(
|
||||||
|
0,
|
||||||
|
MAX_STORED_RANGES,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Store in localStorage
|
||||||
|
try {
|
||||||
|
localStorage.setItem(
|
||||||
|
LOCALSTORAGE.LAST_USED_CUSTOM_TIME_RANGES,
|
||||||
|
JSON.stringify(updatedRanges),
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to save custom time range to localStorage:', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedRanges;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all stored custom time ranges
|
||||||
|
*/
|
||||||
|
export const clearCustomTimeRanges = (): void => {
|
||||||
|
try {
|
||||||
|
localStorage.removeItem(LOCALSTORAGE.LAST_USED_CUSTOM_TIME_RANGES);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to clear custom time ranges from localStorage:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a specific custom time range by its timestamp
|
||||||
|
* @param timestamp - The timestamp of the range to remove
|
||||||
|
* @returns The updated list of custom time ranges
|
||||||
|
*/
|
||||||
|
export const removeCustomTimeRange = (timestamp: number): CustomTimeRange[] => {
|
||||||
|
const existingRanges = getCustomTimeRanges();
|
||||||
|
const updatedRanges = existingRanges.filter(
|
||||||
|
(range) => range.timestamp !== timestamp,
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
localStorage.setItem(
|
||||||
|
LOCALSTORAGE.LAST_USED_CUSTOM_TIME_RANGES,
|
||||||
|
JSON.stringify(updatedRanges),
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to update custom time ranges in localStorage:', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedRanges;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a time range already exists in the stored list
|
||||||
|
* @param dateTimeRange - The DateTimeRangeType to check
|
||||||
|
* @returns True if the range already exists
|
||||||
|
*/
|
||||||
|
export const isCustomTimeRangeExists = (
|
||||||
|
dateTimeRange: DateTimeRangeType,
|
||||||
|
): boolean => {
|
||||||
|
if (!dateTimeRange || !dateTimeRange[0] || !dateTimeRange[1]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [startTime, endTime] = dateTimeRange;
|
||||||
|
const existingRanges = getCustomTimeRanges();
|
||||||
|
|
||||||
|
return existingRanges.some(
|
||||||
|
(range) =>
|
||||||
|
range.from === startTime.toISOString() && range.to === endTime.toISOString(),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a CustomTimeRange to DateTimeRangeType
|
||||||
|
* @param customRange - The CustomTimeRange to convert
|
||||||
|
* @returns DateTimeRangeType
|
||||||
|
*/
|
||||||
|
export const convertCustomRangeToDateTimeRange = (
|
||||||
|
customRange: CustomTimeRange,
|
||||||
|
): DateTimeRangeType => [dayjs(customRange.from), dayjs(customRange.to)];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a DateTimeRangeType to CustomTimeRange format
|
||||||
|
* @param dateTimeRange - The DateTimeRangeType to convert
|
||||||
|
* @param label - Optional label, will be auto-generated if not provided
|
||||||
|
* @returns CustomTimeRange
|
||||||
|
*/
|
||||||
|
export const convertDateTimeRangeToCustomRange = (
|
||||||
|
dateTimeRange: DateTimeRangeType,
|
||||||
|
): CustomTimeRange | null => {
|
||||||
|
if (!dateTimeRange || !dateTimeRange[0] || !dateTimeRange[1]) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [startTime, endTime] = dateTimeRange;
|
||||||
|
|
||||||
|
return {
|
||||||
|
from: startTime.toISOString(),
|
||||||
|
to: endTime.toISOString(),
|
||||||
|
|
||||||
|
timestamp: Date.now(),
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -128,7 +128,6 @@ export const secondsToMilliseconds = (seconds: number): number =>
|
|||||||
seconds * 1_000;
|
seconds * 1_000;
|
||||||
|
|
||||||
export const epochToTimeString = (epochMs: number): string => {
|
export const epochToTimeString = (epochMs: number): string => {
|
||||||
console.log({ epochMs });
|
|
||||||
const date = new Date(epochMs);
|
const date = new Date(epochMs);
|
||||||
const options: Intl.DateTimeFormatOptions = {
|
const options: Intl.DateTimeFormatOptions = {
|
||||||
hour: '2-digit',
|
hour: '2-digit',
|
||||||
|
|||||||
@ -2578,6 +2578,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz#b6c75a56a1947cc916ea058772d666a2c8932f31"
|
resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz#b6c75a56a1947cc916ea058772d666a2c8932f31"
|
||||||
integrity sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==
|
integrity sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==
|
||||||
|
|
||||||
|
"@date-fns/tz@^1.4.1":
|
||||||
|
version "1.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@date-fns/tz/-/tz-1.4.1.tgz#2d905f282304630e07bef6d02d2e7dbf3f0cc4e4"
|
||||||
|
integrity sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==
|
||||||
|
|
||||||
"@discoveryjs/json-ext@0.5.7", "@discoveryjs/json-ext@^0.5.0":
|
"@discoveryjs/json-ext@0.5.7", "@discoveryjs/json-ext@^0.5.0":
|
||||||
version "0.5.7"
|
version "0.5.7"
|
||||||
resolved "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz"
|
resolved "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz"
|
||||||
@ -3543,6 +3548,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
|
|
||||||
|
"@radix-ui/primitive@1.1.3":
|
||||||
|
version "1.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.3.tgz#e2dbc13bdc5e4168f4334f75832d7bdd3e2de5ba"
|
||||||
|
integrity sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==
|
||||||
|
|
||||||
"@radix-ui/react-arrow@1.0.3":
|
"@radix-ui/react-arrow@1.0.3":
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz#c24f7968996ed934d57fe6cde5d6ec7266e1d25d"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz#c24f7968996ed934d57fe6cde5d6ec7266e1d25d"
|
||||||
@ -3551,6 +3561,13 @@
|
|||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
"@radix-ui/react-primitive" "1.0.3"
|
"@radix-ui/react-primitive" "1.0.3"
|
||||||
|
|
||||||
|
"@radix-ui/react-arrow@1.1.7":
|
||||||
|
version "1.1.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz#e14a2657c81d961598c5e72b73dd6098acc04f09"
|
||||||
|
integrity sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==
|
||||||
|
dependencies:
|
||||||
|
"@radix-ui/react-primitive" "2.1.3"
|
||||||
|
|
||||||
"@radix-ui/react-collection@1.0.3":
|
"@radix-ui/react-collection@1.0.3":
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.0.3.tgz#9595a66e09026187524a36c6e7e9c7d286469159"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.0.3.tgz#9595a66e09026187524a36c6e7e9c7d286469159"
|
||||||
@ -3569,6 +3586,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
|
|
||||||
|
"@radix-ui/react-compose-refs@1.1.2":
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz#a2c4c47af6337048ee78ff6dc0d090b390d2bb30"
|
||||||
|
integrity sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==
|
||||||
|
|
||||||
"@radix-ui/react-context@1.0.1":
|
"@radix-ui/react-context@1.0.1":
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.0.1.tgz#fe46e67c96b240de59187dcb7a1a50ce3e2ec00c"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.0.1.tgz#fe46e67c96b240de59187dcb7a1a50ce3e2ec00c"
|
||||||
@ -3576,6 +3598,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
|
|
||||||
|
"@radix-ui/react-context@1.1.2":
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.2.tgz#61628ef269a433382c364f6f1e3788a6dc213a36"
|
||||||
|
integrity sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==
|
||||||
|
|
||||||
"@radix-ui/react-direction@1.0.1":
|
"@radix-ui/react-direction@1.0.1":
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.0.1.tgz#9cb61bf2ccf568f3421422d182637b7f47596c9b"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.0.1.tgz#9cb61bf2ccf568f3421422d182637b7f47596c9b"
|
||||||
@ -3595,6 +3622,36 @@
|
|||||||
"@radix-ui/react-use-callback-ref" "1.0.1"
|
"@radix-ui/react-use-callback-ref" "1.0.1"
|
||||||
"@radix-ui/react-use-escape-keydown" "1.0.3"
|
"@radix-ui/react-use-escape-keydown" "1.0.3"
|
||||||
|
|
||||||
|
"@radix-ui/react-dismissable-layer@1.1.11":
|
||||||
|
version "1.1.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz#e33ab6f6bdaa00f8f7327c408d9f631376b88b37"
|
||||||
|
integrity sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==
|
||||||
|
dependencies:
|
||||||
|
"@radix-ui/primitive" "1.1.3"
|
||||||
|
"@radix-ui/react-compose-refs" "1.1.2"
|
||||||
|
"@radix-ui/react-primitive" "2.1.3"
|
||||||
|
"@radix-ui/react-use-callback-ref" "1.1.1"
|
||||||
|
"@radix-ui/react-use-escape-keydown" "1.1.1"
|
||||||
|
|
||||||
|
"@radix-ui/react-focus-guards@1.1.3":
|
||||||
|
version "1.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz#2a5669e464ad5fde9f86d22f7fdc17781a4dfa7f"
|
||||||
|
integrity sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==
|
||||||
|
|
||||||
|
"@radix-ui/react-focus-scope@1.1.7":
|
||||||
|
version "1.1.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz#dfe76fc103537d80bf42723a183773fd07bfb58d"
|
||||||
|
integrity sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==
|
||||||
|
dependencies:
|
||||||
|
"@radix-ui/react-compose-refs" "1.1.2"
|
||||||
|
"@radix-ui/react-primitive" "2.1.3"
|
||||||
|
"@radix-ui/react-use-callback-ref" "1.1.1"
|
||||||
|
|
||||||
|
"@radix-ui/react-icons@^1.3.0":
|
||||||
|
version "1.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-icons/-/react-icons-1.3.2.tgz#09be63d178262181aeca5fb7f7bc944b10a7f441"
|
||||||
|
integrity sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g==
|
||||||
|
|
||||||
"@radix-ui/react-id@1.0.1":
|
"@radix-ui/react-id@1.0.1":
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.0.1.tgz#73cdc181f650e4df24f0b6a5b7aa426b912c88c0"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.0.1.tgz#73cdc181f650e4df24f0b6a5b7aa426b912c88c0"
|
||||||
@ -3603,6 +3660,34 @@
|
|||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
"@radix-ui/react-use-layout-effect" "1.0.1"
|
"@radix-ui/react-use-layout-effect" "1.0.1"
|
||||||
|
|
||||||
|
"@radix-ui/react-id@1.1.1":
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.1.1.tgz#1404002e79a03fe062b7e3864aa01e24bd1471f7"
|
||||||
|
integrity sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==
|
||||||
|
dependencies:
|
||||||
|
"@radix-ui/react-use-layout-effect" "1.1.1"
|
||||||
|
|
||||||
|
"@radix-ui/react-popover@^1.1.15":
|
||||||
|
version "1.1.15"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-popover/-/react-popover-1.1.15.tgz#9c852f93990a687ebdc949b2c3de1f37cdc4c5d5"
|
||||||
|
integrity sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==
|
||||||
|
dependencies:
|
||||||
|
"@radix-ui/primitive" "1.1.3"
|
||||||
|
"@radix-ui/react-compose-refs" "1.1.2"
|
||||||
|
"@radix-ui/react-context" "1.1.2"
|
||||||
|
"@radix-ui/react-dismissable-layer" "1.1.11"
|
||||||
|
"@radix-ui/react-focus-guards" "1.1.3"
|
||||||
|
"@radix-ui/react-focus-scope" "1.1.7"
|
||||||
|
"@radix-ui/react-id" "1.1.1"
|
||||||
|
"@radix-ui/react-popper" "1.2.8"
|
||||||
|
"@radix-ui/react-portal" "1.1.9"
|
||||||
|
"@radix-ui/react-presence" "1.1.5"
|
||||||
|
"@radix-ui/react-primitive" "2.1.3"
|
||||||
|
"@radix-ui/react-slot" "1.2.3"
|
||||||
|
"@radix-ui/react-use-controllable-state" "1.2.2"
|
||||||
|
aria-hidden "^1.2.4"
|
||||||
|
react-remove-scroll "^2.6.3"
|
||||||
|
|
||||||
"@radix-ui/react-popper@1.1.3":
|
"@radix-ui/react-popper@1.1.3":
|
||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.1.3.tgz#24c03f527e7ac348fabf18c89795d85d21b00b42"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.1.3.tgz#24c03f527e7ac348fabf18c89795d85d21b00b42"
|
||||||
@ -3620,6 +3705,22 @@
|
|||||||
"@radix-ui/react-use-size" "1.0.1"
|
"@radix-ui/react-use-size" "1.0.1"
|
||||||
"@radix-ui/rect" "1.0.1"
|
"@radix-ui/rect" "1.0.1"
|
||||||
|
|
||||||
|
"@radix-ui/react-popper@1.2.8":
|
||||||
|
version "1.2.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.2.8.tgz#a79f39cdd2b09ab9fb50bf95250918422c4d9602"
|
||||||
|
integrity sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==
|
||||||
|
dependencies:
|
||||||
|
"@floating-ui/react-dom" "^2.0.0"
|
||||||
|
"@radix-ui/react-arrow" "1.1.7"
|
||||||
|
"@radix-ui/react-compose-refs" "1.1.2"
|
||||||
|
"@radix-ui/react-context" "1.1.2"
|
||||||
|
"@radix-ui/react-primitive" "2.1.3"
|
||||||
|
"@radix-ui/react-use-callback-ref" "1.1.1"
|
||||||
|
"@radix-ui/react-use-layout-effect" "1.1.1"
|
||||||
|
"@radix-ui/react-use-rect" "1.1.1"
|
||||||
|
"@radix-ui/react-use-size" "1.1.1"
|
||||||
|
"@radix-ui/rect" "1.1.1"
|
||||||
|
|
||||||
"@radix-ui/react-portal@1.0.4":
|
"@radix-ui/react-portal@1.0.4":
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.0.4.tgz#df4bfd353db3b1e84e639e9c63a5f2565fb00e15"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.0.4.tgz#df4bfd353db3b1e84e639e9c63a5f2565fb00e15"
|
||||||
@ -3628,6 +3729,14 @@
|
|||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
"@radix-ui/react-primitive" "1.0.3"
|
"@radix-ui/react-primitive" "1.0.3"
|
||||||
|
|
||||||
|
"@radix-ui/react-portal@1.1.9":
|
||||||
|
version "1.1.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.1.9.tgz#14c3649fe48ec474ac51ed9f2b9f5da4d91c4472"
|
||||||
|
integrity sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==
|
||||||
|
dependencies:
|
||||||
|
"@radix-ui/react-primitive" "2.1.3"
|
||||||
|
"@radix-ui/react-use-layout-effect" "1.1.1"
|
||||||
|
|
||||||
"@radix-ui/react-presence@1.0.1":
|
"@radix-ui/react-presence@1.0.1":
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.0.1.tgz#491990ba913b8e2a5db1b06b203cb24b5cdef9ba"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.0.1.tgz#491990ba913b8e2a5db1b06b203cb24b5cdef9ba"
|
||||||
@ -3637,6 +3746,14 @@
|
|||||||
"@radix-ui/react-compose-refs" "1.0.1"
|
"@radix-ui/react-compose-refs" "1.0.1"
|
||||||
"@radix-ui/react-use-layout-effect" "1.0.1"
|
"@radix-ui/react-use-layout-effect" "1.0.1"
|
||||||
|
|
||||||
|
"@radix-ui/react-presence@1.1.5":
|
||||||
|
version "1.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.5.tgz#5d8f28ac316c32f078afce2996839250c10693db"
|
||||||
|
integrity sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==
|
||||||
|
dependencies:
|
||||||
|
"@radix-ui/react-compose-refs" "1.1.2"
|
||||||
|
"@radix-ui/react-use-layout-effect" "1.1.1"
|
||||||
|
|
||||||
"@radix-ui/react-primitive@1.0.3":
|
"@radix-ui/react-primitive@1.0.3":
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz#d49ea0f3f0b2fe3ab1cb5667eb03e8b843b914d0"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz#d49ea0f3f0b2fe3ab1cb5667eb03e8b843b914d0"
|
||||||
@ -3645,6 +3762,13 @@
|
|||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
"@radix-ui/react-slot" "1.0.2"
|
"@radix-ui/react-slot" "1.0.2"
|
||||||
|
|
||||||
|
"@radix-ui/react-primitive@2.1.3":
|
||||||
|
version "2.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz#db9b8bcff49e01be510ad79893fb0e4cda50f1bc"
|
||||||
|
integrity sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==
|
||||||
|
dependencies:
|
||||||
|
"@radix-ui/react-slot" "1.2.3"
|
||||||
|
|
||||||
"@radix-ui/react-roving-focus@1.0.4":
|
"@radix-ui/react-roving-focus@1.0.4":
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz#e90c4a6a5f6ac09d3b8c1f5b5e81aab2f0db1974"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz#e90c4a6a5f6ac09d3b8c1f5b5e81aab2f0db1974"
|
||||||
@ -3669,6 +3793,13 @@
|
|||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
"@radix-ui/react-compose-refs" "1.0.1"
|
"@radix-ui/react-compose-refs" "1.0.1"
|
||||||
|
|
||||||
|
"@radix-ui/react-slot@1.2.3", "@radix-ui/react-slot@^1.1.0", "@radix-ui/react-slot@^1.2.3":
|
||||||
|
version "1.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.2.3.tgz#502d6e354fc847d4169c3bc5f189de777f68cfe1"
|
||||||
|
integrity sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==
|
||||||
|
dependencies:
|
||||||
|
"@radix-ui/react-compose-refs" "1.1.2"
|
||||||
|
|
||||||
"@radix-ui/react-tabs@1.0.4":
|
"@radix-ui/react-tabs@1.0.4":
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-tabs/-/react-tabs-1.0.4.tgz#993608eec55a5d1deddd446fa9978d2bc1053da2"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-tabs/-/react-tabs-1.0.4.tgz#993608eec55a5d1deddd446fa9978d2bc1053da2"
|
||||||
@ -3710,6 +3841,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
|
|
||||||
|
"@radix-ui/react-use-callback-ref@1.1.1":
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz#62a4dba8b3255fdc5cc7787faeac1c6e4cc58d40"
|
||||||
|
integrity sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==
|
||||||
|
|
||||||
"@radix-ui/react-use-controllable-state@1.0.1":
|
"@radix-ui/react-use-controllable-state@1.0.1":
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz#ecd2ced34e6330caf89a82854aa2f77e07440286"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz#ecd2ced34e6330caf89a82854aa2f77e07440286"
|
||||||
@ -3718,6 +3854,21 @@
|
|||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
"@radix-ui/react-use-callback-ref" "1.0.1"
|
"@radix-ui/react-use-callback-ref" "1.0.1"
|
||||||
|
|
||||||
|
"@radix-ui/react-use-controllable-state@1.2.2":
|
||||||
|
version "1.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz#905793405de57d61a439f4afebbb17d0645f3190"
|
||||||
|
integrity sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==
|
||||||
|
dependencies:
|
||||||
|
"@radix-ui/react-use-effect-event" "0.0.2"
|
||||||
|
"@radix-ui/react-use-layout-effect" "1.1.1"
|
||||||
|
|
||||||
|
"@radix-ui/react-use-effect-event@0.0.2":
|
||||||
|
version "0.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz#090cf30d00a4c7632a15548512e9152217593907"
|
||||||
|
integrity sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==
|
||||||
|
dependencies:
|
||||||
|
"@radix-ui/react-use-layout-effect" "1.1.1"
|
||||||
|
|
||||||
"@radix-ui/react-use-escape-keydown@1.0.3":
|
"@radix-ui/react-use-escape-keydown@1.0.3":
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz#217b840c250541609c66f67ed7bab2b733620755"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz#217b840c250541609c66f67ed7bab2b733620755"
|
||||||
@ -3726,6 +3877,13 @@
|
|||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
"@radix-ui/react-use-callback-ref" "1.0.1"
|
"@radix-ui/react-use-callback-ref" "1.0.1"
|
||||||
|
|
||||||
|
"@radix-ui/react-use-escape-keydown@1.1.1":
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz#b3fed9bbea366a118f40427ac40500aa1423cc29"
|
||||||
|
integrity sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==
|
||||||
|
dependencies:
|
||||||
|
"@radix-ui/react-use-callback-ref" "1.1.1"
|
||||||
|
|
||||||
"@radix-ui/react-use-layout-effect@1.0.1":
|
"@radix-ui/react-use-layout-effect@1.0.1":
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz#be8c7bc809b0c8934acf6657b577daf948a75399"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz#be8c7bc809b0c8934acf6657b577daf948a75399"
|
||||||
@ -3733,6 +3891,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
|
|
||||||
|
"@radix-ui/react-use-layout-effect@1.1.1":
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz#0c4230a9eed49d4589c967e2d9c0d9d60a23971e"
|
||||||
|
integrity sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==
|
||||||
|
|
||||||
"@radix-ui/react-use-rect@1.0.1":
|
"@radix-ui/react-use-rect@1.0.1":
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz#fde50b3bb9fd08f4a1cd204572e5943c244fcec2"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz#fde50b3bb9fd08f4a1cd204572e5943c244fcec2"
|
||||||
@ -3741,6 +3904,13 @@
|
|||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
"@radix-ui/rect" "1.0.1"
|
"@radix-ui/rect" "1.0.1"
|
||||||
|
|
||||||
|
"@radix-ui/react-use-rect@1.1.1":
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz#01443ca8ed071d33023c1113e5173b5ed8769152"
|
||||||
|
integrity sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==
|
||||||
|
dependencies:
|
||||||
|
"@radix-ui/rect" "1.1.1"
|
||||||
|
|
||||||
"@radix-ui/react-use-size@1.0.1":
|
"@radix-ui/react-use-size@1.0.1":
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz#1c5f5fea940a7d7ade77694bb98116fb49f870b2"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz#1c5f5fea940a7d7ade77694bb98116fb49f870b2"
|
||||||
@ -3749,6 +3919,13 @@
|
|||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
"@radix-ui/react-use-layout-effect" "1.0.1"
|
"@radix-ui/react-use-layout-effect" "1.0.1"
|
||||||
|
|
||||||
|
"@radix-ui/react-use-size@1.1.1":
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz#6de276ffbc389a537ffe4316f5b0f24129405b37"
|
||||||
|
integrity sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==
|
||||||
|
dependencies:
|
||||||
|
"@radix-ui/react-use-layout-effect" "1.1.1"
|
||||||
|
|
||||||
"@radix-ui/react-visually-hidden@1.0.3":
|
"@radix-ui/react-visually-hidden@1.0.3":
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz#51aed9dd0fe5abcad7dee2a234ad36106a6984ac"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz#51aed9dd0fe5abcad7dee2a234ad36106a6984ac"
|
||||||
@ -3764,6 +3941,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
|
|
||||||
|
"@radix-ui/rect@1.1.1":
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-1.1.1.tgz#78244efe12930c56fd255d7923865857c41ac8cb"
|
||||||
|
integrity sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==
|
||||||
|
|
||||||
"@rc-component/color-picker@~1.4.1":
|
"@rc-component/color-picker@~1.4.1":
|
||||||
version "1.4.1"
|
version "1.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/@rc-component/color-picker/-/color-picker-1.4.1.tgz#dcab0b660e9c4ed63a7582db68ed4a77c862cb93"
|
resolved "https://registry.yarnpkg.com/@rc-component/color-picker/-/color-picker-1.4.1.tgz#dcab0b660e9c4ed63a7582db68ed4a77c862cb93"
|
||||||
@ -4009,11 +4191,81 @@
|
|||||||
unplugin "1.0.1"
|
unplugin "1.0.1"
|
||||||
uuid "^9.0.0"
|
uuid "^9.0.0"
|
||||||
|
|
||||||
|
"@signozhq/button@0.0.1":
|
||||||
|
version "0.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@signozhq/button/-/button-0.0.1.tgz#7d3204454b0361bd3fdf91fa6604af01a481a9db"
|
||||||
|
integrity sha512-k5WFpckNXzwcTS82jU+65M3V1KdriopBObB1ls7W2OU0RKof6Gf+/9uqDXnuu+Y4Cxn2cPo8+6MfiQbS02LHeg==
|
||||||
|
dependencies:
|
||||||
|
"@radix-ui/react-icons" "^1.3.0"
|
||||||
|
"@radix-ui/react-slot" "^1.1.0"
|
||||||
|
class-variance-authority "^0.7.0"
|
||||||
|
clsx "^2.1.1"
|
||||||
|
lucide-react "^0.445.0"
|
||||||
|
tailwind-merge "^2.5.2"
|
||||||
|
tailwindcss-animate "^1.0.7"
|
||||||
|
|
||||||
|
"@signozhq/button@^0.0.2":
|
||||||
|
version "0.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@signozhq/button/-/button-0.0.2.tgz#c13edef1e735134b784a41f874b60a14bc16993f"
|
||||||
|
integrity sha512-434/gbTykC00LrnzFPp7c33QPWZkf9n+8+SToLZFTB0rzcaS/xoB4b7QKhvk+8xLCj4zpw6BxfeRAL+gSoOUJw==
|
||||||
|
dependencies:
|
||||||
|
"@radix-ui/react-icons" "^1.3.0"
|
||||||
|
"@radix-ui/react-slot" "^1.1.0"
|
||||||
|
class-variance-authority "^0.7.0"
|
||||||
|
clsx "^2.1.1"
|
||||||
|
lucide-react "^0.445.0"
|
||||||
|
tailwind-merge "^2.5.2"
|
||||||
|
tailwindcss-animate "^1.0.7"
|
||||||
|
|
||||||
|
"@signozhq/calendar@0.0.0":
|
||||||
|
version "0.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@signozhq/calendar/-/calendar-0.0.0.tgz#93b2cec2586efee814df934f88a2193cec95bae9"
|
||||||
|
integrity sha512-lm7tzPEhaHNjrksvi2GPGH4suEe6x2DQJ2dpku+JmKyLGB5rg9saSAosvrZVKhXLoZuSSjlBSkz+oHYEKIdHfA==
|
||||||
|
dependencies:
|
||||||
|
"@radix-ui/react-icons" "^1.3.0"
|
||||||
|
"@radix-ui/react-slot" "^1.2.3"
|
||||||
|
"@signozhq/button" "0.0.1"
|
||||||
|
class-variance-authority "^0.7.0"
|
||||||
|
clsx "^2.1.1"
|
||||||
|
date-fns "^4.1.0"
|
||||||
|
lucide-react "^0.445.0"
|
||||||
|
react-day-picker "^9.8.1"
|
||||||
|
tailwind-merge "^2.5.2"
|
||||||
|
tailwindcss-animate "^1.0.7"
|
||||||
|
|
||||||
"@signozhq/design-tokens@1.1.4":
|
"@signozhq/design-tokens@1.1.4":
|
||||||
version "1.1.4"
|
version "1.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@signozhq/design-tokens/-/design-tokens-1.1.4.tgz#5d5de5bd9d19b6a3631383db015cc4b70c3f7661"
|
resolved "https://registry.yarnpkg.com/@signozhq/design-tokens/-/design-tokens-1.1.4.tgz#5d5de5bd9d19b6a3631383db015cc4b70c3f7661"
|
||||||
integrity sha512-ICZz5szxTq8NcKAsk6LP+nSybPyEcyy8eu2zfxlPQCnJ1YjJP1PglaKLlF0N6+D60gAd3yC5he06BqR8/HxjNg==
|
integrity sha512-ICZz5szxTq8NcKAsk6LP+nSybPyEcyy8eu2zfxlPQCnJ1YjJP1PglaKLlF0N6+D60gAd3yC5he06BqR8/HxjNg==
|
||||||
|
|
||||||
|
"@signozhq/input@0.0.2":
|
||||||
|
version "0.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@signozhq/input/-/input-0.0.2.tgz#b2fea8c0979a53984ebcd5e3c3c50b38082eb1b1"
|
||||||
|
integrity sha512-Iti9GkvexSsULX1pQsN6FT6Gw96YWilts72wITZd5fzgZq1yKqaDtQl98/QNuyoS3I3WEh+hVF4EIeCCe7oRsQ==
|
||||||
|
dependencies:
|
||||||
|
"@radix-ui/react-icons" "^1.3.0"
|
||||||
|
"@radix-ui/react-slot" "^1.1.0"
|
||||||
|
class-variance-authority "^0.7.0"
|
||||||
|
clsx "^2.1.1"
|
||||||
|
lucide-react "^0.445.0"
|
||||||
|
tailwind-merge "^2.5.2"
|
||||||
|
tailwindcss-animate "^1.0.7"
|
||||||
|
|
||||||
|
"@signozhq/popover@0.0.0":
|
||||||
|
version "0.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@signozhq/popover/-/popover-0.0.0.tgz#675baf1c18ca0180369b4df0700c24e2c55ad758"
|
||||||
|
integrity sha512-XW0MhzxWzZNQWjVeb+BFjiOIbBbYCT+9MCUOIW8kiL0axFaaimnk0QPi1rk09u136MMGByI6fYuCJ5Qa07l1dA==
|
||||||
|
dependencies:
|
||||||
|
"@radix-ui/react-icons" "^1.3.0"
|
||||||
|
"@radix-ui/react-popover" "^1.1.15"
|
||||||
|
"@radix-ui/react-slot" "^1.1.0"
|
||||||
|
"@signozhq/button" "^0.0.2"
|
||||||
|
class-variance-authority "^0.7.0"
|
||||||
|
clsx "^2.1.1"
|
||||||
|
lucide-react "^0.445.0"
|
||||||
|
tailwind-merge "^2.5.2"
|
||||||
|
tailwindcss-animate "^1.0.7"
|
||||||
|
|
||||||
"@sinclair/typebox@^0.25.16":
|
"@sinclair/typebox@^0.25.16":
|
||||||
version "0.25.24"
|
version "0.25.24"
|
||||||
resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz"
|
resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz"
|
||||||
@ -5787,6 +6039,13 @@ argparse@^1.0.7:
|
|||||||
dependencies:
|
dependencies:
|
||||||
sprintf-js "~1.0.2"
|
sprintf-js "~1.0.2"
|
||||||
|
|
||||||
|
aria-hidden@^1.2.4:
|
||||||
|
version "1.2.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.2.6.tgz#73051c9b088114c795b1ea414e9c0fff874ffc1a"
|
||||||
|
integrity sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.0.0"
|
||||||
|
|
||||||
aria-query@^5.0.0, aria-query@^5.1.3:
|
aria-query@^5.0.0, aria-query@^5.1.3:
|
||||||
version "5.1.3"
|
version "5.1.3"
|
||||||
resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz"
|
resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz"
|
||||||
@ -6870,6 +7129,13 @@ cjs-module-lexer@^1.0.0:
|
|||||||
resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz"
|
resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz"
|
||||||
integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==
|
integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==
|
||||||
|
|
||||||
|
class-variance-authority@^0.7.0:
|
||||||
|
version "0.7.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/class-variance-authority/-/class-variance-authority-0.7.1.tgz#4008a798a0e4553a781a57ac5177c9fb5d043787"
|
||||||
|
integrity sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==
|
||||||
|
dependencies:
|
||||||
|
clsx "^2.1.1"
|
||||||
|
|
||||||
classnames@2.3.2, classnames@2.x, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classnames@^2.2.6, classnames@^2.3.1, classnames@^2.3.2:
|
classnames@2.3.2, classnames@2.x, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classnames@^2.2.6, classnames@^2.3.1, classnames@^2.3.2:
|
||||||
version "2.3.2"
|
version "2.3.2"
|
||||||
resolved "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz"
|
resolved "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz"
|
||||||
@ -6964,6 +7230,11 @@ clsx@^1.1.1:
|
|||||||
resolved "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz"
|
resolved "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz"
|
||||||
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
|
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
|
||||||
|
|
||||||
|
clsx@^2.1.1:
|
||||||
|
version "2.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999"
|
||||||
|
integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
|
||||||
|
|
||||||
co@^4.6.0:
|
co@^4.6.0:
|
||||||
version "4.6.0"
|
version "4.6.0"
|
||||||
resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz"
|
resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz"
|
||||||
@ -7787,11 +8058,21 @@ data-urls@^2.0.0:
|
|||||||
whatwg-mimetype "^2.3.0"
|
whatwg-mimetype "^2.3.0"
|
||||||
whatwg-url "^8.0.0"
|
whatwg-url "^8.0.0"
|
||||||
|
|
||||||
|
date-fns-jalali@^4.1.0-0:
|
||||||
|
version "4.1.0-0"
|
||||||
|
resolved "https://registry.yarnpkg.com/date-fns-jalali/-/date-fns-jalali-4.1.0-0.tgz#9c7fb286004fab267a300d3e9f1ada9f10b4b6b0"
|
||||||
|
integrity sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==
|
||||||
|
|
||||||
date-fns@3.6.0:
|
date-fns@3.6.0:
|
||||||
version "3.6.0"
|
version "3.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-3.6.0.tgz#f20ca4fe94f8b754951b24240676e8618c0206bf"
|
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-3.6.0.tgz#f20ca4fe94f8b754951b24240676e8618c0206bf"
|
||||||
integrity sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==
|
integrity sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==
|
||||||
|
|
||||||
|
date-fns@^4.1.0:
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-4.1.0.tgz#64b3d83fff5aa80438f5b1a633c2e83b8a1c2d14"
|
||||||
|
integrity sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==
|
||||||
|
|
||||||
dayjs@^1.10.7, dayjs@^1.11.1:
|
dayjs@^1.10.7, dayjs@^1.11.1:
|
||||||
version "1.11.7"
|
version "1.11.7"
|
||||||
resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz"
|
resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz"
|
||||||
@ -7993,6 +8274,11 @@ detect-newline@^3.0.0:
|
|||||||
resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz"
|
resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz"
|
||||||
integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==
|
integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==
|
||||||
|
|
||||||
|
detect-node-es@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493"
|
||||||
|
integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==
|
||||||
|
|
||||||
detect-node@^2.0.4, detect-node@^2.1.0:
|
detect-node@^2.0.4, detect-node@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz"
|
resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz"
|
||||||
@ -9418,6 +9704,11 @@ get-intrinsic@^1.2.6:
|
|||||||
hasown "^2.0.2"
|
hasown "^2.0.2"
|
||||||
math-intrinsics "^1.1.0"
|
math-intrinsics "^1.1.0"
|
||||||
|
|
||||||
|
get-nonce@^1.0.0:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/get-nonce/-/get-nonce-1.0.1.tgz#fdf3f0278073820d2ce9426c18f07481b1e0cdf3"
|
||||||
|
integrity sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==
|
||||||
|
|
||||||
get-package-type@^0.1.0:
|
get-package-type@^0.1.0:
|
||||||
version "0.1.0"
|
version "0.1.0"
|
||||||
resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz"
|
resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz"
|
||||||
@ -11932,6 +12223,11 @@ lucide-react@0.498.0:
|
|||||||
resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.498.0.tgz#3109eac93dfd0c1561db7a5cddfe4b9b20c14315"
|
resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.498.0.tgz#3109eac93dfd0c1561db7a5cddfe4b9b20c14315"
|
||||||
integrity sha512-k8IKbvMNV5Dj7CHRrKyIc1wAtmGdEF0r6SCaiGAt5cZ8KnjcEao8mfdydKkWspy65l40MdlcfdK0kT3QrxpnIg==
|
integrity sha512-k8IKbvMNV5Dj7CHRrKyIc1wAtmGdEF0r6SCaiGAt5cZ8KnjcEao8mfdydKkWspy65l40MdlcfdK0kT3QrxpnIg==
|
||||||
|
|
||||||
|
lucide-react@^0.445.0:
|
||||||
|
version "0.445.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.445.0.tgz#35c42341e98fbf0475b2a6cf74fd25ef7cbfcd62"
|
||||||
|
integrity sha512-YrLf3aAHvmd4dZ8ot+mMdNFrFpJD7YRwQ2pUcBhgqbmxtrMP4xDzIorcj+8y+6kpuXBF4JB0NOCTUWIYetJjgA==
|
||||||
|
|
||||||
lz-string@^1.4.4:
|
lz-string@^1.4.4:
|
||||||
version "1.5.0"
|
version "1.5.0"
|
||||||
resolved "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz"
|
resolved "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz"
|
||||||
@ -14759,6 +15055,15 @@ react-beautiful-dnd@13.1.1:
|
|||||||
redux "^4.0.4"
|
redux "^4.0.4"
|
||||||
use-memo-one "^1.1.1"
|
use-memo-one "^1.1.1"
|
||||||
|
|
||||||
|
react-day-picker@^9.8.1:
|
||||||
|
version "9.9.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-day-picker/-/react-day-picker-9.9.0.tgz#494da0f1470e8f0ca61c30100b17615687b68393"
|
||||||
|
integrity sha512-NtkJbuX6cl/VaGNb3sVVhmMA6LSMnL5G3xNL+61IyoZj0mUZFWTg4hmj7PHjIQ8MXN9dHWhUHFoJWG6y60DKSg==
|
||||||
|
dependencies:
|
||||||
|
"@date-fns/tz" "^1.4.1"
|
||||||
|
date-fns "^4.1.0"
|
||||||
|
date-fns-jalali "^4.1.0-0"
|
||||||
|
|
||||||
react-dnd-html5-backend@16.0.1:
|
react-dnd-html5-backend@16.0.1:
|
||||||
version "16.0.1"
|
version "16.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz#87faef15845d512a23b3c08d29ecfd34871688b6"
|
resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz#87faef15845d512a23b3c08d29ecfd34871688b6"
|
||||||
@ -14962,6 +15267,25 @@ react-redux@^7.2.0, react-redux@^7.2.2:
|
|||||||
prop-types "^15.7.2"
|
prop-types "^15.7.2"
|
||||||
react-is "^17.0.2"
|
react-is "^17.0.2"
|
||||||
|
|
||||||
|
react-remove-scroll-bar@^2.3.7:
|
||||||
|
version "2.3.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz#99c20f908ee467b385b68a3469b4a3e750012223"
|
||||||
|
integrity sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==
|
||||||
|
dependencies:
|
||||||
|
react-style-singleton "^2.2.2"
|
||||||
|
tslib "^2.0.0"
|
||||||
|
|
||||||
|
react-remove-scroll@^2.6.3:
|
||||||
|
version "2.7.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz#d2101d414f6d81d7d3bf033f3c1cb4785789f753"
|
||||||
|
integrity sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==
|
||||||
|
dependencies:
|
||||||
|
react-remove-scroll-bar "^2.3.7"
|
||||||
|
react-style-singleton "^2.2.3"
|
||||||
|
tslib "^2.1.0"
|
||||||
|
use-callback-ref "^1.3.3"
|
||||||
|
use-sidecar "^1.1.3"
|
||||||
|
|
||||||
react-resizable@3.0.4:
|
react-resizable@3.0.4:
|
||||||
version "3.0.4"
|
version "3.0.4"
|
||||||
resolved "https://registry.npmjs.org/react-resizable/-/react-resizable-3.0.4.tgz"
|
resolved "https://registry.npmjs.org/react-resizable/-/react-resizable-3.0.4.tgz"
|
||||||
@ -15022,6 +15346,14 @@ react-router@6.27.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@remix-run/router" "1.20.0"
|
"@remix-run/router" "1.20.0"
|
||||||
|
|
||||||
|
react-style-singleton@^2.2.2, react-style-singleton@^2.2.3:
|
||||||
|
version "2.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.2.3.tgz#4265608be69a4d70cfe3047f2c6c88b2c3ace388"
|
||||||
|
integrity sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==
|
||||||
|
dependencies:
|
||||||
|
get-nonce "^1.0.0"
|
||||||
|
tslib "^2.0.0"
|
||||||
|
|
||||||
react-syntax-highlighter@15.5.0:
|
react-syntax-highlighter@15.5.0:
|
||||||
version "15.5.0"
|
version "15.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-syntax-highlighter/-/react-syntax-highlighter-15.5.0.tgz#4b3eccc2325fa2ec8eff1e2d6c18fa4a9e07ab20"
|
resolved "https://registry.yarnpkg.com/react-syntax-highlighter/-/react-syntax-highlighter-15.5.0.tgz#4b3eccc2325fa2ec8eff1e2d6c18fa4a9e07ab20"
|
||||||
@ -16608,6 +16940,16 @@ table@^6.0.9:
|
|||||||
string-width "^4.2.3"
|
string-width "^4.2.3"
|
||||||
strip-ansi "^6.0.1"
|
strip-ansi "^6.0.1"
|
||||||
|
|
||||||
|
tailwind-merge@^2.5.2:
|
||||||
|
version "2.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/tailwind-merge/-/tailwind-merge-2.6.0.tgz#ac5fb7e227910c038d458f396b7400d93a3142d5"
|
||||||
|
integrity sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==
|
||||||
|
|
||||||
|
tailwindcss-animate@^1.0.7:
|
||||||
|
version "1.0.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz#318b692c4c42676cc9e67b19b78775742388bef4"
|
||||||
|
integrity sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==
|
||||||
|
|
||||||
tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0:
|
tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0:
|
||||||
version "2.2.1"
|
version "2.2.1"
|
||||||
resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz"
|
resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz"
|
||||||
@ -17346,6 +17688,13 @@ url-set-query@^1.0.0:
|
|||||||
resolved "https://registry.npmjs.org/url-set-query/-/url-set-query-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/url-set-query/-/url-set-query-1.0.0.tgz"
|
||||||
integrity sha512-3AChu4NiXquPfeckE5R5cGdiHCMWJx1dwCWOmWIL4KHAziJNOFIYJlpGFeKDvwLPHovZRCxK3cYlwzqI9Vp+Gg==
|
integrity sha512-3AChu4NiXquPfeckE5R5cGdiHCMWJx1dwCWOmWIL4KHAziJNOFIYJlpGFeKDvwLPHovZRCxK3cYlwzqI9Vp+Gg==
|
||||||
|
|
||||||
|
use-callback-ref@^1.3.3:
|
||||||
|
version "1.3.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.3.3.tgz#98d9fab067075841c5b2c6852090d5d0feabe2bf"
|
||||||
|
integrity sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.0.0"
|
||||||
|
|
||||||
use-isomorphic-layout-effect@^1.1.2:
|
use-isomorphic-layout-effect@^1.1.2:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz"
|
resolved "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz"
|
||||||
@ -17356,6 +17705,14 @@ use-memo-one@^1.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.3.tgz#2fd2e43a2169eabc7496960ace8c79efef975e99"
|
resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.3.tgz#2fd2e43a2169eabc7496960ace8c79efef975e99"
|
||||||
integrity sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==
|
integrity sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==
|
||||||
|
|
||||||
|
use-sidecar@^1.1.3:
|
||||||
|
version "1.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.1.3.tgz#10e7fd897d130b896e2c546c63a5e8233d00efdb"
|
||||||
|
integrity sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==
|
||||||
|
dependencies:
|
||||||
|
detect-node-es "^1.1.0"
|
||||||
|
tslib "^2.0.0"
|
||||||
|
|
||||||
use-sync-external-store@^1.0.0:
|
use-sync-external-store@^1.0.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz"
|
resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user