mirror of
https://github.com/kremalicious/umami.git
synced 2024-11-22 01:46:58 +01:00
Convert realtime components to TS.
This commit is contained in:
parent
e67282d7d8
commit
8d31f43f0f
@ -6,7 +6,7 @@ import { EventDataMetricsBar } from './EventDataMetricsBar';
|
||||
import { useDateRange, useApi, useNavigation } from 'components/hooks';
|
||||
import styles from './WebsiteEventData.module.css';
|
||||
|
||||
function useData(websiteId, event) {
|
||||
function useData(websiteId: string, event: string) {
|
||||
const [dateRange] = useDateRange(websiteId);
|
||||
const { startDate, endDate } = dateRange;
|
||||
const { get, useQuery } = useApi();
|
@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
import { useMemo, useState, useEffect } from 'react';
|
||||
import { subMinutes, startOfMinute } from 'date-fns';
|
||||
import firstBy from 'thenby';
|
||||
import thenby from 'thenby';
|
||||
import { Grid, GridRow } from 'components/layout/Grid';
|
||||
import Page from 'components/layout/Page';
|
||||
import RealtimeChart from 'components/metrics/RealtimeChart';
|
||||
@ -15,9 +15,10 @@ import useApi from 'components/hooks/useApi';
|
||||
import { percentFilter } from 'lib/filters';
|
||||
import { REALTIME_RANGE, REALTIME_INTERVAL } from 'lib/constants';
|
||||
import { useWebsite } from 'components/hooks';
|
||||
import { RealtimeData } from 'lib/types';
|
||||
import styles from './Realtime.module.css';
|
||||
|
||||
function mergeData(state = [], data = [], time) {
|
||||
function mergeData(state = [], data = [], time: number) {
|
||||
const ids = state.map(({ __id }) => __id);
|
||||
return state
|
||||
.concat(data.filter(({ __id }) => !ids.includes(__id)))
|
||||
@ -25,7 +26,7 @@ function mergeData(state = [], data = [], time) {
|
||||
}
|
||||
|
||||
export function Realtime({ websiteId }) {
|
||||
const [currentData, setCurrentData] = useState();
|
||||
const [currentData, setCurrentData] = useState<RealtimeData>();
|
||||
const { get, useQuery } = useApi();
|
||||
const { data: website } = useWebsite(websiteId);
|
||||
const { data, isLoading, error } = useQuery({
|
||||
@ -33,7 +34,6 @@ export function Realtime({ websiteId }) {
|
||||
queryFn: () => get(`/realtime/${websiteId}`, { startAt: currentData?.timestamp || 0 }),
|
||||
enabled: !!(websiteId && website),
|
||||
refetchInterval: REALTIME_INTERVAL,
|
||||
cache: false,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@ -50,9 +50,9 @@ export function Realtime({ websiteId }) {
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
const realtimeData = useMemo(() => {
|
||||
const realtimeData: RealtimeData = useMemo(() => {
|
||||
if (!currentData) {
|
||||
return { pageviews: [], sessions: [], events: [], countries: [], visitors: [] };
|
||||
return { pageviews: [], sessions: [], events: [], countries: [], visitors: [], timestamp: 0 };
|
||||
}
|
||||
|
||||
currentData.countries = percentFilter(
|
||||
@ -75,7 +75,7 @@ export function Realtime({ websiteId }) {
|
||||
}
|
||||
return arr;
|
||||
}, [])
|
||||
.sort(firstBy('y', -1)),
|
||||
.sort(thenby.firstBy('y', -1)),
|
||||
);
|
||||
|
||||
currentData.visitors = currentData.sessions.reduce((arr, val) => {
|
||||
@ -89,18 +89,18 @@ export function Realtime({ websiteId }) {
|
||||
}, [currentData]);
|
||||
|
||||
if (isLoading || error) {
|
||||
return <Page loading={isLoading} error={error} />;
|
||||
return <Page isLoading={isLoading} error={error} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<WebsiteHeader websiteId={websiteId} />
|
||||
<RealtimeHeader websiteId={websiteId} data={currentData} />
|
||||
<RealtimeHeader data={currentData} />
|
||||
<RealtimeChart className={styles.chart} data={realtimeData} unit="minute" />
|
||||
<Grid>
|
||||
<GridRow columns="one-two">
|
||||
<RealtimeUrls websiteId={websiteId} websiteDomain={website?.domain} data={realtimeData} />
|
||||
<RealtimeLog websiteId={websiteId} websiteDomain={website?.domain} data={realtimeData} />
|
||||
<RealtimeUrls websiteDomain={website?.domain} data={realtimeData} />
|
||||
<RealtimeLog websiteDomain={website?.domain} data={realtimeData} />
|
||||
</GridRow>
|
||||
<GridRow columns="one-two">
|
||||
<RealtimeCountries data={realtimeData?.countries} />
|
@ -1,10 +1,11 @@
|
||||
import MetricCard from 'components/metrics/MetricCard';
|
||||
import useMessages from 'components/hooks/useMessages';
|
||||
import { RealtimeData } from 'lib/types';
|
||||
import styles from './RealtimeHeader.module.css';
|
||||
|
||||
export function RealtimeHeader({ data = {} }) {
|
||||
export function RealtimeHeader({ data }: { data: RealtimeData }) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { pageviews, visitors, events, countries } = data;
|
||||
const { pageviews, visitors, events, countries } = data || {};
|
||||
|
||||
return (
|
||||
<div className={styles.header}>
|
@ -1,7 +1,7 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { StatusLight, Icon, Text } from 'react-basics';
|
||||
import { FixedSizeList } from 'react-window';
|
||||
import firstBy from 'thenby';
|
||||
import thenby from 'thenby';
|
||||
import FilterButtons from 'components/common/FilterButtons';
|
||||
import Empty from 'components/common/Empty';
|
||||
import useLocale from 'components/hooks/useLocale';
|
||||
@ -130,7 +130,7 @@ export function RealtimeLog({ data, websiteDomain }) {
|
||||
}
|
||||
|
||||
const { pageviews, visitors, events } = data;
|
||||
const logs = [...pageviews, ...visitors, ...events].sort(firstBy('createdAt', -1));
|
||||
const logs = [...pageviews, ...visitors, ...events].sort(thenby.firstBy('createdAt', -1));
|
||||
|
||||
if (filter !== TYPE_ALL) {
|
||||
return logs.filter(({ __type }) => __type === filter);
|
@ -1,15 +1,22 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { Key, useMemo, useState } from 'react';
|
||||
import { ButtonGroup, Button, Flexbox } from 'react-basics';
|
||||
import firstBy from 'thenby';
|
||||
import thenby from 'thenby';
|
||||
import { percentFilter } from 'lib/filters';
|
||||
import ListTable from 'components/metrics/ListTable';
|
||||
import { FILTER_PAGES, FILTER_REFERRERS } from 'lib/constants';
|
||||
import useMessages from 'components/hooks/useMessages';
|
||||
import { RealtimeData } from 'lib/types';
|
||||
|
||||
export function RealtimeUrls({ websiteDomain, data = {} }) {
|
||||
export function RealtimeUrls({
|
||||
websiteDomain,
|
||||
data,
|
||||
}: {
|
||||
websiteDomain: string;
|
||||
data: RealtimeData;
|
||||
}) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { pageviews } = data;
|
||||
const [filter, setFilter] = useState(FILTER_REFERRERS);
|
||||
const { pageviews } = data || {};
|
||||
const [filter, setFilter] = useState<Key>(FILTER_REFERRERS);
|
||||
const limit = 15;
|
||||
|
||||
const buttons = [
|
||||
@ -48,7 +55,7 @@ export function RealtimeUrls({ websiteDomain, data = {} }) {
|
||||
}
|
||||
return arr;
|
||||
}, [])
|
||||
.sort(firstBy('y', -1))
|
||||
.sort(thenby.firstBy('y', -1))
|
||||
.slice(0, limit),
|
||||
);
|
||||
|
||||
@ -64,7 +71,7 @@ export function RealtimeUrls({ websiteDomain, data = {} }) {
|
||||
}
|
||||
return arr;
|
||||
}, [])
|
||||
.sort(firstBy('y', -1))
|
||||
.sort(thenby.firstBy('y', -1))
|
||||
.slice(0, limit),
|
||||
);
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { getMinimumUnit, parseDateRange } from 'lib/date';
|
||||
import { setItem } from 'next-basics';
|
||||
import { DATE_RANGE_CONFIG, DEFAULT_DATE_RANGE } from 'lib/constants';
|
||||
import useLocale from './useLocale';
|
||||
import websiteStore, { setWebsiteDateRange } from 'store/websites';
|
||||
import appStore, { setDateRange } from 'store/app';
|
||||
import { DateRange } from 'lib/types';
|
||||
import useLocale from './useLocale';
|
||||
import useApi from './useApi';
|
||||
|
||||
export function useDateRange(websiteId?: string) {
|
||||
@ -14,9 +15,9 @@ export function useDateRange(websiteId?: string) {
|
||||
const globalConfig = appStore(state => state.dateRange);
|
||||
const dateRange = parseDateRange(websiteConfig || globalConfig || defaultConfig, locale);
|
||||
|
||||
const saveDateRange = async value => {
|
||||
const saveDateRange = async (value: DateRange | string) => {
|
||||
if (websiteId) {
|
||||
let dateRange = value;
|
||||
let dateRange: DateRange | string = value;
|
||||
|
||||
if (typeof value === 'string') {
|
||||
if (value === 'all') {
|
||||
@ -37,14 +38,17 @@ export function useDateRange(websiteId?: string) {
|
||||
}
|
||||
}
|
||||
|
||||
setWebsiteDateRange(websiteId, dateRange);
|
||||
setWebsiteDateRange(websiteId, dateRange as DateRange);
|
||||
} else {
|
||||
setItem(DATE_RANGE_CONFIG, value);
|
||||
setDateRange(value);
|
||||
}
|
||||
};
|
||||
|
||||
return [dateRange, saveDateRange];
|
||||
return [dateRange, saveDateRange] as [
|
||||
{ startDate: Date; endDate: Date },
|
||||
(value: string | DateRange) => void,
|
||||
];
|
||||
}
|
||||
|
||||
export default useDateRange;
|
||||
|
@ -3,6 +3,7 @@ import { format, startOfMinute, subMinutes, isBefore } from 'date-fns';
|
||||
import PageviewsChart from './PageviewsChart';
|
||||
import { getDateArray } from 'lib/date';
|
||||
import { DEFAULT_ANIMATION_DURATION, REALTIME_RANGE } from 'lib/constants';
|
||||
import { RealtimeData } from 'lib/types';
|
||||
|
||||
function mapData(data: any[]) {
|
||||
let last = 0;
|
||||
@ -24,11 +25,9 @@ function mapData(data: any[]) {
|
||||
}
|
||||
|
||||
export interface RealtimeChartProps {
|
||||
data: {
|
||||
pageviews: any[];
|
||||
visitors: any[];
|
||||
};
|
||||
data: RealtimeData;
|
||||
unit: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function RealtimeChart({ data, unit, ...props }: RealtimeChartProps) {
|
||||
|
@ -32,6 +32,7 @@ import {
|
||||
subWeeks,
|
||||
} from 'date-fns';
|
||||
import { getDateLocale } from 'lib/lang';
|
||||
import { DateRange } from 'lib/types';
|
||||
|
||||
export const TIME_UNIT = {
|
||||
minute: 'minute',
|
||||
@ -54,13 +55,13 @@ export function getTimezone() {
|
||||
return moment.tz.guess();
|
||||
}
|
||||
|
||||
export function getLocalTime(t) {
|
||||
export function getLocalTime(t: string | number | Date) {
|
||||
return addMinutes(new Date(t), new Date().getTimezoneOffset());
|
||||
}
|
||||
|
||||
export function parseDateRange(value, locale = 'en-US') {
|
||||
export function parseDateRange(value: string | object, locale = 'en-US'): DateRange {
|
||||
if (typeof value === 'object') {
|
||||
return value;
|
||||
return value as DateRange;
|
||||
}
|
||||
|
||||
if (value === 'all') {
|
||||
@ -93,7 +94,7 @@ export function parseDateRange(value, locale = 'en-US') {
|
||||
if (!match) return null;
|
||||
|
||||
const { num, unit } = match.groups;
|
||||
const selectedUnit = { num, unit };
|
||||
const selectedUnit = { num: +num, unit };
|
||||
|
||||
if (+num === 1) {
|
||||
switch (unit) {
|
||||
@ -172,7 +173,7 @@ export function parseDateRange(value, locale = 'en-US') {
|
||||
switch (unit) {
|
||||
case 'day':
|
||||
return {
|
||||
startDate: subDays(startOfDay(now), num - 1),
|
||||
startDate: subDays(startOfDay(now), +num - 1),
|
||||
endDate: endOfDay(now),
|
||||
unit,
|
||||
value,
|
||||
@ -180,7 +181,7 @@ export function parseDateRange(value, locale = 'en-US') {
|
||||
};
|
||||
case 'hour':
|
||||
return {
|
||||
startDate: subHours(startOfHour(now), num - 1),
|
||||
startDate: subHours(startOfHour(now), +num - 1),
|
||||
endDate: endOfHour(now),
|
||||
unit,
|
||||
value,
|
||||
@ -189,7 +190,10 @@ export function parseDateRange(value, locale = 'en-US') {
|
||||
}
|
||||
}
|
||||
|
||||
export function incrementDateRange(value, increment) {
|
||||
export function incrementDateRange(
|
||||
value: { startDate: any; endDate: any; selectedUnit: any },
|
||||
increment: number,
|
||||
) {
|
||||
const { startDate, endDate, selectedUnit } = value;
|
||||
|
||||
const { num, unit } = selectedUnit;
|
||||
@ -235,7 +239,7 @@ export function incrementDateRange(value, increment) {
|
||||
}
|
||||
}
|
||||
|
||||
export function getAllowedUnits(startDate, endDate) {
|
||||
export function getAllowedUnits(startDate: Date, endDate: Date) {
|
||||
const units = ['minute', 'hour', 'day', 'month', 'year'];
|
||||
const minUnit = getMinimumUnit(startDate, endDate);
|
||||
const index = units.indexOf(minUnit === 'year' ? 'month' : minUnit);
|
||||
@ -243,7 +247,7 @@ export function getAllowedUnits(startDate, endDate) {
|
||||
return index >= 0 ? units.splice(index) : [];
|
||||
}
|
||||
|
||||
export function getMinimumUnit(startDate, endDate) {
|
||||
export function getMinimumUnit(startDate: number | Date, endDate: number | Date) {
|
||||
if (differenceInMinutes(endDate, startDate) <= 60) {
|
||||
return 'minute';
|
||||
} else if (differenceInHours(endDate, startDate) <= 48) {
|
||||
@ -257,25 +261,25 @@ export function getMinimumUnit(startDate, endDate) {
|
||||
return 'year';
|
||||
}
|
||||
|
||||
export function getDateFromString(str) {
|
||||
export function getDateFromString(str: string) {
|
||||
const [ymd, hms] = str.split(' ');
|
||||
const [year, month, day] = ymd.split('-');
|
||||
|
||||
if (hms) {
|
||||
const [hour, min, sec] = hms.split(':');
|
||||
|
||||
return new Date(year, month - 1, day, hour, min, sec);
|
||||
return new Date(+year, +month - 1, +day, +hour, +min, +sec);
|
||||
}
|
||||
|
||||
return new Date(year, month - 1, day);
|
||||
return new Date(+year, +month - 1, +day);
|
||||
}
|
||||
|
||||
export function getDateArray(data, startDate, endDate, unit) {
|
||||
export function getDateArray(data: any[], startDate: Date, endDate: Date, unit: string) {
|
||||
const arr = [];
|
||||
const [diff, add, normalize] = dateFuncs[unit];
|
||||
const n = diff(endDate, startDate) + 1;
|
||||
|
||||
function findData(date) {
|
||||
function findData(date: Date) {
|
||||
const d = data.find(({ x }) => {
|
||||
return normalize(getDateFromString(x)).getTime() === date.getTime();
|
||||
});
|
||||
@ -293,7 +297,7 @@ export function getDateArray(data, startDate, endDate, unit) {
|
||||
return arr;
|
||||
}
|
||||
|
||||
export function getDateLength(startDate, endDate, unit) {
|
||||
export function getDateLength(startDate: Date, endDate: Date, unit: string | number) {
|
||||
const [diff] = dateFuncs[unit];
|
||||
return diff(endDate, startDate) + 1;
|
||||
}
|
||||
@ -310,7 +314,7 @@ export const CUSTOM_FORMATS = {
|
||||
},
|
||||
};
|
||||
|
||||
export function formatDate(date, str, locale = 'en-US') {
|
||||
export function formatDate(date: string | number | Date, str: string, locale = 'en-US') {
|
||||
return format(
|
||||
typeof date === 'string' ? new Date(date) : date,
|
||||
CUSTOM_FORMATS?.[locale]?.[str] || str,
|
||||
@ -320,10 +324,10 @@ export function formatDate(date, str, locale = 'en-US') {
|
||||
);
|
||||
}
|
||||
|
||||
export function maxDate(...args) {
|
||||
export function maxDate(...args: Date[]) {
|
||||
return max(args.filter(n => isDate(n)));
|
||||
}
|
||||
|
||||
export function minDate(...args) {
|
||||
export function minDate(...args: any[]) {
|
||||
return min(args.filter(n => isDate(n)));
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ export interface DateRange {
|
||||
endDate: Date;
|
||||
value: string;
|
||||
unit?: TimeUnit;
|
||||
selectedUnit?: TimeUnit;
|
||||
selectedUnit?: { num: number; unit: TimeUnit };
|
||||
}
|
||||
|
||||
export interface QueryFilters {
|
||||
@ -207,3 +207,12 @@ export interface QueryOptions {
|
||||
joinSession?: boolean;
|
||||
columns?: { [key: string]: string };
|
||||
}
|
||||
|
||||
export interface RealtimeData {
|
||||
pageviews: any[];
|
||||
sessions: any[];
|
||||
events: any[];
|
||||
timestamp: number;
|
||||
countries?: any[];
|
||||
visitors?: any[];
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user