mirror of
https://github.com/kremalicious/umami.git
synced 2025-01-02 02:03:10 +01:00
Updated fetch hook API.
This commit is contained in:
commit
69b317386a
7
components/common/Tag.js
Normal file
7
components/common/Tag.js
Normal file
@ -0,0 +1,7 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import styles from './Tag.module.css';
|
||||
|
||||
export default function Tag({ className, children }) {
|
||||
return <span className={classNames(styles.tag, className)}>{children}</span>;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
.type {
|
||||
.tag {
|
||||
font-size: var(--font-size-small);
|
||||
padding: 2px 4px;
|
||||
border: 1px solid var(--gray300);
|
@ -5,7 +5,10 @@ import useFetch from 'hooks/useFetch';
|
||||
import styles from './ActiveUsers.module.css';
|
||||
|
||||
export default function ActiveUsers({ websiteId, token, className }) {
|
||||
const { data } = useFetch(`/api/website/${websiteId}/active`, { token }, { interval: 60000 });
|
||||
const { data } = useFetch(`/api/website/${websiteId}/active`, {
|
||||
params: { token },
|
||||
interval: 60000,
|
||||
});
|
||||
const count = useMemo(() => {
|
||||
return data?.[0]?.x || 0;
|
||||
}, [data]);
|
||||
|
@ -17,14 +17,16 @@ export default function EventsChart({ websiteId, token }) {
|
||||
const { data } = useFetch(
|
||||
`/api/website/${websiteId}/events`,
|
||||
{
|
||||
start_at: +startDate,
|
||||
end_at: +endDate,
|
||||
unit,
|
||||
tz: timezone,
|
||||
url: query.url,
|
||||
token,
|
||||
params: {
|
||||
start_at: +startDate,
|
||||
end_at: +endDate,
|
||||
unit,
|
||||
tz: timezone,
|
||||
url: query.url,
|
||||
token,
|
||||
},
|
||||
},
|
||||
{ update: [modified] },
|
||||
[modified],
|
||||
);
|
||||
const datasets = useMemo(() => {
|
||||
if (!data) return [];
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import MetricsTable from './MetricsTable';
|
||||
import styles from './EventsTable.module.css';
|
||||
import Tag from 'components/common/Tag';
|
||||
|
||||
export default function EventsTable({ websiteId, token, limit, onDataLoad }) {
|
||||
return (
|
||||
@ -22,7 +22,7 @@ const Label = ({ value }) => {
|
||||
const [event, label] = value.split(':');
|
||||
return (
|
||||
<>
|
||||
<span className={styles.type}>{event}</span>
|
||||
<Tag>{event}</Tag>
|
||||
{label}
|
||||
</>
|
||||
);
|
||||
|
@ -18,17 +18,19 @@ export default function MetricsBar({ websiteId, token, className }) {
|
||||
query: { url },
|
||||
} = usePageQuery();
|
||||
|
||||
console.log({ modified });
|
||||
|
||||
const { data, error, loading } = useFetch(
|
||||
`/api/website/${websiteId}/stats`,
|
||||
{
|
||||
start_at: +startDate,
|
||||
end_at: +endDate,
|
||||
url,
|
||||
token,
|
||||
},
|
||||
{
|
||||
update: [modified],
|
||||
params: {
|
||||
start_at: +startDate,
|
||||
end_at: +endDate,
|
||||
url,
|
||||
token,
|
||||
},
|
||||
},
|
||||
[modified],
|
||||
);
|
||||
|
||||
const formatFunc = format ? formatLongNumber : formatNumber;
|
||||
|
@ -40,14 +40,18 @@ export default function MetricsTable({
|
||||
const { data, loading, error } = useFetch(
|
||||
`/api/website/${websiteId}/metrics`,
|
||||
{
|
||||
type,
|
||||
start_at: +startDate,
|
||||
end_at: +endDate,
|
||||
domain: websiteDomain,
|
||||
url,
|
||||
token,
|
||||
params: {
|
||||
type,
|
||||
start_at: +startDate,
|
||||
end_at: +endDate,
|
||||
domain: websiteDomain,
|
||||
url,
|
||||
token,
|
||||
},
|
||||
onDataLoad,
|
||||
delay: 300,
|
||||
},
|
||||
{ onDataLoad, delay: 300, update: [modified] },
|
||||
[modified],
|
||||
);
|
||||
const [format, setFormat] = useState(true);
|
||||
const formatFunc = format ? formatLongNumber : formatNumber;
|
||||
|
@ -4,7 +4,7 @@ import tinycolor from 'tinycolor2';
|
||||
import CheckVisible from 'components/helpers/CheckVisible';
|
||||
import BarChart from './BarChart';
|
||||
import useTheme from 'hooks/useTheme';
|
||||
import { THEME_COLORS } from 'lib/constants';
|
||||
import { THEME_COLORS, DEFAULT_ANIMATION_DURATION } from 'lib/constants';
|
||||
|
||||
export default function PageviewsChart({
|
||||
websiteId,
|
||||
@ -13,7 +13,7 @@ export default function PageviewsChart({
|
||||
records,
|
||||
className,
|
||||
loading,
|
||||
animationDuration = 300,
|
||||
animationDuration = DEFAULT_ANIMATION_DURATION,
|
||||
}) {
|
||||
const intl = useIntl();
|
||||
const [theme] = useTheme();
|
||||
|
@ -2,6 +2,7 @@ import React, { useMemo, useRef } from 'react';
|
||||
import { format, parseISO, startOfMinute, subMinutes, isBefore } from 'date-fns';
|
||||
import PageviewsChart from './PageviewsChart';
|
||||
import { getDateArray } from 'lib/date';
|
||||
import { DEFAULT_ANIMATION_DURATION } from 'lib/constants';
|
||||
|
||||
function mapData(data) {
|
||||
let last = 0;
|
||||
@ -44,7 +45,7 @@ export default function RealtimeChart({ data, unit, ...props }) {
|
||||
prevEndDate.current = endDate;
|
||||
return 0;
|
||||
}
|
||||
return 300;
|
||||
return DEFAULT_ANIMATION_DURATION;
|
||||
}, [data]);
|
||||
|
||||
return (
|
||||
|
@ -5,6 +5,7 @@ import firstBy from 'thenby';
|
||||
import { format } from 'date-fns';
|
||||
import Icon from 'components/common/Icon';
|
||||
import Table, { TableRow } from 'components/common/Table';
|
||||
import Tag from 'components/common/Tag';
|
||||
import useLocale from 'hooks/useLocale';
|
||||
import useCountryNames from 'hooks/useCountryNames';
|
||||
import { BROWSERS } from 'lib/constants';
|
||||
@ -92,7 +93,7 @@ export default function RealtimeLog({ data, websites }) {
|
||||
if (event_type) {
|
||||
return (
|
||||
<div>
|
||||
<span className={styles.event}>{event_type}</span> {event_value}
|
||||
<Tag>{event_type}</Tag> {event_value}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -12,14 +12,6 @@
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.event {
|
||||
font-size: var(--font-size-small);
|
||||
padding: 2px 4px;
|
||||
border: 1px solid var(--gray300);
|
||||
border-radius: 4px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
align-self: center;
|
||||
margin-right: 20px;
|
||||
|
@ -35,14 +35,17 @@ export default function WebsiteChart({
|
||||
const { data, loading, error } = useFetch(
|
||||
`/api/website/${websiteId}/pageviews`,
|
||||
{
|
||||
start_at: +startDate,
|
||||
end_at: +endDate,
|
||||
unit,
|
||||
tz: timezone,
|
||||
url,
|
||||
token,
|
||||
params: {
|
||||
start_at: +startDate,
|
||||
end_at: +endDate,
|
||||
unit,
|
||||
tz: timezone,
|
||||
url,
|
||||
token,
|
||||
},
|
||||
onDataLoad,
|
||||
},
|
||||
{ onDataLoad, update: [modified] },
|
||||
[modified],
|
||||
);
|
||||
|
||||
const chartData = useMemo(() => {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { subMinutes, startOfMinute } from 'date-fns';
|
||||
import Page from 'components/layout/Page';
|
||||
@ -18,19 +18,34 @@ function mergeData(state, data, time) {
|
||||
.filter(({ created_at }) => new Date(created_at).getTime() >= time);
|
||||
}
|
||||
|
||||
function filterWebsite(data, id) {
|
||||
return data.filter(({ website_id }) => website_id === id);
|
||||
}
|
||||
|
||||
export default function RealtimeDashboard() {
|
||||
const [data, setData] = useState();
|
||||
const [website, setWebsite] = useState();
|
||||
const { data: init, loading } = useFetch('/api/realtime', { type: 'init' });
|
||||
const { data: updates } = useFetch(
|
||||
'/api/realtime',
|
||||
{ type: 'update', start_at: data?.timestamp },
|
||||
{
|
||||
disabled: !init?.token || !data,
|
||||
interval: REALTIME_INTERVAL,
|
||||
headers: { 'x-umami-token': init?.token },
|
||||
},
|
||||
);
|
||||
const { data: init, loading } = useFetch('/api/realtime', { params: { type: 'init' } });
|
||||
const { data: updates } = useFetch('/api/realtime', {
|
||||
params: { type: 'update', start_at: data?.timestamp },
|
||||
disabled: !init?.token || !data,
|
||||
interval: REALTIME_INTERVAL,
|
||||
headers: { 'x-umami-token': init?.token },
|
||||
});
|
||||
|
||||
const realtimeData = useMemo(() => {
|
||||
if (website) {
|
||||
const { website_id } = website;
|
||||
const { pageviews, sessions, events, ...props } = data;
|
||||
return {
|
||||
pageviews: filterWebsite(pageviews, website_id),
|
||||
sessions: filterWebsite(sessions, website_id),
|
||||
events: filterWebsite(events, website_id),
|
||||
...props,
|
||||
};
|
||||
}
|
||||
return data;
|
||||
}, [data, website]);
|
||||
|
||||
useEffect(() => {
|
||||
if (init && !data) {
|
||||
@ -70,10 +85,15 @@ export default function RealtimeDashboard() {
|
||||
</div>
|
||||
<DropDown value={selectedValue} options={options} onChange={handleSelect} />
|
||||
</PageHeader>
|
||||
<RealtimeChart websiteId={website?.website_id} data={data} unit="minute" records={30} />
|
||||
<RealtimeChart
|
||||
websiteId={website?.website_id}
|
||||
data={realtimeData}
|
||||
unit="minute"
|
||||
records={REALTIME_RANGE}
|
||||
/>
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
<RealtimeLog data={data} websites={websites} />
|
||||
<RealtimeLog data={realtimeData} websites={websites} />
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
|
@ -31,7 +31,7 @@ const views = {
|
||||
};
|
||||
|
||||
export default function WebsiteDetails({ websiteId, token }) {
|
||||
const { data } = useFetch(`/api/website/${websiteId}`, { token });
|
||||
const { data } = useFetch(`/api/website/${websiteId}`, { params: { token } });
|
||||
const [chartLoaded, setChartLoaded] = useState(false);
|
||||
const [countryData, setCountryData] = useState();
|
||||
const [eventsData, setEventsData] = useState();
|
||||
|
@ -9,7 +9,7 @@ import Arrow from 'assets/arrow-right.svg';
|
||||
import styles from './WebsiteList.module.css';
|
||||
|
||||
export default function WebsiteList({ userId }) {
|
||||
const { data } = useFetch('/api/websites', { user_id: userId });
|
||||
const { data } = useFetch('/api/websites', { params: { user_id: userId } });
|
||||
|
||||
if (!data) {
|
||||
return null;
|
||||
|
@ -25,7 +25,7 @@ export default function AccountSettings() {
|
||||
const [deleteAccount, setDeleteAccount] = useState();
|
||||
const [saved, setSaved] = useState(0);
|
||||
const [message, setMessage] = useState();
|
||||
const { data } = useFetch(`/api/accounts`, {}, { update: [saved] });
|
||||
const { data } = useFetch(`/api/accounts`, {}, [saved]);
|
||||
|
||||
const Checkmark = ({ is_admin }) => (is_admin ? <Icon icon={<Check />} size="medium" /> : null);
|
||||
|
||||
|
@ -29,7 +29,7 @@ export default function WebsiteSettings() {
|
||||
const [showUrl, setShowUrl] = useState();
|
||||
const [saved, setSaved] = useState(0);
|
||||
const [message, setMessage] = useState();
|
||||
const { data } = useFetch(`/api/websites`, {}, { update: [saved] });
|
||||
const { data } = useFetch(`/api/websites`, {}, [saved]);
|
||||
|
||||
const Buttons = row => (
|
||||
<ButtonLayout align="right">
|
||||
|
@ -4,14 +4,14 @@ import { get } from 'lib/web';
|
||||
import { updateQuery } from 'redux/actions/queries';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
export default function useFetch(url, params = {}, options = {}) {
|
||||
export default function useFetch(url, options = {}, update = []) {
|
||||
const dispatch = useDispatch();
|
||||
const [data, setData] = useState();
|
||||
const [status, setStatus] = useState();
|
||||
const [error, setError] = useState();
|
||||
const [loading, setLoadiing] = useState(false);
|
||||
const { basePath } = useRouter();
|
||||
const { update = [], onDataLoad = () => {}, disabled, headers, interval, delay = 0 } = options;
|
||||
const { params, disabled, headers, interval, delay = 0, onDataLoad } = options;
|
||||
|
||||
async function loadData() {
|
||||
try {
|
||||
@ -30,7 +30,7 @@ export default function useFetch(url, params = {}, options = {}) {
|
||||
}
|
||||
|
||||
setStatus(status);
|
||||
onDataLoad(data);
|
||||
onDataLoad?.(data);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
setError(e);
|
||||
@ -41,17 +41,17 @@ export default function useFetch(url, params = {}, options = {}) {
|
||||
|
||||
useEffect(() => {
|
||||
if (url && !disabled) {
|
||||
if (!data) {
|
||||
setTimeout(() => loadData(), delay);
|
||||
}
|
||||
|
||||
const id = interval ? setInterval(() => loadData(), interval) : null;
|
||||
|
||||
return () => {
|
||||
clearInterval(id);
|
||||
};
|
||||
setTimeout(() => loadData(), delay);
|
||||
}
|
||||
}, [data, url, disabled, ...update]);
|
||||
}, [url, disabled, ...update]);
|
||||
|
||||
useEffect(() => {
|
||||
const id = interval ? setInterval(() => loadData(), interval) : null;
|
||||
|
||||
return () => {
|
||||
clearInterval(id);
|
||||
};
|
||||
}, [interval, params]);
|
||||
|
||||
return { data, status, error, loading };
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ export const EVENT_COLORS = [
|
||||
'#ffec16',
|
||||
];
|
||||
|
||||
export const DEFAULT_ANIMATION_DURATION = 300;
|
||||
export const DEFAULT_DATE_RANGE = '24hour';
|
||||
|
||||
export const POSTGRESQL = 'postgresql';
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "umami",
|
||||
"version": "0.82.0",
|
||||
"version": "0.88.0",
|
||||
"description": "A simple, fast, website analytics alternative to Google Analytics. ",
|
||||
"author": "Mike Cao <mike@mikecao.com>",
|
||||
"license": "MIT",
|
||||
|
@ -8,7 +8,7 @@ export default function SharePage() {
|
||||
const router = useRouter();
|
||||
const { id } = router.query;
|
||||
const shareId = id?.[0];
|
||||
const { data } = useFetch(shareId ? `/api/share/${shareId}` : null);
|
||||
const { data } = useFetch(`/api/share/${shareId}`, { disabled: !shareId });
|
||||
|
||||
if (!data) {
|
||||
return null;
|
||||
|
Loading…
Reference in New Issue
Block a user