mirror of
https://github.com/kremalicious/umami.git
synced 2024-11-15 17:55:08 +01:00
Merge pull request #234 from mikecao/dev
v0.62.0 Prisma update and version check
This commit is contained in:
commit
241156d9ee
@ -37,7 +37,7 @@ export default function DropDown({
|
|||||||
return (
|
return (
|
||||||
<div ref={ref} className={classNames(styles.dropdown, className)} onClick={handleShowMenu}>
|
<div ref={ref} className={classNames(styles.dropdown, className)} onClick={handleShowMenu}>
|
||||||
<div className={styles.value}>
|
<div className={styles.value}>
|
||||||
{options.find(e => e.value === value)?.label || value}
|
<div className={styles.text}>{options.find(e => e.value === value)?.label || value}</div>
|
||||||
<Icon icon={<Chevron />} className={styles.icon} size="small" />
|
<Icon icon={<Chevron />} className={styles.icon} size="small" />
|
||||||
</div>
|
</div>
|
||||||
{showMenu && (
|
{showMenu && (
|
||||||
|
@ -19,6 +19,10 @@
|
|||||||
min-width: 160px;
|
min-width: 160px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
}
|
}
|
||||||
|
47
components/common/UpdateNotice.js
Normal file
47
components/common/UpdateNotice.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
import useVersion from 'hooks/useVersion';
|
||||||
|
import styles from './UpdateNotice.module.css';
|
||||||
|
import ButtonLayout from '../layout/ButtonLayout';
|
||||||
|
import Button from './Button';
|
||||||
|
import useForceUpdate from '../../hooks/useForceUpdate';
|
||||||
|
|
||||||
|
export default function UpdateNotice() {
|
||||||
|
const forceUpdte = useForceUpdate();
|
||||||
|
const { hasUpdate, latest, updateCheck } = useVersion();
|
||||||
|
|
||||||
|
function handleViewClick() {
|
||||||
|
location.href = 'https://github.com/mikecao/umami/releases';
|
||||||
|
updateCheck();
|
||||||
|
forceUpdte();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDismissClick() {
|
||||||
|
updateCheck();
|
||||||
|
forceUpdte();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasUpdate) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.notice}>
|
||||||
|
<div className={styles.message}>
|
||||||
|
<FormattedMessage
|
||||||
|
id="message.new-version-available"
|
||||||
|
defaultMessage="A new version of umami {version} is available!"
|
||||||
|
values={{ version: `v${latest}` }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<ButtonLayout>
|
||||||
|
<Button size="xsmall" variant="action" onClick={handleViewClick}>
|
||||||
|
<FormattedMessage id="button.view-details" defaultMessage="View details" />
|
||||||
|
</Button>
|
||||||
|
<Button size="xsmall" onClick={handleDismissClick}>
|
||||||
|
<FormattedMessage id="button.dismiss" defaultMessage="Dismiss" />
|
||||||
|
</Button>
|
||||||
|
</ButtonLayout>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
13
components/common/UpdateNotice.module.css
Normal file
13
components/common/UpdateNotice.module.css
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
.notice {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 10px;
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
text-align: center;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
@ -6,6 +6,7 @@ import Link from 'components/common/Link';
|
|||||||
import Icon from 'components/common/Icon';
|
import Icon from 'components/common/Icon';
|
||||||
import LanguageButton from 'components/settings/LanguageButton';
|
import LanguageButton from 'components/settings/LanguageButton';
|
||||||
import ThemeButton from 'components/settings/ThemeButton';
|
import ThemeButton from 'components/settings/ThemeButton';
|
||||||
|
import UpdateNotice from 'components/common/UpdateNotice';
|
||||||
import UserButton from 'components/settings/UserButton';
|
import UserButton from 'components/settings/UserButton';
|
||||||
import Logo from 'assets/logo.svg';
|
import Logo from 'assets/logo.svg';
|
||||||
import styles from './Header.module.css';
|
import styles from './Header.module.css';
|
||||||
@ -15,6 +16,7 @@ export default function Header() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="container">
|
<header className="container">
|
||||||
|
{user?.is_admin && <UpdateNotice />}
|
||||||
<div className={classNames(styles.header, 'row align-items-center')}>
|
<div className={classNames(styles.header, 'row align-items-center')}>
|
||||||
<div className="col-12 col-md-12 col-lg-3">
|
<div className="col-12 col-md-12 col-lg-3">
|
||||||
<div className={styles.title}>
|
<div className={styles.title}>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
import styles from './Page.module.css';
|
import styles from './Page.module.css';
|
||||||
|
|
||||||
export default function Page({ children }) {
|
export default function Page({ className, children }) {
|
||||||
return <div className={styles.page}>{children}</div>;
|
return <div className={classNames(styles.page, className)}>{children}</div>;
|
||||||
}
|
}
|
||||||
|
@ -4,4 +4,5 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
align-content: center;
|
align-content: center;
|
||||||
min-height: 80px;
|
min-height: 80px;
|
||||||
|
align-self: stretch;
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ export default function WebsiteChart({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className={styles.container}>
|
||||||
<WebsiteHeader websiteId={websiteId} token={token} title={title} showLink={showLink} />
|
<WebsiteHeader websiteId={websiteId} token={token} title={title} showLink={showLink} />
|
||||||
<div className={classNames(styles.header, 'row')}>
|
<div className={classNames(styles.header, 'row')}>
|
||||||
<StickyHeader
|
<StickyHeader
|
||||||
@ -92,7 +92,7 @@ export default function WebsiteChart({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
.container {
|
.container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
align-self: stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
|
@ -2,9 +2,9 @@ import React, { useState } from 'react';
|
|||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import Page from 'components/layout/Page';
|
import Page from 'components/layout/Page';
|
||||||
import MenuLayout from 'components/layout/MenuLayout';
|
import MenuLayout from 'components/layout/MenuLayout';
|
||||||
import WebsiteSettings from './WebsiteSettings';
|
import WebsiteSettings from '../settings/WebsiteSettings';
|
||||||
import AccountSettings from './AccountSettings';
|
import AccountSettings from '../settings/AccountSettings';
|
||||||
import ProfileSettings from './ProfileSettings';
|
import ProfileSettings from '../settings/ProfileSettings';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ export default function Settings() {
|
|||||||
{
|
{
|
||||||
label: <FormattedMessage id="label.accounts" defaultMessage="Accounts" />,
|
label: <FormattedMessage id="label.accounts" defaultMessage="Accounts" />,
|
||||||
value: ACCOUNTS,
|
value: ACCOUNTS,
|
||||||
hidden: !user.is_admin,
|
hidden: !user?.is_admin,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: <FormattedMessage id="label.profile" defaultMessage="Profile" />,
|
label: <FormattedMessage id="label.profile" defaultMessage="Profile" />,
|
5
components/pages/Test.module.css
Normal file
5
components/pages/Test.module.css
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
.test {
|
||||||
|
border: 1px solid var(--gray200);
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 0 20px 20px 20px;
|
||||||
|
}
|
94
components/pages/TestConsole.js
Normal file
94
components/pages/TestConsole.js
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import Head from 'next/head';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import Page from '../layout/Page';
|
||||||
|
import PageHeader from '../layout/PageHeader';
|
||||||
|
import useFetch from '../../hooks/useFetch';
|
||||||
|
import DropDown from '../common/DropDown';
|
||||||
|
import styles from './Test.module.css';
|
||||||
|
import WebsiteChart from '../metrics/WebsiteChart';
|
||||||
|
import EventsChart from '../metrics/EventsChart';
|
||||||
|
import Button from '../common/Button';
|
||||||
|
import EmptyPlaceholder from '../common/EmptyPlaceholder';
|
||||||
|
|
||||||
|
export default function TestConsole() {
|
||||||
|
const user = useSelector(state => state.user);
|
||||||
|
const [website, setWebsite] = useState();
|
||||||
|
const { data } = useFetch('/api/websites');
|
||||||
|
|
||||||
|
if (!data || !user?.is_admin) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = data.map(({ name, website_id }) => ({ label: name, value: website_id }));
|
||||||
|
const selectedValue = options.find(({ value }) => value === website?.website_id)?.value;
|
||||||
|
|
||||||
|
function handleSelect(value) {
|
||||||
|
setWebsite(data.find(({ website_id }) => website_id === value));
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClick() {
|
||||||
|
window.umami('event (default)');
|
||||||
|
window.umami.trackView('/page-view', 'https://www.google.com');
|
||||||
|
window.umami.trackEvent('event (custom)', 'custom-type');
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Page>
|
||||||
|
<Head>
|
||||||
|
{typeof window !== 'undefined' && website && (
|
||||||
|
<script async defer data-website-id={website.website_uuid} src="/umami.js" />
|
||||||
|
)}
|
||||||
|
</Head>
|
||||||
|
<PageHeader>
|
||||||
|
<div>Test Console</div>
|
||||||
|
<DropDown
|
||||||
|
value={selectedValue || 'Select website'}
|
||||||
|
options={options}
|
||||||
|
onChange={handleSelect}
|
||||||
|
/>
|
||||||
|
</PageHeader>
|
||||||
|
{!selectedValue && <EmptyPlaceholder msg="I hope you know what you're doing here" />}
|
||||||
|
{selectedValue && (
|
||||||
|
<>
|
||||||
|
<div className={classNames(styles.test, 'row')}>
|
||||||
|
<div className="col-4">
|
||||||
|
<PageHeader>Page links</PageHeader>
|
||||||
|
<div>
|
||||||
|
<Link href={`?page=1`}>
|
||||||
|
<a>page one</a>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Link href={`?page=2`}>
|
||||||
|
<a>page two</a>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-4">
|
||||||
|
<PageHeader>CSS events</PageHeader>
|
||||||
|
<Button id="primary-button" className="umami--click--primary-button" variant="action">
|
||||||
|
Send event
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="col-4">
|
||||||
|
<PageHeader>Javascript events</PageHeader>
|
||||||
|
<Button id="manual-button" variant="action" onClick={handleClick}>
|
||||||
|
Run script
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-12">
|
||||||
|
<WebsiteChart websiteId={website.website_id} title={website.name} showLink />
|
||||||
|
<PageHeader>Events</PageHeader>
|
||||||
|
<EventsChart websiteId={website.website_id} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Page>
|
||||||
|
);
|
||||||
|
}
|
@ -9,14 +9,14 @@ import Link from 'components/common/Link';
|
|||||||
import Loading from 'components/common/Loading';
|
import Loading from 'components/common/Loading';
|
||||||
import Arrow from 'assets/arrow-right.svg';
|
import Arrow from 'assets/arrow-right.svg';
|
||||||
import styles from './WebsiteDetails.module.css';
|
import styles from './WebsiteDetails.module.css';
|
||||||
import PagesTable from './metrics/PagesTable';
|
import PagesTable from '../metrics/PagesTable';
|
||||||
import ReferrersTable from './metrics/ReferrersTable';
|
import ReferrersTable from '../metrics/ReferrersTable';
|
||||||
import BrowsersTable from './metrics/BrowsersTable';
|
import BrowsersTable from '../metrics/BrowsersTable';
|
||||||
import OSTable from './metrics/OSTable';
|
import OSTable from '../metrics/OSTable';
|
||||||
import DevicesTable from './metrics/DevicesTable';
|
import DevicesTable from '../metrics/DevicesTable';
|
||||||
import CountriesTable from './metrics/CountriesTable';
|
import CountriesTable from '../metrics/CountriesTable';
|
||||||
import EventsTable from './metrics/EventsTable';
|
import EventsTable from '../metrics/EventsTable';
|
||||||
import EventsChart from './metrics/EventsChart';
|
import EventsChart from '../metrics/EventsChart';
|
||||||
import useFetch from 'hooks/useFetch';
|
import useFetch from 'hooks/useFetch';
|
||||||
import usePageQuery from 'hooks/usePageQuery';
|
import usePageQuery from 'hooks/usePageQuery';
|
||||||
|
|
||||||
@ -42,16 +42,17 @@ export default function WebsiteDetails({ websiteId, token }) {
|
|||||||
} = usePageQuery();
|
} = usePageQuery();
|
||||||
|
|
||||||
const BackButton = () => (
|
const BackButton = () => (
|
||||||
<Link
|
<div className={styles.backButton}>
|
||||||
key="back-button"
|
<Link
|
||||||
className={styles.backButton}
|
key="back-button"
|
||||||
href={router.pathname}
|
href={router.pathname}
|
||||||
as={resolve({ view: undefined })}
|
as={resolve({ view: undefined })}
|
||||||
icon={<Arrow />}
|
icon={<Arrow />}
|
||||||
size="small"
|
size="small"
|
||||||
>
|
>
|
||||||
<FormattedMessage id="button.back" defaultMessage="Back" />
|
<FormattedMessage id="button.back" defaultMessage="Back" />
|
||||||
</Link>
|
</Link>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const menuOptions = [
|
const menuOptions = [
|
@ -16,6 +16,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.backButton {
|
.backButton {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
|||||||
padding-bottom: 30px;
|
padding-bottom: 30px;
|
||||||
border-bottom: 1px solid var(--gray300);
|
border-bottom: 1px solid var(--gray300);
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
|
align-self: stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
.website:last-child {
|
.website:last-child {
|
@ -10,6 +10,7 @@ import TimezoneSetting from 'components/settings/TimezoneSetting';
|
|||||||
import Dots from 'assets/ellipsis-h.svg';
|
import Dots from 'assets/ellipsis-h.svg';
|
||||||
import styles from './ProfileSettings.module.css';
|
import styles from './ProfileSettings.module.css';
|
||||||
import DateRangeSetting from './DateRangeSetting';
|
import DateRangeSetting from './DateRangeSetting';
|
||||||
|
import useEscapeKey from 'hooks/useEscapeKey';
|
||||||
|
|
||||||
export default function ProfileSettings() {
|
export default function ProfileSettings() {
|
||||||
const user = useSelector(state => state.user);
|
const user = useSelector(state => state.user);
|
||||||
@ -22,6 +23,10 @@ export default function ProfileSettings() {
|
|||||||
setMessage(<FormattedMessage id="message.save-success" defaultMessage="Saved successfully." />);
|
setMessage(<FormattedMessage id="message.save-success" defaultMessage="Saved successfully." />);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEscapeKey(() => {
|
||||||
|
setChangePassword(false);
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
|
19
hooks/useEscapeKey.js
Normal file
19
hooks/useEscapeKey.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { useEffect, useCallback } from 'react';
|
||||||
|
|
||||||
|
export default function useEscapeKey(handler) {
|
||||||
|
const escFunction = useCallback(event => {
|
||||||
|
if (event.keyCode === 27) {
|
||||||
|
handler(event);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.addEventListener('keydown', escFunction, false);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('keydown', escFunction, false);
|
||||||
|
};
|
||||||
|
}, [escFunction]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
27
hooks/useVersion.js
Normal file
27
hooks/useVersion.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { useEffect, useCallback } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import semver from 'semver';
|
||||||
|
import { getItem, setItem } from 'lib/web';
|
||||||
|
import { checkVersion } from 'redux/actions/app';
|
||||||
|
import { VERSION_CHECK } from 'lib/constants';
|
||||||
|
|
||||||
|
export default function useVersion() {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const versions = useSelector(state => state.app.versions);
|
||||||
|
const lastCheck = getItem(VERSION_CHECK);
|
||||||
|
|
||||||
|
const { current, latest } = versions;
|
||||||
|
const hasUpdate = latest && semver.gt(latest, current) && lastCheck?.version !== latest;
|
||||||
|
|
||||||
|
const updateCheck = useCallback(() => {
|
||||||
|
setItem(VERSION_CHECK, { version: latest, time: Date.now() });
|
||||||
|
}, [versions]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!versions.latest) {
|
||||||
|
dispatch(checkVersion());
|
||||||
|
}
|
||||||
|
}, [versions]);
|
||||||
|
|
||||||
|
return { ...versions, hasUpdate, updateCheck };
|
||||||
|
}
|
@ -7,6 +7,7 @@
|
|||||||
"button.copy-to-clipboard": "Kopier til udklipsholder",
|
"button.copy-to-clipboard": "Kopier til udklipsholder",
|
||||||
"button.date-range": "Datointerval",
|
"button.date-range": "Datointerval",
|
||||||
"button.delete": "Slet",
|
"button.delete": "Slet",
|
||||||
|
"button.dismiss": "Dismiss",
|
||||||
"button.edit": "Rediger",
|
"button.edit": "Rediger",
|
||||||
"button.login": "Log ind",
|
"button.login": "Log ind",
|
||||||
"button.more": "Mere",
|
"button.more": "Mere",
|
||||||
@ -54,6 +55,7 @@
|
|||||||
"message.get-tracking-code": "Få sporingskode",
|
"message.get-tracking-code": "Få sporingskode",
|
||||||
"message.go-to-settings": "Gå til betjeningspanel",
|
"message.go-to-settings": "Gå til betjeningspanel",
|
||||||
"message.incorrect-username-password": "Ugyldigt brugernavn/adgangskode.",
|
"message.incorrect-username-password": "Ugyldigt brugernavn/adgangskode.",
|
||||||
|
"message.new-version-available": "A new version of umami {version} is available!",
|
||||||
"message.no-data-available": "Ingen data tilgængelig.",
|
"message.no-data-available": "Ingen data tilgængelig.",
|
||||||
"message.no-websites-configured": "Du har ikke konfigureret nogen websteder.",
|
"message.no-websites-configured": "Du har ikke konfigureret nogen websteder.",
|
||||||
"message.page-not-found": "Side ikke fundet.",
|
"message.page-not-found": "Side ikke fundet.",
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"button.copy-to-clipboard": "In die Zwischenablage kopieren",
|
"button.copy-to-clipboard": "In die Zwischenablage kopieren",
|
||||||
"button.date-range": "Datumsbereich",
|
"button.date-range": "Datumsbereich",
|
||||||
"button.delete": "Löschen",
|
"button.delete": "Löschen",
|
||||||
|
"button.dismiss": "Dismiss",
|
||||||
"button.edit": "Bearbeiten",
|
"button.edit": "Bearbeiten",
|
||||||
"button.login": "Anmelden",
|
"button.login": "Anmelden",
|
||||||
"button.more": "Mehr",
|
"button.more": "Mehr",
|
||||||
@ -54,6 +55,7 @@
|
|||||||
"message.get-tracking-code": "Erstelle Tracking Kennung",
|
"message.get-tracking-code": "Erstelle Tracking Kennung",
|
||||||
"message.go-to-settings": "Zu den Einstellungen",
|
"message.go-to-settings": "Zu den Einstellungen",
|
||||||
"message.incorrect-username-password": "Falsches Passwort oder Benutzername.",
|
"message.incorrect-username-password": "Falsches Passwort oder Benutzername.",
|
||||||
|
"message.new-version-available": "A new version of umami {version} is available!",
|
||||||
"message.no-data-available": "Keine Daten vorhanden.",
|
"message.no-data-available": "Keine Daten vorhanden.",
|
||||||
"message.no-websites-configured": "Es ist keine Webseite vorhanden.",
|
"message.no-websites-configured": "Es ist keine Webseite vorhanden.",
|
||||||
"message.page-not-found": "Seite nicht gefunden.",
|
"message.page-not-found": "Seite nicht gefunden.",
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"button.copy-to-clipboard": "Αντιγραφή στο πρόχειρο",
|
"button.copy-to-clipboard": "Αντιγραφή στο πρόχειρο",
|
||||||
"button.date-range": "Εύρος ημερομηνιών",
|
"button.date-range": "Εύρος ημερομηνιών",
|
||||||
"button.delete": "Διαγραφή",
|
"button.delete": "Διαγραφή",
|
||||||
|
"button.dismiss": "Dismiss",
|
||||||
"button.edit": "Επεξεργασία",
|
"button.edit": "Επεξεργασία",
|
||||||
"button.login": "Είσοδος",
|
"button.login": "Είσοδος",
|
||||||
"button.more": "Περισσότερα",
|
"button.more": "Περισσότερα",
|
||||||
@ -54,6 +55,7 @@
|
|||||||
"message.get-tracking-code": "Λήψη κώδικα παρακολούθησης",
|
"message.get-tracking-code": "Λήψη κώδικα παρακολούθησης",
|
||||||
"message.go-to-settings": "Μεταβείτε στις ρυθμίσεις",
|
"message.go-to-settings": "Μεταβείτε στις ρυθμίσεις",
|
||||||
"message.incorrect-username-password": "Εσφαλμένο όνομα χρήστη / κωδικός πρόσβασης.",
|
"message.incorrect-username-password": "Εσφαλμένο όνομα χρήστη / κωδικός πρόσβασης.",
|
||||||
|
"message.new-version-available": "A new version of umami {version} is available!",
|
||||||
"message.no-data-available": "Δεν υπάρχουν διαθέσιμα δεδομένα.",
|
"message.no-data-available": "Δεν υπάρχουν διαθέσιμα δεδομένα.",
|
||||||
"message.no-websites-configured": "Δεν έχετε ρυθμίσει κανένα ιστότοπο.",
|
"message.no-websites-configured": "Δεν έχετε ρυθμίσει κανένα ιστότοπο.",
|
||||||
"message.page-not-found": "Η σελίδα δεν βρέθηκε.",
|
"message.page-not-found": "Η σελίδα δεν βρέθηκε.",
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"button.copy-to-clipboard": "Copy to clipboard",
|
"button.copy-to-clipboard": "Copy to clipboard",
|
||||||
"button.date-range": "Date range",
|
"button.date-range": "Date range",
|
||||||
"button.delete": "Delete",
|
"button.delete": "Delete",
|
||||||
|
"button.dismiss": "Dismiss",
|
||||||
"button.edit": "Edit",
|
"button.edit": "Edit",
|
||||||
"button.login": "Login",
|
"button.login": "Login",
|
||||||
"button.more": "More",
|
"button.more": "More",
|
||||||
@ -54,6 +55,7 @@
|
|||||||
"message.get-tracking-code": "Get tracking code",
|
"message.get-tracking-code": "Get tracking code",
|
||||||
"message.go-to-settings": "Go to settings",
|
"message.go-to-settings": "Go to settings",
|
||||||
"message.incorrect-username-password": "Incorrect username/password.",
|
"message.incorrect-username-password": "Incorrect username/password.",
|
||||||
|
"message.new-version-available": "A new version of umami {version} is available!",
|
||||||
"message.no-data-available": "No data available.",
|
"message.no-data-available": "No data available.",
|
||||||
"message.no-websites-configured": "You don't have any websites configured.",
|
"message.no-websites-configured": "You don't have any websites configured.",
|
||||||
"message.page-not-found": "Page not found.",
|
"message.page-not-found": "Page not found.",
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"button.copy-to-clipboard": "Copiar al portapapeles",
|
"button.copy-to-clipboard": "Copiar al portapapeles",
|
||||||
"button.date-range": "Date range",
|
"button.date-range": "Date range",
|
||||||
"button.delete": "Eliminar",
|
"button.delete": "Eliminar",
|
||||||
|
"button.dismiss": "Dismiss",
|
||||||
"button.edit": "Editar",
|
"button.edit": "Editar",
|
||||||
"button.login": "Iniciar sesión",
|
"button.login": "Iniciar sesión",
|
||||||
"button.more": "Más",
|
"button.more": "Más",
|
||||||
@ -54,6 +55,7 @@
|
|||||||
"message.get-tracking-code": "Obtener código de rastreo",
|
"message.get-tracking-code": "Obtener código de rastreo",
|
||||||
"message.go-to-settings": "Ir a la configuración",
|
"message.go-to-settings": "Ir a la configuración",
|
||||||
"message.incorrect-username-password": "Nombre de usuario o contraseña incorrectos.",
|
"message.incorrect-username-password": "Nombre de usuario o contraseña incorrectos.",
|
||||||
|
"message.new-version-available": "A new version of umami {version} is available!",
|
||||||
"message.no-data-available": "Sin información disponible.",
|
"message.no-data-available": "Sin información disponible.",
|
||||||
"message.no-websites-configured": "No tienes ningún sitio configurado.",
|
"message.no-websites-configured": "No tienes ningún sitio configurado.",
|
||||||
"message.page-not-found": "Page not found",
|
"message.page-not-found": "Page not found",
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"button.copy-to-clipboard": "Kopier til clipboard",
|
"button.copy-to-clipboard": "Kopier til clipboard",
|
||||||
"button.date-range": "Vel dato",
|
"button.date-range": "Vel dato",
|
||||||
"button.delete": "Sletta",
|
"button.delete": "Sletta",
|
||||||
|
"button.dismiss": "Dismiss",
|
||||||
"button.edit": "Ger broyting",
|
"button.edit": "Ger broyting",
|
||||||
"button.login": "Rita inn",
|
"button.login": "Rita inn",
|
||||||
"button.more": "Meira",
|
"button.more": "Meira",
|
||||||
@ -54,6 +55,7 @@
|
|||||||
"message.get-tracking-code": "Fá sporings kotu",
|
"message.get-tracking-code": "Fá sporings kotu",
|
||||||
"message.go-to-settings": "Far til stillingar",
|
"message.go-to-settings": "Far til stillingar",
|
||||||
"message.incorrect-username-password": "Skeivt brúkaranavn/loyniorð.",
|
"message.incorrect-username-password": "Skeivt brúkaranavn/loyniorð.",
|
||||||
|
"message.new-version-available": "A new version of umami {version} is available!",
|
||||||
"message.no-data-available": "Einki data tøk.",
|
"message.no-data-available": "Einki data tøk.",
|
||||||
"message.no-websites-configured": "Tú hevur ongar heimasíður stillaða til.",
|
"message.no-websites-configured": "Tú hevur ongar heimasíður stillaða til.",
|
||||||
"message.page-not-found": "Síðan bleiv ikki funnin.",
|
"message.page-not-found": "Síðan bleiv ikki funnin.",
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"button.copy-to-clipboard": "Copier dans le presse papier",
|
"button.copy-to-clipboard": "Copier dans le presse papier",
|
||||||
"button.date-range": "Date range",
|
"button.date-range": "Date range",
|
||||||
"button.delete": "Supprimer",
|
"button.delete": "Supprimer",
|
||||||
|
"button.dismiss": "Dismiss",
|
||||||
"button.edit": "Modifier",
|
"button.edit": "Modifier",
|
||||||
"button.login": "Connexion",
|
"button.login": "Connexion",
|
||||||
"button.more": "Plus",
|
"button.more": "Plus",
|
||||||
@ -54,6 +55,7 @@
|
|||||||
"message.get-tracking-code": "Obtenez le code de suivi",
|
"message.get-tracking-code": "Obtenez le code de suivi",
|
||||||
"message.go-to-settings": "Aller aux paramètres",
|
"message.go-to-settings": "Aller aux paramètres",
|
||||||
"message.incorrect-username-password": "nom d'utilisateurs/mot de passe incorrect.",
|
"message.incorrect-username-password": "nom d'utilisateurs/mot de passe incorrect.",
|
||||||
|
"message.new-version-available": "A new version of umami {version} is available!",
|
||||||
"message.no-data-available": "Pas de données disponibles.",
|
"message.no-data-available": "Pas de données disponibles.",
|
||||||
"message.no-websites-configured": "Vous n'avez configuré aucun site Web.",
|
"message.no-websites-configured": "Vous n'avez configuré aucun site Web.",
|
||||||
"message.page-not-found": "Page non trouvée.",
|
"message.page-not-found": "Page non trouvée.",
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"button.copy-to-clipboard": "クリップボードにコピー",
|
"button.copy-to-clipboard": "クリップボードにコピー",
|
||||||
"button.date-range": "期間",
|
"button.date-range": "期間",
|
||||||
"button.delete": "削除",
|
"button.delete": "削除",
|
||||||
|
"button.dismiss": "Dismiss",
|
||||||
"button.edit": "編集",
|
"button.edit": "編集",
|
||||||
"button.login": "ログイン",
|
"button.login": "ログイン",
|
||||||
"button.more": "さらに表示",
|
"button.more": "さらに表示",
|
||||||
@ -54,6 +55,7 @@
|
|||||||
"message.get-tracking-code": "トラッキングコードを取得",
|
"message.get-tracking-code": "トラッキングコードを取得",
|
||||||
"message.go-to-settings": "設定する",
|
"message.go-to-settings": "設定する",
|
||||||
"message.incorrect-username-password": "ユーザー名/パスワードが正しくありません。",
|
"message.incorrect-username-password": "ユーザー名/パスワードが正しくありません。",
|
||||||
|
"message.new-version-available": "A new version of umami {version} is available!",
|
||||||
"message.no-data-available": "データがありません。",
|
"message.no-data-available": "データがありません。",
|
||||||
"message.no-websites-configured": "Webサイトが設定されていません。",
|
"message.no-websites-configured": "Webサイトが設定されていません。",
|
||||||
"message.page-not-found": "ページが見つかりません。",
|
"message.page-not-found": "ページが見つかりません。",
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"button.copy-to-clipboard": "Хуулах",
|
"button.copy-to-clipboard": "Хуулах",
|
||||||
"button.date-range": "Хугацааны мужид",
|
"button.date-range": "Хугацааны мужид",
|
||||||
"button.delete": "Устгах",
|
"button.delete": "Устгах",
|
||||||
|
"button.dismiss": "Dismiss",
|
||||||
"button.edit": "Засах",
|
"button.edit": "Засах",
|
||||||
"button.login": "Нэвтрэх",
|
"button.login": "Нэвтрэх",
|
||||||
"button.more": "Цааш",
|
"button.more": "Цааш",
|
||||||
@ -54,6 +55,7 @@
|
|||||||
"message.get-tracking-code": "Мөрдөх код авах",
|
"message.get-tracking-code": "Мөрдөх код авах",
|
||||||
"message.go-to-settings": "Тохиргоо руу очих",
|
"message.go-to-settings": "Тохиргоо руу очих",
|
||||||
"message.incorrect-username-password": "Буруу хэрэглэгчийн нэр/нууц үг.",
|
"message.incorrect-username-password": "Буруу хэрэглэгчийн нэр/нууц үг.",
|
||||||
|
"message.new-version-available": "A new version of umami {version} is available!",
|
||||||
"message.no-data-available": "Өгөгдөл алга.",
|
"message.no-data-available": "Өгөгдөл алга.",
|
||||||
"message.no-websites-configured": "Та ямар нэгэн веб тохируулаагүй байна.",
|
"message.no-websites-configured": "Та ямар нэгэн веб тохируулаагүй байна.",
|
||||||
"message.page-not-found": "Хуудас олдсонгүй.",
|
"message.page-not-found": "Хуудас олдсонгүй.",
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"button.copy-to-clipboard": "Kopiëer naar klembord",
|
"button.copy-to-clipboard": "Kopiëer naar klembord",
|
||||||
"button.date-range": "Datumbereik",
|
"button.date-range": "Datumbereik",
|
||||||
"button.delete": "Verwijderen",
|
"button.delete": "Verwijderen",
|
||||||
|
"button.dismiss": "Dismiss",
|
||||||
"button.edit": "Bewerken",
|
"button.edit": "Bewerken",
|
||||||
"button.login": "Inloggen",
|
"button.login": "Inloggen",
|
||||||
"button.more": "Toon meer",
|
"button.more": "Toon meer",
|
||||||
@ -54,6 +55,7 @@
|
|||||||
"message.get-tracking-code": "Tracking code",
|
"message.get-tracking-code": "Tracking code",
|
||||||
"message.go-to-settings": "Naar instellingen",
|
"message.go-to-settings": "Naar instellingen",
|
||||||
"message.incorrect-username-password": "Incorrecte gebruikersnaam/wachtwoord.",
|
"message.incorrect-username-password": "Incorrecte gebruikersnaam/wachtwoord.",
|
||||||
|
"message.new-version-available": "A new version of umami {version} is available!",
|
||||||
"message.no-data-available": "Geen gegevens beschikbaar.",
|
"message.no-data-available": "Geen gegevens beschikbaar.",
|
||||||
"message.no-websites-configured": "Je hebt geen websites ingesteld.",
|
"message.no-websites-configured": "Je hebt geen websites ingesteld.",
|
||||||
"message.page-not-found": "Pagina niet gevonden.",
|
"message.page-not-found": "Pagina niet gevonden.",
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"button.copy-to-clipboard": "Скопировать в буфер обмена",
|
"button.copy-to-clipboard": "Скопировать в буфер обмена",
|
||||||
"button.date-range": "Диапазон дат",
|
"button.date-range": "Диапазон дат",
|
||||||
"button.delete": "Удалить",
|
"button.delete": "Удалить",
|
||||||
|
"button.dismiss": "Dismiss",
|
||||||
"button.edit": "Редактировать",
|
"button.edit": "Редактировать",
|
||||||
"button.login": "Войти",
|
"button.login": "Войти",
|
||||||
"button.more": "Больше",
|
"button.more": "Больше",
|
||||||
@ -54,6 +55,7 @@
|
|||||||
"message.get-tracking-code": "Получить код отслеживания",
|
"message.get-tracking-code": "Получить код отслеживания",
|
||||||
"message.go-to-settings": "Перейти к настройкам",
|
"message.go-to-settings": "Перейти к настройкам",
|
||||||
"message.incorrect-username-password": "Неверное имя пользователя/пароль.",
|
"message.incorrect-username-password": "Неверное имя пользователя/пароль.",
|
||||||
|
"message.new-version-available": "A new version of umami {version} is available!",
|
||||||
"message.no-data-available": "Нет данных.",
|
"message.no-data-available": "Нет данных.",
|
||||||
"message.no-websites-configured": "У вас нет настроенных сайтов.",
|
"message.no-websites-configured": "У вас нет настроенных сайтов.",
|
||||||
"message.page-not-found": "Страница не найдена.",
|
"message.page-not-found": "Страница не найдена.",
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"button.copy-to-clipboard": "Kopiera till urklipp",
|
"button.copy-to-clipboard": "Kopiera till urklipp",
|
||||||
"button.date-range": "Datumomfång",
|
"button.date-range": "Datumomfång",
|
||||||
"button.delete": "Radera",
|
"button.delete": "Radera",
|
||||||
|
"button.dismiss": "Dismiss",
|
||||||
"button.edit": "Redigera",
|
"button.edit": "Redigera",
|
||||||
"button.login": "Logga in",
|
"button.login": "Logga in",
|
||||||
"button.more": "Mer",
|
"button.more": "Mer",
|
||||||
@ -54,6 +55,7 @@
|
|||||||
"message.get-tracking-code": "Visa spårningskod",
|
"message.get-tracking-code": "Visa spårningskod",
|
||||||
"message.go-to-settings": "Gå till inställningar",
|
"message.go-to-settings": "Gå till inställningar",
|
||||||
"message.incorrect-username-password": "Felaktikt användarnamn/lösenord.",
|
"message.incorrect-username-password": "Felaktikt användarnamn/lösenord.",
|
||||||
|
"message.new-version-available": "A new version of umami {version} is available!",
|
||||||
"message.no-data-available": "Ingen data tillgänglig.",
|
"message.no-data-available": "Ingen data tillgänglig.",
|
||||||
"message.no-websites-configured": "Du har inga webbsajter.",
|
"message.no-websites-configured": "Du har inga webbsajter.",
|
||||||
"message.page-not-found": "Sidan kan inte hittas.",
|
"message.page-not-found": "Sidan kan inte hittas.",
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"button.copy-to-clipboard": "Panoya kopyala",
|
"button.copy-to-clipboard": "Panoya kopyala",
|
||||||
"button.date-range": "Tarih aralığı",
|
"button.date-range": "Tarih aralığı",
|
||||||
"button.delete": "Sil",
|
"button.delete": "Sil",
|
||||||
|
"button.dismiss": "Dismiss",
|
||||||
"button.edit": "Düzenle",
|
"button.edit": "Düzenle",
|
||||||
"button.login": "Giriş Yap",
|
"button.login": "Giriş Yap",
|
||||||
"button.more": "Detaylı göster",
|
"button.more": "Detaylı göster",
|
||||||
@ -54,6 +55,7 @@
|
|||||||
"message.get-tracking-code": "İzleme kodunu al",
|
"message.get-tracking-code": "İzleme kodunu al",
|
||||||
"message.go-to-settings": "Ayarlara git",
|
"message.go-to-settings": "Ayarlara git",
|
||||||
"message.incorrect-username-password": "Hatalı kullanıcı adı ya da parola.",
|
"message.incorrect-username-password": "Hatalı kullanıcı adı ya da parola.",
|
||||||
|
"message.new-version-available": "A new version of umami {version} is available!",
|
||||||
"message.no-data-available": "Henüz hiç veri yok.",
|
"message.no-data-available": "Henüz hiç veri yok.",
|
||||||
"message.no-websites-configured": "Henüz hiç web sitesi tanımlamadınız",
|
"message.no-websites-configured": "Henüz hiç web sitesi tanımlamadınız",
|
||||||
"message.page-not-found": "Sayfa bulunamadı.",
|
"message.page-not-found": "Sayfa bulunamadı.",
|
||||||
@ -92,4 +94,4 @@
|
|||||||
"title.edit-website": "Web sitesini düzenle",
|
"title.edit-website": "Web sitesini düzenle",
|
||||||
"title.share-url": "Paylaşım adresi",
|
"title.share-url": "Paylaşım adresi",
|
||||||
"title.tracking-code": "İzleme kodu"
|
"title.tracking-code": "İzleme kodu"
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"button.copy-to-clipboard": "复制",
|
"button.copy-to-clipboard": "复制",
|
||||||
"button.date-range": "多日",
|
"button.date-range": "多日",
|
||||||
"button.delete": "删除",
|
"button.delete": "删除",
|
||||||
|
"button.dismiss": "Dismiss",
|
||||||
"button.edit": "编辑",
|
"button.edit": "编辑",
|
||||||
"button.login": "登录",
|
"button.login": "登录",
|
||||||
"button.more": "更多",
|
"button.more": "更多",
|
||||||
@ -54,6 +55,7 @@
|
|||||||
"message.get-tracking-code": "获得跟踪代码",
|
"message.get-tracking-code": "获得跟踪代码",
|
||||||
"message.go-to-settings": "去设置",
|
"message.go-to-settings": "去设置",
|
||||||
"message.incorrect-username-password": "用户名密码不正确.",
|
"message.incorrect-username-password": "用户名密码不正确.",
|
||||||
|
"message.new-version-available": "A new version of umami {version} is available!",
|
||||||
"message.no-data-available": "无可用数据.",
|
"message.no-data-available": "无可用数据.",
|
||||||
"message.no-websites-configured": "你还没有设置任何网站.",
|
"message.no-websites-configured": "你还没有设置任何网站.",
|
||||||
"message.page-not-found": "网页未找到.",
|
"message.page-not-found": "网页未找到.",
|
||||||
|
@ -3,6 +3,7 @@ export const LOCALE_CONFIG = 'umami.locale';
|
|||||||
export const TIMEZONE_CONFIG = 'umami.timezone';
|
export const TIMEZONE_CONFIG = 'umami.timezone';
|
||||||
export const DATE_RANGE_CONFIG = 'umami.date-range';
|
export const DATE_RANGE_CONFIG = 'umami.date-range';
|
||||||
export const THEME_CONFIG = 'umami.theme';
|
export const THEME_CONFIG = 'umami.theme';
|
||||||
|
export const VERSION_CHECK = 'umami.version-check';
|
||||||
|
|
||||||
export const THEME_COLORS = {
|
export const THEME_COLORS = {
|
||||||
light: {
|
light: {
|
||||||
|
@ -16,9 +16,13 @@ export function getDatabase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function runQuery(query) {
|
export async function runQuery(query) {
|
||||||
return query.catch(e => {
|
return query
|
||||||
throw e;
|
.catch(e => {
|
||||||
});
|
throw e;
|
||||||
|
})
|
||||||
|
.finally(async () => {
|
||||||
|
await prisma.$disconnect();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function rawQuery(query, params) {
|
export async function rawQuery(query, params) {
|
||||||
@ -285,8 +289,9 @@ export async function createAccount(data) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMetrics(website_id, start_at, end_at, url) {
|
export function getMetrics(website_id, start_at, end_at, filters = {}) {
|
||||||
const params = [website_id, start_at, end_at];
|
const params = [website_id, start_at, end_at];
|
||||||
|
const { url } = filters;
|
||||||
let urlFilter = '';
|
let urlFilter = '';
|
||||||
|
|
||||||
if (url) {
|
if (url) {
|
||||||
@ -348,8 +353,10 @@ export function getPageviews(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSessionMetrics(website_id, start_at, end_at, field, url) {
|
export function getSessionMetrics(website_id, start_at, end_at, field, filters = {}) {
|
||||||
const params = [website_id, start_at, end_at];
|
const params = [website_id, start_at, end_at];
|
||||||
|
const { url } = filters;
|
||||||
|
|
||||||
let urlFilter = '';
|
let urlFilter = '';
|
||||||
|
|
||||||
if (url) {
|
if (url) {
|
||||||
@ -375,13 +382,15 @@ export function getSessionMetrics(website_id, start_at, end_at, field, url) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPageviewMetrics(website_id, start_at, end_at, field, table, domain, url) {
|
export function getPageviewMetrics(website_id, start_at, end_at, field, table, filters = {}) {
|
||||||
const params = [website_id, start_at, end_at];
|
const params = [website_id, start_at, end_at];
|
||||||
|
const { domain, url } = filters;
|
||||||
|
|
||||||
let domainFilter = '';
|
let domainFilter = '';
|
||||||
let urlFilter = '';
|
let urlFilter = '';
|
||||||
|
|
||||||
if (domain) {
|
if (domain) {
|
||||||
domainFilter = `and referrer not like $${params.length + 1}`;
|
domainFilter = `and referrer not like $${params.length + 1} and referrer not like '/%'`;
|
||||||
params.push(`%${domain}%`);
|
params.push(`%${domain}%`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -420,8 +429,17 @@ export function getActiveVisitors(website_id) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getEvents(website_id, start_at, end_at, timezone = 'utc', unit = 'day', url) {
|
export function getEvents(
|
||||||
|
website_id,
|
||||||
|
start_at,
|
||||||
|
end_at,
|
||||||
|
timezone = 'utc',
|
||||||
|
unit = 'day',
|
||||||
|
filters = {},
|
||||||
|
) {
|
||||||
const params = [website_id, start_at, end_at];
|
const params = [website_id, start_at, end_at];
|
||||||
|
const { url } = filters;
|
||||||
|
|
||||||
let urlFilter = '';
|
let urlFilter = '';
|
||||||
|
|
||||||
if (url) {
|
if (url) {
|
||||||
|
13
package.json
13
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "umami",
|
"name": "umami",
|
||||||
"version": "0.58.0",
|
"version": "0.62.0",
|
||||||
"description": "A simple, fast, website analytics alternative to Google Analytics. ",
|
"description": "A simple, fast, website analytics alternative to Google Analytics. ",
|
||||||
"author": "Mike Cao <mike@mikecao.com>",
|
"author": "Mike Cao <mike@mikecao.com>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -50,7 +50,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "2.7.1",
|
"@prisma/client": "2.8.0",
|
||||||
"@reduxjs/toolkit": "^1.4.0",
|
"@reduxjs/toolkit": "^1.4.0",
|
||||||
"bcrypt": "^5.0.0",
|
"bcrypt": "^5.0.0",
|
||||||
"chalk": "^4.1.0",
|
"chalk": "^4.1.0",
|
||||||
@ -61,12 +61,12 @@
|
|||||||
"date-fns": "^2.16.1",
|
"date-fns": "^2.16.1",
|
||||||
"date-fns-tz": "^1.0.10",
|
"date-fns-tz": "^1.0.10",
|
||||||
"detect-browser": "^5.1.1",
|
"detect-browser": "^5.1.1",
|
||||||
"formik": "^2.1.5",
|
"formik": "^2.1.6",
|
||||||
"immer": "^7.0.9",
|
"immer": "^7.0.9",
|
||||||
"is-localhost-ip": "^1.4.0",
|
"is-localhost-ip": "^1.4.0",
|
||||||
"isbot-fast": "^1.2.0",
|
"isbot-fast": "^1.2.0",
|
||||||
"jose": "^2.0.2",
|
"jose": "^2.0.2",
|
||||||
"maxmind": "^4.1.4",
|
"maxmind": "^4.2.0",
|
||||||
"moment-timezone": "^0.5.31",
|
"moment-timezone": "^0.5.31",
|
||||||
"next": "^9.5.3",
|
"next": "^9.5.3",
|
||||||
"react": "^16.13.1",
|
"react": "^16.13.1",
|
||||||
@ -80,6 +80,7 @@
|
|||||||
"redux": "^4.0.5",
|
"redux": "^4.0.5",
|
||||||
"redux-thunk": "^2.3.0",
|
"redux-thunk": "^2.3.0",
|
||||||
"request-ip": "^2.1.3",
|
"request-ip": "^2.1.3",
|
||||||
|
"semver": "^7.3.2",
|
||||||
"thenby": "^1.3.4",
|
"thenby": "^1.3.4",
|
||||||
"timezone-support": "^2.0.2",
|
"timezone-support": "^2.0.2",
|
||||||
"tinycolor2": "^1.4.2",
|
"tinycolor2": "^1.4.2",
|
||||||
@ -87,7 +88,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@formatjs/cli": "^2.12.0",
|
"@formatjs/cli": "^2.12.0",
|
||||||
"@prisma/cli": "2.7.1",
|
"@prisma/cli": "2.8.0",
|
||||||
"@rollup/plugin-buble": "^0.21.3",
|
"@rollup/plugin-buble": "^0.21.3",
|
||||||
"@rollup/plugin-node-resolve": "^9.0.0",
|
"@rollup/plugin-node-resolve": "^9.0.0",
|
||||||
"@rollup/plugin-replace": "^2.3.3",
|
"@rollup/plugin-replace": "^2.3.3",
|
||||||
@ -113,7 +114,7 @@
|
|||||||
"rollup": "^2.28.2",
|
"rollup": "^2.28.2",
|
||||||
"rollup-plugin-hashbang": "^2.2.2",
|
"rollup-plugin-hashbang": "^2.2.2",
|
||||||
"rollup-plugin-terser": "^7.0.2",
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
"stylelint": "^13.7.1",
|
"stylelint": "^13.7.2",
|
||||||
"stylelint-config-css-modules": "^2.2.0",
|
"stylelint-config-css-modules": "^2.2.0",
|
||||||
"stylelint-config-prettier": "^8.0.1",
|
"stylelint-config-prettier": "^8.0.1",
|
||||||
"stylelint-config-recommended": "^3.0.0",
|
"stylelint-config-recommended": "^3.0.0",
|
||||||
|
@ -21,7 +21,7 @@ export default async (req, res) => {
|
|||||||
const startDate = new Date(+start_at);
|
const startDate = new Date(+start_at);
|
||||||
const endDate = new Date(+end_at);
|
const endDate = new Date(+end_at);
|
||||||
|
|
||||||
const events = await getEvents(websiteId, startDate, endDate, tz, unit, url);
|
const events = await getEvents(websiteId, startDate, endDate, tz, unit, { url });
|
||||||
|
|
||||||
return ok(res, events);
|
return ok(res, events);
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ export default async (req, res) => {
|
|||||||
const startDate = new Date(+start_at);
|
const startDate = new Date(+start_at);
|
||||||
const endDate = new Date(+end_at);
|
const endDate = new Date(+end_at);
|
||||||
|
|
||||||
const metrics = await getMetrics(websiteId, startDate, endDate, url);
|
const metrics = await getMetrics(websiteId, startDate, endDate, { url });
|
||||||
|
|
||||||
const stats = Object.keys(metrics[0]).reduce((obj, key) => {
|
const stats = Object.keys(metrics[0]).reduce((obj, key) => {
|
||||||
obj[key] = Number(metrics[0][key]) || 0;
|
obj[key] = Number(metrics[0][key]) || 0;
|
||||||
|
@ -42,7 +42,7 @@ export default async (req, res) => {
|
|||||||
const endDate = new Date(+end_at);
|
const endDate = new Date(+end_at);
|
||||||
|
|
||||||
if (sessionColumns.includes(type)) {
|
if (sessionColumns.includes(type)) {
|
||||||
const data = await getSessionMetrics(websiteId, startDate, endDate, type, url);
|
const data = await getSessionMetrics(websiteId, startDate, endDate, type, { url });
|
||||||
|
|
||||||
return ok(res, data);
|
return ok(res, data);
|
||||||
}
|
}
|
||||||
@ -54,8 +54,10 @@ export default async (req, res) => {
|
|||||||
endDate,
|
endDate,
|
||||||
getColumn(type),
|
getColumn(type),
|
||||||
getTable(type),
|
getTable(type),
|
||||||
domain,
|
{
|
||||||
type !== 'url' ? url : undefined,
|
domain,
|
||||||
|
url: type !== 'url' && url,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return ok(res, data);
|
return ok(res, data);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import Layout from 'components/layout/Layout';
|
import Layout from 'components/layout/Layout';
|
||||||
import WebsiteList from 'components/WebsiteList';
|
import WebsiteList from 'components/pages/WebsiteList';
|
||||||
import useRequireLogin from 'hooks/useRequireLogin';
|
import useRequireLogin from 'hooks/useRequireLogin';
|
||||||
|
|
||||||
export default function DashboardPage() {
|
export default function DashboardPage() {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Layout from 'components/layout/Layout';
|
import Layout from 'components/layout/Layout';
|
||||||
import Settings from 'components/settings/Settings';
|
import Settings from 'components/pages/Settings';
|
||||||
import useRequireLogin from 'hooks/useRequireLogin';
|
import useRequireLogin from 'hooks/useRequireLogin';
|
||||||
|
|
||||||
export default function SettingsPage() {
|
export default function SettingsPage() {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import Layout from 'components/layout/Layout';
|
import Layout from 'components/layout/Layout';
|
||||||
import WebsiteDetails from 'components/WebsiteDetails';
|
import WebsiteDetails from 'components/pages/WebsiteDetails';
|
||||||
import useFetch from 'hooks/useFetch';
|
import useFetch from 'hooks/useFetch';
|
||||||
|
|
||||||
export default function SharePage() {
|
export default function SharePage() {
|
||||||
|
@ -1,56 +1,18 @@
|
|||||||
import Head from 'next/head';
|
import React from 'react';
|
||||||
import Link from 'next/link';
|
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import Layout from 'components/layout/Layout';
|
import Layout from 'components/layout/Layout';
|
||||||
|
import TestConsole from 'components/pages/TestConsole';
|
||||||
|
import useRequireLogin from 'hooks/useRequireLogin';
|
||||||
|
|
||||||
export default function Test() {
|
export default function TestPage() {
|
||||||
const router = useRouter();
|
const { loading } = useRequireLogin();
|
||||||
const { id } = router.query;
|
|
||||||
|
|
||||||
if (!id) {
|
if (loading) {
|
||||||
return <h1>No id query specified.</h1>;
|
return null;
|
||||||
}
|
|
||||||
|
|
||||||
function handleClick() {
|
|
||||||
window.umami('Custom event');
|
|
||||||
window.umami.pageView('/fake', 'https://www.google.com');
|
|
||||||
window.umami.pageEvent('pageEvent', 'custom-type');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Layout>
|
||||||
<Head>
|
<TestConsole />
|
||||||
{typeof window !== 'undefined' && (
|
</Layout>
|
||||||
<script async defer data-website-id={id} src="/umami.js" />
|
|
||||||
)}
|
|
||||||
</Head>
|
|
||||||
<Layout>
|
|
||||||
<p>
|
|
||||||
Here you can test if your umami installation works. Open the network tab in your browser
|
|
||||||
developer console and watch for requests to the url <b>collect</b>. The links below should
|
|
||||||
trigger page views. Clicking on the button should trigger an event.
|
|
||||||
</p>
|
|
||||||
<h2>Page links</h2>
|
|
||||||
<Link href={`?id=${id}&q=1`}>
|
|
||||||
<a>Page One</a>
|
|
||||||
</Link>
|
|
||||||
<br />
|
|
||||||
<Link href={`?id=${id}&q=2`}>
|
|
||||||
<a>Page Two</a>
|
|
||||||
</Link>
|
|
||||||
<h2>Events</h2>
|
|
||||||
<button
|
|
||||||
id="primary-button"
|
|
||||||
className="otherClass umami--click--primary-button align-self-start"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
Button
|
|
||||||
</button>
|
|
||||||
<h2>Manual trigger</h2>
|
|
||||||
<button id="manual-button" type="button" onClick={handleClick}>
|
|
||||||
Button
|
|
||||||
</button>
|
|
||||||
</Layout>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import Layout from 'components/layout/Layout';
|
import Layout from 'components/layout/Layout';
|
||||||
import WebsiteDetails from 'components/WebsiteDetails';
|
import WebsiteDetails from 'components/pages/WebsiteDetails';
|
||||||
import useRequireLogin from 'hooks/useRequireLogin';
|
import useRequireLogin from 'hooks/useRequireLogin';
|
||||||
|
|
||||||
export default function DetailsPage() {
|
export default function DetailsPage() {
|
||||||
|
@ -7,6 +7,10 @@ const app = createSlice({
|
|||||||
initialState: {
|
initialState: {
|
||||||
locale: getItem(LOCALE_CONFIG) || 'en-US',
|
locale: getItem(LOCALE_CONFIG) || 'en-US',
|
||||||
theme: getItem(THEME_CONFIG) || 'light',
|
theme: getItem(THEME_CONFIG) || 'light',
|
||||||
|
versions: {
|
||||||
|
current: process.env.VERSION,
|
||||||
|
latest: null,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
reducers: {
|
reducers: {
|
||||||
setLocale(state, action) {
|
setLocale(state, action) {
|
||||||
@ -17,9 +21,51 @@ const app = createSlice({
|
|||||||
state.theme = action.payload;
|
state.theme = action.payload;
|
||||||
return state;
|
return state;
|
||||||
},
|
},
|
||||||
|
setVersions(state, action) {
|
||||||
|
state.versions = action.payload;
|
||||||
|
return state;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const { setLocale, setTheme } = app.actions;
|
export const { setLocale, setTheme, setVersions } = app.actions;
|
||||||
|
|
||||||
export default app.reducer;
|
export default app.reducer;
|
||||||
|
|
||||||
|
export function checkVersion() {
|
||||||
|
return async (dispatch, getState) => {
|
||||||
|
const {
|
||||||
|
app: {
|
||||||
|
versions: { current },
|
||||||
|
},
|
||||||
|
} = getState();
|
||||||
|
|
||||||
|
const data = await fetch('https://api.github.com/repos/mikecao/umami/releases/latest', {
|
||||||
|
method: 'get',
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/vnd.github.v3+json',
|
||||||
|
},
|
||||||
|
}).then(res => {
|
||||||
|
if (res.ok) {
|
||||||
|
return res.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { tag_name } = data;
|
||||||
|
|
||||||
|
const latest = tag_name.startsWith('v') ? tag_name.slice(1) : tag_name;
|
||||||
|
|
||||||
|
return dispatch(
|
||||||
|
setVersions({
|
||||||
|
current,
|
||||||
|
latest,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
40
yarn.lock
40
yarn.lock
@ -1193,15 +1193,15 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@panva/asn1.js/-/asn1.js-1.0.0.tgz#dd55ae7b8129e02049f009408b97c61ccf9032f6"
|
resolved "https://registry.yarnpkg.com/@panva/asn1.js/-/asn1.js-1.0.0.tgz#dd55ae7b8129e02049f009408b97c61ccf9032f6"
|
||||||
integrity sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==
|
integrity sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==
|
||||||
|
|
||||||
"@prisma/cli@2.7.1":
|
"@prisma/cli@2.8.0":
|
||||||
version "2.7.1"
|
version "2.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/@prisma/cli/-/cli-2.7.1.tgz#98f2cb434bb931341e6c6292c7bab601e5f842f8"
|
resolved "https://registry.yarnpkg.com/@prisma/cli/-/cli-2.8.0.tgz#919d7f66023affa76d14823212b62a8512cfd37d"
|
||||||
integrity sha512-0uA+gWkNQ35DveVHDPltiTCTr4wcXtEhnPs463IEM+Xn8dTv9x0gtZiYHSuQM3t7uwlOxj1rurBsqSbiljynfQ==
|
integrity sha512-Kg1C47d75jdEIMmJif8TMlv/2Ihx08E1qWp0euwoZhjd807HGnjgC9tJYjTfkdf+NMJSAUbvoPXKInEX0HoOMw==
|
||||||
|
|
||||||
"@prisma/client@2.7.1":
|
"@prisma/client@2.8.0":
|
||||||
version "2.7.1"
|
version "2.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-2.7.1.tgz#0a37ddff7fe80ae3a86dfa620c1141c8607be6c2"
|
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-2.8.0.tgz#a0f7247786c9b6ee804437acf8215854c5eb3946"
|
||||||
integrity sha512-IEWDCuvIaQTira8/jAyf+uY+AuPPUFDIXMSN4zEA/gvoJv2woq7RmkaubS+NQVgDbbyOR6F3UcXLiFTYQDzZkQ==
|
integrity sha512-5+GzRTkPnmv4OEV2tB8kwQt/xLLxBR/daJBcMt6pnnonJvrREsu0tSTdz2LJNPaj3kTT0fSS/OaeGMMdfVYSpw==
|
||||||
dependencies:
|
dependencies:
|
||||||
pkg-up "^3.1.0"
|
pkg-up "^3.1.0"
|
||||||
|
|
||||||
@ -4145,10 +4145,10 @@ for-own@^0.1.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
for-in "^1.0.1"
|
for-in "^1.0.1"
|
||||||
|
|
||||||
formik@^2.1.5:
|
formik@^2.1.6:
|
||||||
version "2.1.5"
|
version "2.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/formik/-/formik-2.1.5.tgz#de5bbbe35543fa6d049fe96b8ee329d6cd6892b8"
|
resolved "https://registry.yarnpkg.com/formik/-/formik-2.1.6.tgz#f723bfccb2c7abec886aa6a4930b360d20f1a0b3"
|
||||||
integrity sha512-bWpo3PiqVDYslvrRjTq0Isrm0mFXHiO33D8MS6t6dWcqSFGeYF52nlpCM2xwOJ6tRVRznDkL+zz/iHPL4LDuvQ==
|
integrity sha512-m9DcxlZw/58p4xuhH3dzUzQWaC4dig0RKX7yNQOJt4VRhXn7p+YRrs3o17r3YwzvOLua3zC53VMbfupLsDwO5w==
|
||||||
dependencies:
|
dependencies:
|
||||||
deepmerge "^2.1.1"
|
deepmerge "^2.1.1"
|
||||||
hoist-non-react-statics "^3.3.0"
|
hoist-non-react-statics "^3.3.0"
|
||||||
@ -5493,10 +5493,10 @@ mathml-tag-names@^2.1.3:
|
|||||||
resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3"
|
resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3"
|
||||||
integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==
|
integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==
|
||||||
|
|
||||||
maxmind@^4.1.4:
|
maxmind@^4.2.0:
|
||||||
version "4.1.4"
|
version "4.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/maxmind/-/maxmind-4.1.4.tgz#14fa0cf9a88f15b708edfd1378c5e49e0f0105c1"
|
resolved "https://registry.yarnpkg.com/maxmind/-/maxmind-4.2.0.tgz#912e5ec4a961807d20d7fb541160aeb5ea802c1c"
|
||||||
integrity sha512-DfcZPpc0XJVF1yypRpVqMs9JiSYYShVkfexSjwTbfqZMCEoQlCU83ooX9cRmWMUaLqm4zffi7HtZg7XqcJaL1A==
|
integrity sha512-TADiE11Q10IjvLtlo05tTD52xLqfCJMhE3eYJHmpYIKg668STi/fQZGH9X3FpqpIP/2WPgKFxf899awFvfMtQA==
|
||||||
dependencies:
|
dependencies:
|
||||||
tiny-lru "7.0.6"
|
tiny-lru "7.0.6"
|
||||||
|
|
||||||
@ -8347,10 +8347,10 @@ stylelint-config-recommended@^3.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-3.0.0.tgz#e0e547434016c5539fe2650afd58049a2fd1d657"
|
resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-3.0.0.tgz#e0e547434016c5539fe2650afd58049a2fd1d657"
|
||||||
integrity sha512-F6yTRuc06xr1h5Qw/ykb2LuFynJ2IxkKfCMf+1xqPffkxh0S09Zc902XCffcsw/XMFq/OzQ1w54fLIDtmRNHnQ==
|
integrity sha512-F6yTRuc06xr1h5Qw/ykb2LuFynJ2IxkKfCMf+1xqPffkxh0S09Zc902XCffcsw/XMFq/OzQ1w54fLIDtmRNHnQ==
|
||||||
|
|
||||||
stylelint@^13.7.1:
|
stylelint@^13.7.2:
|
||||||
version "13.7.1"
|
version "13.7.2"
|
||||||
resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-13.7.1.tgz#bee97ee78d778a3f1dbe3f7397b76414973e263e"
|
resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-13.7.2.tgz#6f3c58eea4077680ed0ceb0d064b22b100970486"
|
||||||
integrity sha512-qzqazcyRxrSRdmFuO0/SZOJ+LyCxYy0pwcvaOBBnl8/2VfHSMrtNIE+AnyJoyq6uKb+mt+hlgmVrvVi6G6XHfQ==
|
integrity sha512-mmieorkfmO+ZA6CNDu1ic9qpt4tFvH2QUB7vqXgrMVHe5ENU69q7YDq0YUg/UHLuCsZOWhUAvcMcLzLDIERzSg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@stylelint/postcss-css-in-js" "^0.37.2"
|
"@stylelint/postcss-css-in-js" "^0.37.2"
|
||||||
"@stylelint/postcss-markdown" "^0.36.1"
|
"@stylelint/postcss-markdown" "^0.36.1"
|
||||||
|
Loading…
Reference in New Issue
Block a user