diff --git a/shlink-web-component/short-urls/ShortUrlForm.tsx b/shlink-web-component/short-urls/ShortUrlForm.tsx index ef64347c..93c33313 100644 --- a/shlink-web-component/short-urls/ShortUrlForm.tsx +++ b/shlink-web-component/short-urls/ShortUrlForm.tsx @@ -9,13 +9,13 @@ import { useEffect, useState } from 'react'; import { Button, FormGroup, Input, Row } from 'reactstrap'; import type { InputType } from 'reactstrap/types/lib/Input'; import { Checkbox } from '../../src/utils/Checkbox'; -import type { DateTimeInputProps } from '../../src/utils/dates/DateTimeInput'; -import { DateTimeInput } from '../../src/utils/dates/DateTimeInput'; -import { formatIsoDate } from '../../src/utils/helpers/date'; import { IconInput } from '../../src/utils/IconInput'; import { SimpleCard } from '../../src/utils/SimpleCard'; import type { DomainSelectorProps } from '../domains/DomainSelector'; import type { TagsSelectorProps } from '../tags/helpers/TagsSelector'; +import type { DateTimeInputProps } from '../utils/dates/DateTimeInput'; +import { DateTimeInput } from '../utils/dates/DateTimeInput'; +import { formatIsoDate } from '../utils/dates/helpers/date'; import { useFeature } from '../utils/features'; import { handleEventPreventingDefault, hasValue } from '../utils/helpers'; import type { DeviceLongUrls, ShortUrlData } from './data'; diff --git a/shlink-web-component/short-urls/ShortUrlsFilteringBar.tsx b/shlink-web-component/short-urls/ShortUrlsFilteringBar.tsx index 7949eb56..4bd769ab 100644 --- a/shlink-web-component/short-urls/ShortUrlsFilteringBar.tsx +++ b/shlink-web-component/short-urls/ShortUrlsFilteringBar.tsx @@ -4,14 +4,14 @@ import classNames from 'classnames'; import { isEmpty, pipe } from 'ramda'; import type { FC } from 'react'; import { Button, InputGroup, Row, UncontrolledTooltip } from 'reactstrap'; -import { DateRangeSelector } from '../../src/utils/dates/DateRangeSelector'; -import { formatIsoDate } from '../../src/utils/helpers/date'; -import type { DateRange } from '../../src/utils/helpers/dateIntervals'; -import { datesToDateRange } from '../../src/utils/helpers/dateIntervals'; import type { OrderDir } from '../../src/utils/helpers/ordering'; import { OrderingDropdown } from '../../src/utils/OrderingDropdown'; import { SearchField } from '../../src/utils/SearchField'; import type { TagsSelectorProps } from '../tags/helpers/TagsSelector'; +import { DateRangeSelector } from '../utils/dates/DateRangeSelector'; +import { formatIsoDate } from '../utils/dates/helpers/date'; +import type { DateRange } from '../utils/dates/helpers/dateIntervals'; +import { datesToDateRange } from '../utils/dates/helpers/dateIntervals'; import { useFeature } from '../utils/features'; import { useSetting } from '../utils/settings'; import type { ShortUrlsOrder, ShortUrlsOrderableFields } from './data'; diff --git a/shlink-web-component/short-urls/helpers/ShortUrlStatus.tsx b/shlink-web-component/short-urls/helpers/ShortUrlStatus.tsx index f7a88230..8b8a88f0 100644 --- a/shlink-web-component/short-urls/helpers/ShortUrlStatus.tsx +++ b/shlink-web-component/short-urls/helpers/ShortUrlStatus.tsx @@ -4,7 +4,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { isBefore } from 'date-fns'; import type { FC, ReactNode } from 'react'; import { UncontrolledTooltip } from 'reactstrap'; -import { formatHumanFriendly, now, parseISO } from '../../../src/utils/helpers/date'; +import { formatHumanFriendly, now, parseISO } from '../../utils/dates/helpers/date'; import { useElementRef } from '../../utils/helpers/hooks'; import type { ShortUrl } from '../data'; diff --git a/shlink-web-component/short-urls/helpers/ShortUrlVisitsCount.tsx b/shlink-web-component/short-urls/helpers/ShortUrlVisitsCount.tsx index 1d672356..084ca14d 100644 --- a/shlink-web-component/short-urls/helpers/ShortUrlVisitsCount.tsx +++ b/shlink-web-component/short-urls/helpers/ShortUrlVisitsCount.tsx @@ -2,7 +2,7 @@ import { faInfoCircle as infoIcon } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import classNames from 'classnames'; import { UncontrolledTooltip } from 'reactstrap'; -import { formatHumanFriendly, parseISO } from '../../../src/utils/helpers/date'; +import { formatHumanFriendly, parseISO } from '../../utils/dates/helpers/date'; import { useElementRef } from '../../utils/helpers/hooks'; import { prettify } from '../../utils/helpers/numbers'; import type { ShortUrl } from '../data'; diff --git a/shlink-web-component/short-urls/helpers/ShortUrlsRow.tsx b/shlink-web-component/short-urls/helpers/ShortUrlsRow.tsx index 56cd8b71..9e19917c 100644 --- a/shlink-web-component/short-urls/helpers/ShortUrlsRow.tsx +++ b/shlink-web-component/short-urls/helpers/ShortUrlsRow.tsx @@ -1,9 +1,9 @@ import type { FC } from 'react'; import { useEffect, useRef } from 'react'; import { ExternalLink } from 'react-external-link'; -import { Time } from '../../../src/utils/dates/Time'; import type { TimeoutToggle } from '../../../src/utils/helpers/hooks'; import { CopyToClipboardIcon } from '../../utils/components/CopyToClipboardIcon'; +import { Time } from '../../utils/dates/Time'; import type { ColorGenerator } from '../../utils/services/ColorGenerator'; import { useSetting } from '../../utils/settings'; import type { ShortUrl } from '../data'; diff --git a/src/utils/dates/DateInput.scss b/shlink-web-component/utils/dates/DateInput.scss similarity index 97% rename from src/utils/dates/DateInput.scss rename to shlink-web-component/utils/dates/DateInput.scss index d61fd0d2..57bb43dc 100644 --- a/src/utils/dates/DateInput.scss +++ b/shlink-web-component/utils/dates/DateInput.scss @@ -1,5 +1,5 @@ -@import '../mixins/vertical-align'; -@import '../base'; +@import '../../../src/utils/mixins/vertical-align'; +@import '../../../src/utils/base'; .react-datepicker__close-icon.react-datepicker__close-icon { @include vertical-align(); diff --git a/src/utils/dates/DateInput.tsx b/shlink-web-component/utils/dates/DateInput.tsx similarity index 96% rename from src/utils/dates/DateInput.tsx rename to shlink-web-component/utils/dates/DateInput.tsx index 9f32aaa9..60bd22cb 100644 --- a/src/utils/dates/DateInput.tsx +++ b/shlink-web-component/utils/dates/DateInput.tsx @@ -5,7 +5,7 @@ import { isNil } from 'ramda'; import { useRef } from 'react'; import type { ReactDatePickerProps } from 'react-datepicker'; import DatePicker from 'react-datepicker'; -import { STANDARD_DATE_FORMAT } from '../helpers/date'; +import { STANDARD_DATE_FORMAT } from './helpers/date'; import './DateInput.scss'; export type DateInputProps = ReactDatePickerProps; diff --git a/src/utils/dates/DateIntervalDropdownItems.tsx b/shlink-web-component/utils/dates/DateIntervalDropdownItems.tsx similarity index 83% rename from src/utils/dates/DateIntervalDropdownItems.tsx rename to shlink-web-component/utils/dates/DateIntervalDropdownItems.tsx index 3b8e585b..8a6a1a29 100644 --- a/src/utils/dates/DateIntervalDropdownItems.tsx +++ b/shlink-web-component/utils/dates/DateIntervalDropdownItems.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react'; import { DropdownItem } from 'reactstrap'; -import type { DateInterval } from '../helpers/dateIntervals'; -import { DATE_INTERVALS, rangeOrIntervalToString } from '../helpers/dateIntervals'; +import type { DateInterval } from './helpers/dateIntervals'; +import { DATE_INTERVALS, rangeOrIntervalToString } from './helpers/dateIntervals'; export interface DateIntervalDropdownProps { active?: DateInterval; diff --git a/shlink-web-component/utils/dates/DateIntervalSelector.tsx b/shlink-web-component/utils/dates/DateIntervalSelector.tsx new file mode 100644 index 00000000..2faf46ed --- /dev/null +++ b/shlink-web-component/utils/dates/DateIntervalSelector.tsx @@ -0,0 +1,11 @@ +import type { FC } from 'react'; +import { DropdownBtn } from '../../../src/utils/DropdownBtn'; +import type { DateIntervalDropdownProps } from './DateIntervalDropdownItems'; +import { DateIntervalDropdownItems } from './DateIntervalDropdownItems'; +import { rangeOrIntervalToString } from './helpers/dateIntervals'; + +export const DateIntervalSelector: FC = ({ onChange, active, allText }) => ( + + + +); diff --git a/src/utils/dates/DateRangeRow.tsx b/shlink-web-component/utils/dates/DateRangeRow.tsx similarity index 94% rename from src/utils/dates/DateRangeRow.tsx rename to shlink-web-component/utils/dates/DateRangeRow.tsx index f5fe1d7f..ba47389f 100644 --- a/src/utils/dates/DateRangeRow.tsx +++ b/shlink-web-component/utils/dates/DateRangeRow.tsx @@ -1,6 +1,6 @@ import { endOfDay } from 'date-fns'; -import type { DateRange } from '../helpers/dateIntervals'; import { DateInput } from './DateInput'; +import type { DateRange } from './helpers/dateIntervals'; interface DateRangeRowProps extends DateRange { onStartDateChange: (date: Date | null) => void; diff --git a/src/utils/dates/DateRangeSelector.tsx b/shlink-web-component/utils/dates/DateRangeSelector.tsx similarity index 91% rename from src/utils/dates/DateRangeSelector.tsx rename to shlink-web-component/utils/dates/DateRangeSelector.tsx index dd254a6a..01a9660d 100644 --- a/src/utils/dates/DateRangeSelector.tsx +++ b/shlink-web-component/utils/dates/DateRangeSelector.tsx @@ -1,19 +1,19 @@ import { useState } from 'react'; import { DropdownItem } from 'reactstrap'; -import { DropdownBtn } from '../DropdownBtn'; +import { DropdownBtn } from '../../../src/utils/DropdownBtn'; +import { useEffectExceptFirstTime } from '../../../src/utils/helpers/hooks'; +import { DateIntervalDropdownItems } from './DateIntervalDropdownItems'; +import { DateRangeRow } from './DateRangeRow'; import type { DateInterval, - DateRange } from '../helpers/dateIntervals'; + DateRange } from './helpers/dateIntervals'; import { ALL, dateRangeIsEmpty, intervalToDateRange, rangeIsInterval, rangeOrIntervalToString, -} from '../helpers/dateIntervals'; -import { useEffectExceptFirstTime } from '../helpers/hooks'; -import { DateIntervalDropdownItems } from './DateIntervalDropdownItems'; -import { DateRangeRow } from './DateRangeRow'; +} from './helpers/dateIntervals'; export interface DateRangeSelectorProps { initialDateRange?: DateInterval | DateRange; diff --git a/src/utils/dates/DateTimeInput.tsx b/shlink-web-component/utils/dates/DateTimeInput.tsx similarity index 87% rename from src/utils/dates/DateTimeInput.tsx rename to shlink-web-component/utils/dates/DateTimeInput.tsx index 236f1f0a..402423c3 100644 --- a/src/utils/dates/DateTimeInput.tsx +++ b/shlink-web-component/utils/dates/DateTimeInput.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react'; import type { ReactDatePickerProps } from 'react-datepicker'; -import { STANDARD_DATE_AND_TIME_FORMAT } from '../helpers/date'; import { DateInput } from './DateInput'; +import { STANDARD_DATE_AND_TIME_FORMAT } from './helpers/date'; export type DateTimeInputProps = Omit; diff --git a/src/utils/dates/Time.tsx b/shlink-web-component/utils/dates/Time.tsx similarity index 96% rename from src/utils/dates/Time.tsx rename to shlink-web-component/utils/dates/Time.tsx index 8245d031..22cb432c 100644 --- a/src/utils/dates/Time.tsx +++ b/shlink-web-component/utils/dates/Time.tsx @@ -1,5 +1,5 @@ import { format as formatDate, formatDistance, getUnixTime, parseISO } from 'date-fns'; -import { isDateObject, now, STANDARD_DATE_AND_TIME_FORMAT } from '../helpers/date'; +import { isDateObject, now, STANDARD_DATE_AND_TIME_FORMAT } from './helpers/date'; export interface TimeProps { date: Date | string; diff --git a/src/utils/helpers/date.ts b/shlink-web-component/utils/dates/helpers/date.ts similarity index 77% rename from src/utils/helpers/date.ts rename to shlink-web-component/utils/dates/helpers/date.ts index ebb33605..95acd092 100644 --- a/src/utils/helpers/date.ts +++ b/shlink-web-component/utils/dates/helpers/date.ts @@ -1,5 +1,5 @@ import { format, formatISO, isBefore, isEqual, isWithinInterval, parse, parseISO as stdParseISO } from 'date-fns'; -import type { OptionalString } from '../utils'; +import type { OptionalString } from '../../../../src/utils/utils'; export const STANDARD_DATE_FORMAT = 'yyyy-MM-dd'; @@ -21,23 +21,16 @@ const formatDateFromFormat = (date?: NullableDate, theFormat?: string): Optional return theFormat ? format(date, theFormat) : formatISO(date); }; -export const formatDate = (theFormat = STANDARD_DATE_FORMAT) => (date?: NullableDate) => formatDateFromFormat( - date, - theFormat, -); - export const formatIsoDate = (date?: NullableDate) => formatDateFromFormat(date, undefined); -export const formatInternational = formatDate(); +export const formatInternational = (date?: NullableDate) => formatDateFromFormat(date, STANDARD_DATE_FORMAT); -export const formatHumanFriendly = formatDate(STANDARD_DATE_AND_TIME_FORMAT); +export const formatHumanFriendly = (date?: NullableDate) => formatDateFromFormat(date, STANDARD_DATE_AND_TIME_FORMAT); export const parseDate = (date: string, theFormat: string) => parse(date, theFormat, now()); export const parseISO = (date: DateOrString): Date => (isDateObject(date) ? date : stdParseISO(date)); -export const dateOrNull = (date?: string): Date | null => (date ? parseISO(date) : null); - export const isBetween = (date: DateOrString, start?: DateOrString, end?: DateOrString): boolean => { try { return isWithinInterval(parseISO(date), { start: parseISO(start ?? date), end: parseISO(end ?? date) }); diff --git a/src/utils/helpers/dateIntervals.ts b/shlink-web-component/utils/dates/helpers/dateIntervals.ts similarity index 94% rename from src/utils/helpers/dateIntervals.ts rename to shlink-web-component/utils/dates/helpers/dateIntervals.ts index e6ff097d..58512f17 100644 --- a/src/utils/helpers/dateIntervals.ts +++ b/shlink-web-component/utils/dates/helpers/dateIntervals.ts @@ -1,7 +1,7 @@ import { endOfDay, startOfDay, subDays } from 'date-fns'; import { cond, filter, isEmpty, T } from 'ramda'; import type { DateOrString } from './date'; -import { dateOrNull, formatInternational, isBeforeOrEqual, now, parseISO } from './date'; +import { formatInternational, isBeforeOrEqual, now, parseISO } from './date'; export interface DateRange { startDate?: Date | null; @@ -30,7 +30,9 @@ export const dateRangeIsEmpty = (dateRange?: DateRange): boolean => dateRange == export const rangeIsInterval = (range?: DateRange | DateInterval): range is DateInterval => typeof range === 'string' && INTERVALS.includes(range); -export const DATE_INTERVALS = INTERVALS.filter((value) => value !== ALL) as DateInterval[]; +export const DATE_INTERVALS = INTERVALS.filter((value) => value !== ALL) as Exclude[]; + +const dateOrNull = (date?: string): Date | null => (date ? parseISO(date) : null); export const datesToDateRange = (startDate?: string, endDate?: string): DateRange => ({ startDate: dateOrNull(startDate), diff --git a/shlink-web-component/utils/settings.ts b/shlink-web-component/utils/settings.ts index 0a5a8c86..0173c593 100644 --- a/shlink-web-component/utils/settings.ts +++ b/shlink-web-component/utils/settings.ts @@ -1,8 +1,8 @@ import { createContext, useContext } from 'react'; -import type { DateInterval } from '../../src/utils/helpers/dateIntervals'; import type { Theme } from '../../src/utils/theme'; import type { ShortUrlsOrder } from '../short-urls/data'; import type { TagsOrder } from '../tags/data/TagsListChildrenProps'; +import type { DateInterval } from './dates/helpers/dateIntervals'; export const DEFAULT_SHORT_URLS_ORDERING: ShortUrlsOrder = { field: 'dateCreated', diff --git a/shlink-web-component/visits/ShortUrlVisitsHeader.tsx b/shlink-web-component/visits/ShortUrlVisitsHeader.tsx index 91c55931..b40b4293 100644 --- a/shlink-web-component/visits/ShortUrlVisitsHeader.tsx +++ b/shlink-web-component/visits/ShortUrlVisitsHeader.tsx @@ -1,7 +1,7 @@ import { ExternalLink } from 'react-external-link'; import { UncontrolledTooltip } from 'reactstrap'; -import { Time } from '../../src/utils/dates/Time'; import type { ShortUrlDetail } from '../short-urls/reducers/shortUrlDetail'; +import { Time } from '../utils/dates/Time'; import type { ShortUrlVisits } from './reducers/shortUrlVisits'; import { VisitsHeader } from './VisitsHeader'; import './ShortUrlVisitsHeader.scss'; diff --git a/shlink-web-component/visits/VisitsStats.tsx b/shlink-web-component/visits/VisitsStats.tsx index ad0bff0c..6354e4c7 100644 --- a/shlink-web-component/visits/VisitsStats.tsx +++ b/shlink-web-component/visits/VisitsStats.tsx @@ -7,14 +7,14 @@ import type { FC, PropsWithChildren } from 'react'; import { useEffect, useMemo, useRef, useState } from 'react'; import { Navigate, Route, Routes, useLocation } from 'react-router-dom'; import { Button, Progress, Row } from 'reactstrap'; -import { DateRangeSelector } from '../../src/utils/dates/DateRangeSelector'; -import type { DateInterval, DateRange } from '../../src/utils/helpers/dateIntervals'; -import { toDateRange } from '../../src/utils/helpers/dateIntervals'; import { Message } from '../../src/utils/Message'; import { NavPillItem, NavPills } from '../../src/utils/NavPills'; import { Result } from '../../src/utils/Result'; import { ShlinkApiError } from '../common/ShlinkApiError'; import { ExportBtn } from '../utils/components/ExportBtn'; +import { DateRangeSelector } from '../utils/dates/DateRangeSelector'; +import type { DateInterval, DateRange } from '../utils/dates/helpers/dateIntervals'; +import { toDateRange } from '../utils/dates/helpers/dateIntervals'; import { prettify } from '../utils/helpers/numbers'; import { useSetting } from '../utils/settings'; import { DoughnutChartCard } from './charts/DoughnutChartCard'; diff --git a/shlink-web-component/visits/VisitsTable.tsx b/shlink-web-component/visits/VisitsTable.tsx index 3c75002d..869087b5 100644 --- a/shlink-web-component/visits/VisitsTable.tsx +++ b/shlink-web-component/visits/VisitsTable.tsx @@ -4,11 +4,11 @@ import classNames from 'classnames'; import { min, splitEvery } from 'ramda'; import { useEffect, useMemo, useRef, useState } from 'react'; import { UncontrolledTooltip } from 'reactstrap'; -import { Time } from '../../src/utils/dates/Time'; import type { Order } from '../../src/utils/helpers/ordering'; import { determineOrderDir, sortList } from '../../src/utils/helpers/ordering'; import { SearchField } from '../../src/utils/SearchField'; import { SimplePaginator } from '../utils/components/SimplePaginator'; +import { Time } from '../utils/dates/Time'; import { prettify } from '../utils/helpers/numbers'; import { TableOrderIcon } from '../utils/table/TableOrderIcon'; import type { MediaMatcher } from '../utils/types'; diff --git a/shlink-web-component/visits/charts/LineChartCard.tsx b/shlink-web-component/visits/charts/LineChartCard.tsx index 2f634e76..5350a7c5 100644 --- a/shlink-web-component/visits/charts/LineChartCard.tsx +++ b/shlink-web-component/visits/charts/LineChartCard.tsx @@ -23,9 +23,9 @@ import { DropdownToggle, UncontrolledDropdown, } from 'reactstrap'; -import { STANDARD_DATE_FORMAT } from '../../../src/utils/helpers/date'; import { HIGHLIGHTED_COLOR, MAIN_COLOR } from '../../../src/utils/theme'; import { ToggleSwitch } from '../../../src/utils/ToggleSwitch'; +import { formatInternational } from '../../utils/dates/helpers/date'; import { rangeOf } from '../../utils/helpers'; import { pointerOnHover, renderChartLabel } from '../../utils/helpers/charts'; import { useToggle } from '../../utils/helpers/hooks'; @@ -67,10 +67,16 @@ const STEP_TO_DIFF_FUNC_MAP: Record n const STEP_TO_DATE_FORMAT: Record string> = { hourly: (date) => format(date, 'yyyy-MM-dd HH:00'), - daily: (date) => format(date, STANDARD_DATE_FORMAT), + // TODO Fix formatInternational return type + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + daily: (date) => formatInternational(date)!, weekly(date) { - const firstWeekDay = format(startOfISOWeek(date), STANDARD_DATE_FORMAT); - const lastWeekDay = format(endOfISOWeek(date), STANDARD_DATE_FORMAT); + // TODO Fix formatInternational return type + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const firstWeekDay = formatInternational(startOfISOWeek(date))!; + // TODO Fix formatInternational return type + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const lastWeekDay = formatInternational(endOfISOWeek(date))!; return `${firstWeekDay} - ${lastWeekDay}`; }, diff --git a/shlink-web-component/visits/helpers/hooks.ts b/shlink-web-component/visits/helpers/hooks.ts index e14b76d6..88f88f5e 100644 --- a/shlink-web-component/visits/helpers/hooks.ts +++ b/shlink-web-component/visits/helpers/hooks.ts @@ -2,9 +2,9 @@ import type { DeepPartial } from '@reduxjs/toolkit'; import { isEmpty, isNil, mergeDeepRight, pipe } from 'ramda'; import { useMemo } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; -import { formatIsoDate } from '../../../src/utils/helpers/date'; -import type { DateRange } from '../../../src/utils/helpers/dateIntervals'; -import { datesToDateRange } from '../../../src/utils/helpers/dateIntervals'; +import { formatIsoDate } from '../../utils/dates/helpers/date'; +import type { DateRange } from '../../utils/dates/helpers/dateIntervals'; +import { datesToDateRange } from '../../utils/dates/helpers/dateIntervals'; import type { BooleanString } from '../../utils/helpers'; import { parseBooleanToString } from '../../utils/helpers'; import { parseQuery, stringifyQuery } from '../../utils/helpers/query'; diff --git a/shlink-web-component/visits/reducers/common.ts b/shlink-web-component/visits/reducers/common.ts index 93c214e9..d8d6f528 100644 --- a/shlink-web-component/visits/reducers/common.ts +++ b/shlink-web-component/visits/reducers/common.ts @@ -1,10 +1,10 @@ import { createAction, createSlice } from '@reduxjs/toolkit'; import { flatten, prop, range, splitEvery } from 'ramda'; -import type { DateInterval } from '../../../src/utils/helpers/dateIntervals'; -import { dateToMatchingInterval } from '../../../src/utils/helpers/dateIntervals'; import type { ShlinkPaginator, ShlinkVisits, ShlinkVisitsParams } from '../../api-contract'; import { parseApiError } from '../../api-contract/utils'; import type { RootState } from '../../container/store'; +import type { DateInterval } from '../../utils/dates/helpers/dateIntervals'; +import { dateToMatchingInterval } from '../../utils/dates/helpers/dateIntervals'; import { createAsyncThunk } from '../../utils/redux'; import type { CreateVisit, Visit } from '../types'; import type { LoadVisits, VisitsInfo, VisitsLoaded } from './types'; diff --git a/shlink-web-component/visits/reducers/domainVisits.ts b/shlink-web-component/visits/reducers/domainVisits.ts index db767039..9055347d 100644 --- a/shlink-web-component/visits/reducers/domainVisits.ts +++ b/shlink-web-component/visits/reducers/domainVisits.ts @@ -1,6 +1,6 @@ -import { isBetween } from '../../../src/utils/helpers/date'; import type { ShlinkApiClient } from '../../api-contract'; import { domainMatches } from '../../short-urls/helpers'; +import { isBetween } from '../../utils/dates/helpers/date'; import { createVisitsAsyncThunk, createVisitsReducer, lastVisitLoaderForLoader } from './common'; import type { LoadVisits, VisitsInfo } from './types'; diff --git a/shlink-web-component/visits/reducers/nonOrphanVisits.ts b/shlink-web-component/visits/reducers/nonOrphanVisits.ts index 1b257661..59a48143 100644 --- a/shlink-web-component/visits/reducers/nonOrphanVisits.ts +++ b/shlink-web-component/visits/reducers/nonOrphanVisits.ts @@ -1,5 +1,5 @@ -import { isBetween } from '../../../src/utils/helpers/date'; import type { ShlinkApiClient } from '../../api-contract'; +import { isBetween } from '../../utils/dates/helpers/date'; import { createVisitsAsyncThunk, createVisitsReducer, lastVisitLoaderForLoader } from './common'; import type { VisitsInfo } from './types'; diff --git a/shlink-web-component/visits/reducers/orphanVisits.ts b/shlink-web-component/visits/reducers/orphanVisits.ts index ce1664b2..0abf619f 100644 --- a/shlink-web-component/visits/reducers/orphanVisits.ts +++ b/shlink-web-component/visits/reducers/orphanVisits.ts @@ -1,5 +1,5 @@ -import { isBetween } from '../../../src/utils/helpers/date'; import type { ShlinkApiClient } from '../../api-contract'; +import { isBetween } from '../../utils/dates/helpers/date'; import type { OrphanVisit, OrphanVisitType } from '../types'; import { isOrphanVisit } from '../types/helpers'; import { createVisitsAsyncThunk, createVisitsReducer, lastVisitLoaderForLoader } from './common'; diff --git a/shlink-web-component/visits/reducers/shortUrlVisits.ts b/shlink-web-component/visits/reducers/shortUrlVisits.ts index 57f1a0b5..b76ee21d 100644 --- a/shlink-web-component/visits/reducers/shortUrlVisits.ts +++ b/shlink-web-component/visits/reducers/shortUrlVisits.ts @@ -1,7 +1,7 @@ -import { isBetween } from '../../../src/utils/helpers/date'; import type { ShlinkApiClient } from '../../api-contract'; import type { ShortUrlIdentifier } from '../../short-urls/data'; import { shortUrlMatches } from '../../short-urls/helpers'; +import { isBetween } from '../../utils/dates/helpers/date'; import { createVisitsAsyncThunk, createVisitsReducer, lastVisitLoaderForLoader } from './common'; import type { LoadVisits, VisitsInfo } from './types'; diff --git a/shlink-web-component/visits/reducers/tagVisits.ts b/shlink-web-component/visits/reducers/tagVisits.ts index 5507956c..6fdf5dae 100644 --- a/shlink-web-component/visits/reducers/tagVisits.ts +++ b/shlink-web-component/visits/reducers/tagVisits.ts @@ -1,5 +1,5 @@ -import { isBetween } from '../../../src/utils/helpers/date'; import type { ShlinkApiClient } from '../../api-contract'; +import { isBetween } from '../../utils/dates/helpers/date'; import { createVisitsAsyncThunk, createVisitsReducer, lastVisitLoaderForLoader } from './common'; import type { LoadVisits, VisitsInfo } from './types'; diff --git a/shlink-web-component/visits/reducers/types/index.ts b/shlink-web-component/visits/reducers/types/index.ts index 751cd304..a93fadee 100644 --- a/shlink-web-component/visits/reducers/types/index.ts +++ b/shlink-web-component/visits/reducers/types/index.ts @@ -1,5 +1,5 @@ -import type { DateInterval } from '../../../../src/utils/helpers/dateIntervals'; import type { ProblemDetailsError, ShlinkVisitsParams } from '../../../api-contract'; +import type { DateInterval } from '../../../utils/dates/helpers/dateIntervals'; import type { Visit } from '../../types'; export interface VisitsInfo { diff --git a/shlink-web-component/visits/types/helpers.ts b/shlink-web-component/visits/types/helpers.ts index 85e01177..cc4b55f8 100644 --- a/shlink-web-component/visits/types/helpers.ts +++ b/shlink-web-component/visits/types/helpers.ts @@ -1,6 +1,6 @@ import { countBy, groupBy, pipe, prop } from 'ramda'; import type { ShlinkVisitsParams } from '../../../api/types'; -import { formatIsoDate } from '../../../src/utils/helpers/date'; +import { formatIsoDate } from '../../utils/dates/helpers/date'; import type { CreateVisit, NormalizedOrphanVisit, NormalizedVisit, OrphanVisit, Stats, Visit, VisitsParams } from './index'; export const isOrphanVisit = (visit: Visit): visit is OrphanVisit => (visit as OrphanVisit).visitedUrl !== undefined; diff --git a/shlink-web-component/visits/types/index.ts b/shlink-web-component/visits/types/index.ts index 905e280d..5d3635dc 100644 --- a/shlink-web-component/visits/types/index.ts +++ b/shlink-web-component/visits/types/index.ts @@ -1,5 +1,5 @@ -import type { DateRange } from '../../../src/utils/helpers/dateIntervals'; import type { ShortUrl } from '../../short-urls/data'; +import type { DateRange } from '../../utils/dates/helpers/dateIntervals'; export type OrphanVisitType = 'base_url' | 'invalid_short_url' | 'regular_404'; diff --git a/src/settings/VisitsSettings.tsx b/src/settings/VisitsSettings.tsx index 76f045b8..713c095e 100644 --- a/src/settings/VisitsSettings.tsx +++ b/src/settings/VisitsSettings.tsx @@ -1,10 +1,10 @@ import type { FC } from 'react'; import { FormGroup } from 'reactstrap'; -import type { Settings } from '../../shlink-web-component/utils/settings'; +import type { Settings } from '../../shlink-web-component'; +import type { DateInterval } from '../../shlink-web-component/utils/dates/helpers/dateIntervals'; import { DateIntervalSelector } from '../utils/dates/DateIntervalSelector'; import { FormText } from '../utils/forms/FormText'; import { LabeledFormGroup } from '../utils/forms/LabeledFormGroup'; -import type { DateInterval } from '../utils/helpers/dateIntervals'; import { SimpleCard } from '../utils/SimpleCard'; import { ToggleSwitch } from '../utils/ToggleSwitch'; diff --git a/src/utils/dates/DateIntervalSelector.tsx b/src/utils/dates/DateIntervalSelector.tsx index 094eb298..1e8ad01e 100644 --- a/src/utils/dates/DateIntervalSelector.tsx +++ b/src/utils/dates/DateIntervalSelector.tsx @@ -1,11 +1,39 @@ import type { FC } from 'react'; +import { DropdownItem } from 'reactstrap'; +import type { Settings } from '../../../shlink-web-component'; +import { rangeOrIntervalToString } from '../../../shlink-web-component/utils/dates/helpers/dateIntervals'; import { DropdownBtn } from '../DropdownBtn'; -import { rangeOrIntervalToString } from '../helpers/dateIntervals'; -import type { DateIntervalDropdownProps } from './DateIntervalDropdownItems'; -import { DateIntervalDropdownItems } from './DateIntervalDropdownItems'; -export const DateIntervalSelector: FC = ({ onChange, active, allText }) => ( +type DateInterval = Exclude['defaultInterval']; + +export interface DateIntervalSelectorProps { + active?: DateInterval; + allText: string; + onChange: (interval: DateInterval) => void; +} + +const INTERVAL_TO_STRING_MAP: Record, string> = { + today: 'Today', + yesterday: 'Yesterday', + last7Days: 'Last 7 days', + last30Days: 'Last 30 days', + last90Days: 'Last 90 days', + last180Days: 'Last 180 days', + last365Days: 'Last 365 days', +}; + +export const DateIntervalSelector: FC = ({ onChange, active, allText }) => ( - + onChange('all')}> + {allText} + + + {Object.entries(INTERVAL_TO_STRING_MAP).map( + ([interval, name]) => ( + onChange(interval as DateInterval)}> + {name} + + ), + )} ); diff --git a/test/short-urls/ShortUrlForm.test.tsx b/test/short-urls/ShortUrlForm.test.tsx index 49ed0f1c..403a7ec2 100644 --- a/test/short-urls/ShortUrlForm.test.tsx +++ b/test/short-urls/ShortUrlForm.test.tsx @@ -4,8 +4,8 @@ import { fromPartial } from '@total-typescript/shoehorn'; import { formatISO } from 'date-fns'; import type { Mode } from '../../shlink-web-component/short-urls/ShortUrlForm'; import { ShortUrlForm as createShortUrlForm } from '../../shlink-web-component/short-urls/ShortUrlForm'; +import { parseDate } from '../../shlink-web-component/utils/dates/helpers/date'; import type { ReachableServer, SelectedServer } from '../../src/servers/data'; -import { parseDate } from '../../src/utils/helpers/date'; import type { OptionalString } from '../../src/utils/utils'; import { renderWithEvents } from '../__helpers__/setUpTest'; diff --git a/test/short-urls/ShortUrlsFilteringBar.test.tsx b/test/short-urls/ShortUrlsFilteringBar.test.tsx index 0476022c..766bd227 100644 --- a/test/short-urls/ShortUrlsFilteringBar.test.tsx +++ b/test/short-urls/ShortUrlsFilteringBar.test.tsx @@ -3,9 +3,9 @@ import { fromPartial } from '@total-typescript/shoehorn'; import { endOfDay, formatISO, startOfDay } from 'date-fns'; import { MemoryRouter, useLocation, useNavigate } from 'react-router-dom'; import { ShortUrlsFilteringBar as filteringBarCreator } from '../../shlink-web-component/short-urls/ShortUrlsFilteringBar'; +import { formatDate } from '../../shlink-web-component/utils/dates/helpers/date'; +import type { DateRange } from '../../shlink-web-component/utils/dates/helpers/dateIntervals'; import type { ReachableServer, SelectedServer } from '../../src/servers/data'; -import { formatDate } from '../../src/utils/helpers/date'; -import type { DateRange } from '../../src/utils/helpers/dateIntervals'; import { renderWithEvents } from '../__helpers__/setUpTest'; vi.mock('react-router-dom', async () => ({ diff --git a/test/short-urls/helpers/ShortUrlsRow.test.tsx b/test/short-urls/helpers/ShortUrlsRow.test.tsx index 5d2c8730..bb08c4a3 100644 --- a/test/short-urls/helpers/ShortUrlsRow.test.tsx +++ b/test/short-urls/helpers/ShortUrlsRow.test.tsx @@ -5,9 +5,9 @@ import { last } from 'ramda'; import { MemoryRouter, useLocation } from 'react-router-dom'; import type { ShortUrl, ShortUrlMeta } from '../../../shlink-web-component/short-urls/data'; import { ShortUrlsRow as createShortUrlsRow } from '../../../shlink-web-component/short-urls/helpers/ShortUrlsRow'; +import { now, parseDate } from '../../../shlink-web-component/utils/dates/helpers/date'; import type { ReachableServer } from '../../../src/servers/data'; import type { Settings } from '../../../src/settings/reducers/settings'; -import { now, parseDate } from '../../../src/utils/helpers/date'; import type { TimeoutToggle } from '../../../src/utils/helpers/hooks'; import type { OptionalString } from '../../../src/utils/utils'; import { renderWithEvents } from '../../__helpers__/setUpTest'; diff --git a/test/utils/dates/DateInput.test.tsx b/test/utils/dates/DateInput.test.tsx index b37e3409..ec4f7084 100644 --- a/test/utils/dates/DateInput.test.tsx +++ b/test/utils/dates/DateInput.test.tsx @@ -1,8 +1,8 @@ import { screen, waitFor } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; import { parseISO } from 'date-fns'; -import type { DateInputProps } from '../../../src/utils/dates/DateInput'; -import { DateInput } from '../../../src/utils/dates/DateInput'; +import type { DateInputProps } from '../../../shlink-web-component/utils/dates/DateInput'; +import { DateInput } from '../../../shlink-web-component/utils/dates/DateInput'; import { renderWithEvents } from '../../__helpers__/setUpTest'; describe('', () => { diff --git a/test/utils/dates/DateIntervalDropdownItems.test.tsx b/test/utils/dates/DateIntervalDropdownItems.test.tsx index 0a90769c..9937441f 100644 --- a/test/utils/dates/DateIntervalDropdownItems.test.tsx +++ b/test/utils/dates/DateIntervalDropdownItems.test.tsx @@ -1,8 +1,8 @@ import { screen, waitFor } from '@testing-library/react'; +import type { DateInterval } from '../../../shlink-web-component/utils/dates/helpers/dateIntervals'; +import { DATE_INTERVALS, rangeOrIntervalToString } from '../../../shlink-web-component/utils/dates/helpers/dateIntervals'; import { DateIntervalDropdownItems } from '../../../src/utils/dates/DateIntervalDropdownItems'; import { DropdownBtn } from '../../../src/utils/DropdownBtn'; -import type { DateInterval } from '../../../src/utils/helpers/dateIntervals'; -import { DATE_INTERVALS, rangeOrIntervalToString } from '../../../src/utils/helpers/dateIntervals'; import { renderWithEvents } from '../../__helpers__/setUpTest'; describe('', () => { diff --git a/test/utils/dates/DateIntervalSelector.test.tsx b/test/utils/dates/DateIntervalSelector.test.tsx index 1e40164b..ffc4d68d 100644 --- a/test/utils/dates/DateIntervalSelector.test.tsx +++ b/test/utils/dates/DateIntervalSelector.test.tsx @@ -1,7 +1,7 @@ import { screen, waitFor } from '@testing-library/react'; +import type { DateInterval } from '../../../shlink-web-component/utils/dates/helpers/dateIntervals'; +import { rangeOrIntervalToString } from '../../../shlink-web-component/utils/dates/helpers/dateIntervals'; import { DateIntervalSelector } from '../../../src/utils/dates/DateIntervalSelector'; -import type { DateInterval } from '../../../src/utils/helpers/dateIntervals'; -import { rangeOrIntervalToString } from '../../../src/utils/helpers/dateIntervals'; import { renderWithEvents } from '../../__helpers__/setUpTest'; describe('', () => { diff --git a/test/utils/dates/DateRangeRow.test.tsx b/test/utils/dates/DateRangeRow.test.tsx index 85f05904..3b0a13a5 100644 --- a/test/utils/dates/DateRangeRow.test.tsx +++ b/test/utils/dates/DateRangeRow.test.tsx @@ -1,5 +1,5 @@ import { screen } from '@testing-library/react'; -import { DateRangeRow } from '../../../src/utils/dates/DateRangeRow'; +import { DateRangeRow } from '../../../shlink-web-component/utils/dates/DateRangeRow'; import { renderWithEvents } from '../../__helpers__/setUpTest'; describe('', () => { diff --git a/test/utils/dates/DateRangeSelector.test.tsx b/test/utils/dates/DateRangeSelector.test.tsx index be8be592..a0b9806b 100644 --- a/test/utils/dates/DateRangeSelector.test.tsx +++ b/test/utils/dates/DateRangeSelector.test.tsx @@ -1,8 +1,8 @@ import { screen, waitFor } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; -import type { DateRangeSelectorProps } from '../../../src/utils/dates/DateRangeSelector'; -import { DateRangeSelector } from '../../../src/utils/dates/DateRangeSelector'; -import type { DateInterval } from '../../../src/utils/helpers/dateIntervals'; +import type { DateRangeSelectorProps } from '../../../shlink-web-component/utils/dates/DateRangeSelector'; +import { DateRangeSelector } from '../../../shlink-web-component/utils/dates/DateRangeSelector'; +import type { DateInterval } from '../../../shlink-web-component/utils/dates/helpers/dateIntervals'; import { renderWithEvents } from '../../__helpers__/setUpTest'; describe('', () => { diff --git a/test/utils/dates/Time.test.tsx b/test/utils/dates/Time.test.tsx index aa57cdd8..a0fb692d 100644 --- a/test/utils/dates/Time.test.tsx +++ b/test/utils/dates/Time.test.tsx @@ -1,7 +1,7 @@ import { render } from '@testing-library/react'; -import type { TimeProps } from '../../../src/utils/dates/Time'; -import { Time } from '../../../src/utils/dates/Time'; -import { parseDate } from '../../../src/utils/helpers/date'; +import { parseDate } from '../../../shlink-web-component/utils/dates/helpers/date'; +import type { TimeProps } from '../../../shlink-web-component/utils/dates/Time'; +import { Time } from '../../../shlink-web-component/utils/dates/Time'; describe('