diff --git a/assets/external-link.svg b/assets/external-link.svg new file mode 100644 index 00000000..ed09306f --- /dev/null +++ b/assets/external-link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/components/common/LanguageButton.js b/components/common/LanguageButton.js index 6fe7eb83..fae90415 100644 --- a/components/common/LanguageButton.js +++ b/components/common/LanguageButton.js @@ -3,7 +3,6 @@ import Head from 'next/head'; import Menu from './Menu'; import Button from './Button'; import { menuOptions } from 'lib/lang'; -import { setItem } from 'lib/web'; import useLocale from 'hooks/useLocale'; import useDocumentClick from 'hooks/useDocumentClick'; import Globe from 'assets/globe.svg'; @@ -17,7 +16,6 @@ export default function LanguageButton({ menuPosition = 'bottom', menuAlign = 'l function handleSelect(value) { setLocale(value); - setItem('umami.locale', value); setShowMenu(false); } diff --git a/components/common/RefreshButton.js b/components/common/RefreshButton.js index 1fa02f48..af754a9c 100644 --- a/components/common/RefreshButton.js +++ b/components/common/RefreshButton.js @@ -10,7 +10,7 @@ import { getDateRange } from '../../lib/date'; export default function RefreshButton({ websiteId }) { const dispatch = useDispatch(); - const dateRange = useDateRange(websiteId); + const [dateRange] = useDateRange(websiteId); const [loading, setLoading] = useState(false); const completed = useSelector(state => state.queries[`/api/website/${websiteId}/metrics`]); diff --git a/components/metrics/EventsChart.js b/components/metrics/EventsChart.js index 4a49d129..48fb6f2b 100644 --- a/components/metrics/EventsChart.js +++ b/components/metrics/EventsChart.js @@ -1,9 +1,10 @@ import React, { useMemo } from 'react'; import tinycolor from 'tinycolor2'; import BarChart from './BarChart'; -import { getTimezone, getDateArray, getDateLength } from 'lib/date'; +import { getDateArray, getDateLength } from 'lib/date'; import useFetch from 'hooks/useFetch'; import useDateRange from 'hooks/useDateRange'; +import useTimezone from 'hooks/useTimezone'; const COLORS = [ '#2680eb', @@ -17,15 +18,17 @@ const COLORS = [ ]; export default function EventsChart({ websiteId, token }) { - const dateRange = useDateRange(websiteId); + const [dateRange] = useDateRange(websiteId); const { startDate, endDate, unit, modified } = dateRange; + const [timezone] = useTimezone(); + const { data } = useFetch( `/api/website/${websiteId}/events`, { start_at: +startDate, end_at: +endDate, unit, - tz: getTimezone(), + tz: timezone, token, }, { update: [modified] }, diff --git a/components/metrics/MetricsBar.js b/components/metrics/MetricsBar.js index 8d128c18..cad4c00e 100644 --- a/components/metrics/MetricsBar.js +++ b/components/metrics/MetricsBar.js @@ -9,7 +9,7 @@ import MetricCard from './MetricCard'; import styles from './MetricsBar.module.css'; export default function MetricsBar({ websiteId, token, className }) { - const dateRange = useDateRange(websiteId); + const [dateRange] = useDateRange(websiteId); const { startDate, endDate, modified } = dateRange; const { data } = useFetch( `/api/website/${websiteId}/metrics`, diff --git a/components/metrics/MetricsTable.js b/components/metrics/MetricsTable.js index 560c25f0..eed5fca2 100644 --- a/components/metrics/MetricsTable.js +++ b/components/metrics/MetricsTable.js @@ -29,7 +29,7 @@ export default function MetricsTable({ onDataLoad = () => {}, onExpand = () => {}, }) { - const dateRange = useDateRange(websiteId); + const [dateRange] = useDateRange(websiteId); const { startDate, endDate, modified } = dateRange; const { data } = useFetch( `/api/website/${websiteId}/rankings`, diff --git a/components/metrics/WebsiteChart.js b/components/metrics/WebsiteChart.js index f2485888..5c6e8988 100644 --- a/components/metrics/WebsiteChart.js +++ b/components/metrics/WebsiteChart.js @@ -1,5 +1,4 @@ import React, { useMemo } from 'react'; -import { useDispatch } from 'react-redux'; import classNames from 'classnames'; import PageviewsChart from './PageviewsChart'; import MetricsBar from './MetricsBar'; @@ -8,8 +7,8 @@ import DateFilter from 'components/common/DateFilter'; import StickyHeader from 'components/helpers/StickyHeader'; import useFetch from 'hooks/useFetch'; import useDateRange from 'hooks/useDateRange'; -import { getDateArray, getDateLength, getTimezone } from 'lib/date'; -import { setDateRange } from 'redux/actions/websites'; +import useTimezone from 'hooks/useTimezone'; +import { getDateArray, getDateLength } from 'lib/date'; import styles from './WebsiteChart.module.css'; export default function WebsiteChart({ @@ -20,9 +19,9 @@ export default function WebsiteChart({ showLink = false, onDataLoad = () => {}, }) { - const dispatch = useDispatch(); - const dateRange = useDateRange(websiteId); + const [dateRange, setDateRange] = useDateRange(websiteId); const { startDate, endDate, unit, value, modified } = dateRange; + const [timezone] = useTimezone(); const { data } = useFetch( `/api/website/${websiteId}/pageviews`, @@ -30,7 +29,7 @@ export default function WebsiteChart({ start_at: +startDate, end_at: +endDate, unit, - tz: getTimezone(), + tz: timezone, token, }, { onDataLoad, update: [modified] }, @@ -46,10 +45,6 @@ export default function WebsiteChart({ return [[], []]; }, [data]); - function handleDateChange(values) { - dispatch(setDateRange(websiteId, values)); - } - return ( <> @@ -67,7 +62,7 @@ export default function WebsiteChart({ value={value} startDate={startDate} endDate={endDate} - onChange={handleDateChange} + onChange={setDateRange} /> diff --git a/components/settings/AccountSettings.js b/components/settings/AccountSettings.js index bc555a0f..2471ba21 100644 --- a/components/settings/AccountSettings.js +++ b/components/settings/AccountSettings.js @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import { FormattedMessage } from 'react-intl'; -import { useRouter } from 'next/router'; +import Link from 'next/link'; import classNames from 'classnames'; import PageHeader from 'components/layout/PageHeader'; import Button from 'components/common/Button'; @@ -16,11 +16,10 @@ import Pen from 'assets/pen.svg'; import Plus from 'assets/plus.svg'; import Trash from 'assets/trash.svg'; import Check from 'assets/check.svg'; -import List from 'assets/list-ul.svg'; +import LinkIcon from 'assets/external-link.svg'; import styles from './AccountSettings.module.css'; export default function AccountSettings() { - const router = useRouter(); const [addAccount, setAddAccount] = useState(); const [editAccount, setEditAccount] = useState(); const [deleteAccount, setDeleteAccount] = useState(); @@ -30,16 +29,18 @@ export default function AccountSettings() { const Checkmark = ({ is_admin }) => (is_admin ? } size="medium" /> : null); + const DashboardLink = row => + row.is_admin ? null : ( + + + } /> + + + ); + const Buttons = row => row.username !== 'admin' ? ( - + + ); +} diff --git a/components/settings/DateRangeSetting.module.css b/components/settings/DateRangeSetting.module.css new file mode 100644 index 00000000..230e7c97 --- /dev/null +++ b/components/settings/DateRangeSetting.module.css @@ -0,0 +1,3 @@ +.button { + margin-left: 10px; +} diff --git a/components/settings/ProfileSettings.js b/components/settings/ProfileSettings.js index 10386318..f28226c5 100644 --- a/components/settings/ProfileSettings.js +++ b/components/settings/ProfileSettings.js @@ -1,40 +1,27 @@ import React, { useState } from 'react'; import { FormattedMessage } from 'react-intl'; -import { useDispatch, useSelector } from 'react-redux'; +import { useSelector } from 'react-redux'; import PageHeader from 'components/layout/PageHeader'; import Button from 'components/common/Button'; import Modal from 'components/common/Modal'; import Toast from 'components/common/Toast'; import ChangePasswordForm from 'components/forms/ChangePasswordForm'; -import DateFilter from 'components/common/DateFilter'; +import TimezoneSetting from 'components/settings/TimezoneSetting'; import Dots from 'assets/ellipsis-h.svg'; -import { getTimezone } from 'lib/date'; -import { setItem } from 'lib/web'; -import useDateRange from 'hooks/useDateRange'; -import { setDateRange } from 'redux/actions/websites'; import styles from './ProfileSettings.module.css'; +import DateRangeSetting from './DateRangeSetting'; export default function ProfileSettings() { - const dispatch = useDispatch(); const user = useSelector(state => state.user); const [changePassword, setChangePassword] = useState(false); const [message, setMessage] = useState(); const { user_id } = user; - const timezone = getTimezone(); - const dateRange = useDateRange(0); - const { startDate, endDate, value } = dateRange; function handleSave() { setChangePassword(false); setMessage(); } - function handleDateChange(values) { - const { value } = values; - setItem(`umami.date-range`, value === 'custom' ? values : value); - dispatch(setDateRange(0, values)); - } - return ( <> @@ -47,7 +34,7 @@ export default function ProfileSettings() { -
+
@@ -55,17 +42,14 @@ export default function ProfileSettings() {
-
{timezone}
+
+ +
-
- +
+
{changePassword && ( diff --git a/components/settings/ProfileSettings.module.css b/components/settings/ProfileSettings.module.css index ea7711ac..fdd8252f 100644 --- a/components/settings/ProfileSettings.module.css +++ b/components/settings/ProfileSettings.module.css @@ -1,3 +1,3 @@ -.date { +.list dd { display: flex; } diff --git a/components/settings/TimezoneSetting.js b/components/settings/TimezoneSetting.js new file mode 100644 index 00000000..54751a45 --- /dev/null +++ b/components/settings/TimezoneSetting.js @@ -0,0 +1,31 @@ +import React from 'react'; +import { FormattedMessage } from 'react-intl'; +import { listTimeZones } from 'timezone-support'; +import DropDown from '../common/DropDown'; +import Button from '../common/Button'; +import useTimezone from 'hooks/useTimezone'; +import { getTimezone } from 'lib/date'; +import styles from './TimezoneSetting.module.css'; + +export default function TimezoneSetting() { + const [timezone, saveTimezone] = useTimezone(); + const options = listTimeZones().map(n => ({ label: n, value: n })); + + function handleReset() { + saveTimezone(getTimezone()); + } + + return ( + <> + + + + ); +} diff --git a/components/settings/TimezoneSetting.module.css b/components/settings/TimezoneSetting.module.css new file mode 100644 index 00000000..9561111d --- /dev/null +++ b/components/settings/TimezoneSetting.module.css @@ -0,0 +1,8 @@ +.menu { + max-height: 300px; + overflow-y: auto; +} + +.button { + margin-left: 10px; +} diff --git a/hooks/useDateRange.js b/hooks/useDateRange.js index 13703d9f..77f892de 100644 --- a/hooks/useDateRange.js +++ b/hooks/useDateRange.js @@ -1,24 +1,41 @@ -import { useSelector } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { parseISO } from 'date-fns'; import { getDateRange } from 'lib/date'; -import { getItem } from 'lib/web'; +import { getItem, setItem } from 'lib/web'; +import { setDateRange } from '../redux/actions/websites'; +import { DATE_RANGE_CONFIG, DEFAULT_DATE_RANGE } from 'lib/constants'; +import useForceUpdate from './useForceUpdate'; -export default function useDateRange(websiteId, defaultDateRange = '24hour') { - const globalDefault = getItem('umami.date-range'); +export default function useDateRange(websiteId, defaultDateRange = DEFAULT_DATE_RANGE) { + const dispatch = useDispatch(); + const dateRange = useSelector(state => state.websites[websiteId]?.dateRange); + const forceUpdate = useForceUpdate(); + + const globalDefault = getItem(DATE_RANGE_CONFIG); let globalDateRange; - if (typeof globalDefault === 'string') { - globalDateRange = getDateRange(globalDefault); - } else if (typeof globalDefault === 'object') { - globalDateRange = { - ...globalDefault, - startDate: parseISO(globalDefault.startDate), - endDate: parseISO(globalDefault.endDate), - }; + if (globalDefault) { + if (typeof globalDefault === 'string') { + globalDateRange = getDateRange(globalDefault); + } else if (typeof globalDefault === 'object') { + globalDateRange = { + ...globalDefault, + startDate: parseISO(globalDefault.startDate), + endDate: parseISO(globalDefault.endDate), + }; + } } - return useSelector( - state => - state.websites[websiteId]?.dateRange || globalDateRange || getDateRange(defaultDateRange), - ); + function saveDateRange(values) { + const { value } = values; + + if (websiteId) { + dispatch(setDateRange(websiteId, values)); + } else { + setItem(DATE_RANGE_CONFIG, value === 'custom' ? values : value); + forceUpdate(); + } + } + + return [dateRange || globalDateRange || getDateRange(defaultDateRange), saveDateRange]; } diff --git a/hooks/useForceUpdate.js b/hooks/useForceUpdate.js new file mode 100644 index 00000000..2b8d6101 --- /dev/null +++ b/hooks/useForceUpdate.js @@ -0,0 +1,9 @@ +import { useCallback, useState } from 'react'; + +export default function useForceUpdate() { + const [, update] = useState(Object.create(null)); + + return useCallback(() => { + update(Object.create(null)); + }, [update]); +} diff --git a/hooks/useLocale.js b/hooks/useLocale.js index 185c1f19..788b9b28 100644 --- a/hooks/useLocale.js +++ b/hooks/useLocale.js @@ -1,11 +1,14 @@ import { useDispatch, useSelector } from 'react-redux'; import { updateApp } from 'redux/actions/app'; +import { setItem } from 'lib/web'; +import { LOCALE_CONFIG } from 'lib/constants'; export default function useLocale() { const locale = useSelector(state => state.app.locale); const dispatch = useDispatch(); function setLocale(value) { + setItem(LOCALE_CONFIG, value); dispatch(updateApp({ locale: value })); } diff --git a/hooks/useTimezone.js b/hooks/useTimezone.js new file mode 100644 index 00000000..5de39f9a --- /dev/null +++ b/hooks/useTimezone.js @@ -0,0 +1,17 @@ +import { useState, useCallback } from 'react'; +import { getTimezone } from 'lib/date'; +import { getItem, setItem } from 'lib/web'; + +export default function useTimezone() { + const [timezone, setTimezone] = useState(getItem('umami.timezone') || getTimezone()); + + const saveTimezone = useCallback( + value => { + setItem('umami.timezone', value); + setTimezone(value); + }, + [setTimezone], + ); + + return [timezone, saveTimezone]; +} diff --git a/lang/da-DK.json b/lang/da-DK.json index 363bfc9a..0061f7ff 100644 --- a/lang/da-DK.json +++ b/lang/da-DK.json @@ -11,16 +11,17 @@ "button.login": "Log ind", "button.more": "Mere", "button.refresh": "Opdater", + "button.reset": "Reset", "button.save": "Gem", "button.single-day": "Enkelt dag", "button.view-details": "Vis detajler", - "button.websites": "Hjemmesider", "label.accounts": "Kontoer", "label.administrator": "Administrator", "label.confirm-password": "Godkendt adgangskode", "label.current-password": "Nuværende adgangskode", "label.custom-range": "Tilpasset interval", "label.dashboard": "Betjeningspanel", + "label.default-date-range": "Default date range", "label.domain": "Domæne", "label.enable-share-url": "Aktivér delings-URL", "label.invalid": "Ugyldig", @@ -39,6 +40,7 @@ "label.this-month": "Denne måned", "label.this-week": "Denne uge", "label.this-year": "Dette år", + "label.timezone": "Timezone", "label.today": "Idag", "label.unknown": "Ukendt", "label.username": "Brugernavn", diff --git a/lang/de-DE.json b/lang/de-DE.json index 9c5ffa20..96ba0f99 100644 --- a/lang/de-DE.json +++ b/lang/de-DE.json @@ -11,16 +11,17 @@ "button.login": "Anmelden", "button.more": "Mehr", "button.refresh": "Aktualisieren", + "button.reset": "Reset", "button.save": "Speichern", "button.single-day": "Ein Tag", "button.view-details": "Details anzeigen", - "button.websites": "Webseiten", "label.accounts": "Konten", "label.administrator": "Administrator", "label.confirm-password": "Passwort wiederholen", "label.current-password": "Derzeitiges Passwort", "label.custom-range": "Benutzerdefinierter Bereich", "label.dashboard": "Übersicht", + "label.default-date-range": "Default date range", "label.domain": "Domain", "label.enable-share-url": "Freigabe-URL aktivieren", "label.invalid": "Ungültig", @@ -39,6 +40,7 @@ "label.this-month": "Diesen Monat", "label.this-week": "Diese Woche", "label.this-year": "Dieses Jahr", + "label.timezone": "Timezone", "label.today": "Heute", "label.unknown": "Unbekannt", "label.username": "Benutzername", diff --git a/lang/en-US.json b/lang/en-US.json index 1feeada1..54ee8cad 100644 --- a/lang/en-US.json +++ b/lang/en-US.json @@ -11,16 +11,17 @@ "button.login": "Login", "button.more": "More", "button.refresh": "Refresh", + "button.reset": "Reset", "button.save": "Save", "button.single-day": "Single day", "button.view-details": "View details", - "button.websites": "Websites", "label.accounts": "Accounts", "label.administrator": "Administrator", "label.confirm-password": "Confirm password", "label.current-password": "Current password", "label.custom-range": "Custom range", "label.dashboard": "Dashboard", + "label.default-date-range": "Default date range", "label.domain": "Domain", "label.enable-share-url": "Enable share URL", "label.invalid": "Invalid", @@ -39,6 +40,7 @@ "label.this-month": "This month", "label.this-week": "This week", "label.this-year": "This year", + "label.timezone": "Timezone", "label.today": "Today", "label.unknown": "Unknown", "label.username": "Username", diff --git a/lang/es-MX.json b/lang/es-MX.json index c42d15b4..ed8cf1a2 100644 --- a/lang/es-MX.json +++ b/lang/es-MX.json @@ -11,16 +11,17 @@ "button.login": "Iniciar sesión", "button.more": "Más", "button.refresh": "Refresh", + "button.reset": "Reset", "button.save": "Guardar", "button.single-day": "Single day", "button.view-details": "Ver detalles", - "button.websites": "Sitios", "label.accounts": "Usuarios", "label.administrator": "Administrador", "label.confirm-password": "Confirmar contraseña", "label.current-password": "Contraseña actual", "label.custom-range": "Custom range", "label.dashboard": "Panel de control", + "label.default-date-range": "Default date range", "label.domain": "Dominio", "label.enable-share-url": "Habilitar compartir URL", "label.invalid": "Inválido", @@ -39,6 +40,7 @@ "label.this-month": "Este mes", "label.this-week": "Esta semana", "label.this-year": "Este año", + "label.timezone": "Timezone", "label.today": "Hoy", "label.unknown": "Unknown", "label.username": "Nombre de usuario", diff --git a/lang/fr-FR.json b/lang/fr-FR.json index c4a12df2..8cfedb33 100644 --- a/lang/fr-FR.json +++ b/lang/fr-FR.json @@ -11,16 +11,17 @@ "button.login": "Connexion", "button.more": "Plus", "button.refresh": "Refresh", + "button.reset": "Reset", "button.save": "Sauvegarder", "button.single-day": "Single day", "button.view-details": "Voir les details", - "button.websites": "Sites", "label.accounts": "Comptes", "label.administrator": "Administrateur", "label.confirm-password": "Confirmation du mot de passe", "label.current-password": "Mot de passe actuel", "label.custom-range": "Plage personnalisée", "label.dashboard": "Tableau de bord", + "label.default-date-range": "Default date range", "label.domain": "Domaine", "label.enable-share-url": "Activer le partage d'URL", "label.invalid": "Invalide", @@ -39,6 +40,7 @@ "label.this-month": "Ce mois ci", "label.this-week": "Cette semaine", "label.this-year": "Cette année", + "label.timezone": "Timezone", "label.today": "Aujourd'hui", "label.unknown": "Unknown", "label.username": "Nom d'utilisateur", diff --git a/lang/ja-JP.json b/lang/ja-JP.json index a2529db5..27d3ca34 100644 --- a/lang/ja-JP.json +++ b/lang/ja-JP.json @@ -11,16 +11,17 @@ "button.login": "ログイン", "button.more": "さらに表示", "button.refresh": "Refresh", + "button.reset": "Reset", "button.save": "保存", "button.single-day": "Single day", "button.view-details": "詳細表示", - "button.websites": "Webサイト", "label.accounts": "アカウント", "label.administrator": "管理者", "label.confirm-password": "パスワード(確認)", "label.current-password": "現在のパスワード", "label.custom-range": "Custom range", "label.dashboard": "ダッシュボード", + "label.default-date-range": "Default date range", "label.domain": "ドメイン", "label.enable-share-url": "共有リンクを有効にする", "label.invalid": "無効", @@ -39,6 +40,7 @@ "label.this-month": "今月", "label.this-week": "今週", "label.this-year": "今年", + "label.timezone": "Timezone", "label.today": "今日", "label.unknown": "Unknown", "label.username": "ユーザー名", diff --git a/lang/mn-MN.json b/lang/mn-MN.json index ab38ebb8..0dd543bf 100644 --- a/lang/mn-MN.json +++ b/lang/mn-MN.json @@ -11,16 +11,17 @@ "button.login": "Нэвтрэх", "button.more": "Цааш", "button.refresh": "Refresh", + "button.reset": "Reset", "button.save": "Хадгалах", "button.single-day": "Single day", "button.view-details": "Дэлгэрүүлж харах", - "button.websites": "Вебүүд", "label.accounts": "Хэрэглэгчид", "label.administrator": "Админ", "label.confirm-password": "Шинэ нууц үгээ давтах", "label.current-password": "Ашиглаж буй нууц үг", "label.custom-range": "Дурын хугацаа", "label.dashboard": "Хянах самбар", + "label.default-date-range": "Default date range", "label.domain": "Домэйн", "label.enable-share-url": "Хуваалцах холбоос идэвхжүүлэх", "label.invalid": "Буруу", @@ -39,6 +40,7 @@ "label.this-month": "Энэ сар", "label.this-week": "Энэ долоо хоног", "label.this-year": "Энэ жил", + "label.timezone": "Timezone", "label.today": "Өнөөдөр", "label.unknown": "Unknown", "label.username": "Хэрэглэгчийн нэр", diff --git a/lang/nl-NL.json b/lang/nl-NL.json index 1fdf1350..3ebfcd14 100644 --- a/lang/nl-NL.json +++ b/lang/nl-NL.json @@ -11,16 +11,17 @@ "button.login": "Inloggen", "button.more": "Toon meer", "button.refresh": "Vernieuwen", + "button.reset": "Reset", "button.save": "Opslaan", "button.single-day": "Enkele dag", "button.view-details": "Meer details", - "button.websites": "Websites", "label.accounts": "Accounts", "label.administrator": "Administrator", "label.confirm-password": "Wachtwoord bevestigen", "label.current-password": "Huidig wachtwoord", "label.custom-range": "Aangepast bereik", "label.dashboard": "Dashboard", + "label.default-date-range": "Default date range", "label.domain": "Domein", "label.enable-share-url": "Sta delen via openbare URL toe", "label.invalid": "Ongeldig", @@ -39,6 +40,7 @@ "label.this-month": "Deze maand", "label.this-week": "Deze week", "label.this-year": "Dit jaar", + "label.timezone": "Timezone", "label.today": "Vandaag", "label.unknown": "Onbekend", "label.username": "Gebruikersnaam", diff --git a/lang/ru-RU.json b/lang/ru-RU.json index fda4511f..5cbedf0b 100644 --- a/lang/ru-RU.json +++ b/lang/ru-RU.json @@ -11,16 +11,17 @@ "button.login": "Войти", "button.more": "Больше", "button.refresh": "Refresh", + "button.reset": "Reset", "button.save": "Сохранить", "button.single-day": "Single day", "button.view-details": "Посмотреть детали", - "button.websites": "Сайты", "label.accounts": "Аккаунты", "label.administrator": "Администратор", "label.confirm-password": "Подтвердить пароль", "label.current-password": "Текущий пароль", "label.custom-range": "Другой период", "label.dashboard": "Информационная панель", + "label.default-date-range": "Default date range", "label.domain": "Домен", "label.enable-share-url": "Разрешить делиться ссылкой", "label.invalid": "Некорректный", @@ -39,6 +40,7 @@ "label.this-month": "Этот месяц", "label.this-week": "Эта неделя", "label.this-year": "Этот год", + "label.timezone": "Timezone", "label.today": "Сегодня", "label.unknown": "Unknown", "label.username": "Имя пользователя", diff --git a/lang/tr-TR.json b/lang/tr-TR.json index eeced942..6a93d1dd 100644 --- a/lang/tr-TR.json +++ b/lang/tr-TR.json @@ -11,16 +11,17 @@ "button.login": "Giriş Yap", "button.more": "Detaylı göster", "button.refresh": "Refresh", + "button.reset": "Reset", "button.save": "Kaydet", "button.single-day": "Single day", "button.view-details": "Detayı incele", - "button.websites": "Web siteleri", "label.accounts": "Hesaplar", "label.administrator": "Yönetici", "label.confirm-password": "Parolayı onayla", "label.current-password": "Mevcut parola", "label.custom-range": "Custom range", "label.dashboard": "Kontrol Paneli", + "label.default-date-range": "Default date range", "label.domain": "Alan adı", "label.enable-share-url": "Anonim paylaşım URL'i aktif", "label.invalid": "Geçeriz", @@ -39,6 +40,7 @@ "label.this-month": "Bu ay", "label.this-week": "Bu hafta", "label.this-year": "Bu yıl", + "label.timezone": "Timezone", "label.today": "Bugün", "label.unknown": "Unknown", "label.username": "Kullanıcı adı", diff --git a/lang/zh-CN.json b/lang/zh-CN.json index 3e192a64..9c6ddda4 100644 --- a/lang/zh-CN.json +++ b/lang/zh-CN.json @@ -11,16 +11,17 @@ "button.login": "登录", "button.more": "更多", "button.refresh": "刷新", + "button.reset": "Reset", "button.save": "保存", "button.single-day": "单日", "button.view-details": "查看更多", - "button.websites": "网站", "label.accounts": "账户", "label.administrator": "管理员", "label.confirm-password": "确认密码", "label.current-password": "目前密码", "label.custom-range": "自定义时间段", "label.dashboard": "仪表板", + "label.default-date-range": "Default date range", "label.domain": "域名", "label.enable-share-url": "激活共享链接", "label.invalid": "输入无效", @@ -39,6 +40,7 @@ "label.this-month": "本月", "label.this-week": "本周", "label.this-year": "今年", + "label.timezone": "Timezone", "label.today": "今天", "label.unknown": "未知", "label.username": "用户名", diff --git a/lib/constants.js b/lib/constants.js index e9641c68..0e2468ab 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -1,4 +1,9 @@ export const AUTH_COOKIE_NAME = 'umami.auth'; +export const LOCALE_CONFIG = 'umami.locale'; +export const TIMEZONE_CONFIG = 'umami.timezone'; +export const DATE_RANGE_CONFIG = 'umami.date-range'; + +export const DEFAULT_DATE_RANGE = '24hour'; export const POSTGRESQL = 'postgresql'; export const MYSQL = 'mysql'; diff --git a/lib/lang.js b/lib/lang.js index 8568ed4f..8074c169 100644 --- a/lib/lang.js +++ b/lib/lang.js @@ -1,5 +1,5 @@ import { format } from 'date-fns'; -import { enUS, nl, zhCN, tr, ru, de, ja, es, fr } from 'date-fns/locale'; +import { enUS, nl, zhCN, tr, ru, de, ja, es, fr, da } from 'date-fns/locale'; import enMessages from 'lang-compiled/en-US.json'; import nlMessages from 'lang-compiled/nl-NL.json'; import zhCNMessages from 'lang-compiled/zh-CN.json'; @@ -10,6 +10,7 @@ import jaMessages from 'lang-compiled/ja-JP.json'; import esMXMessages from 'lang-compiled/es-MX.json'; import frMessages from 'lang-compiled/fr-FR.json'; import mnMNMessages from 'lang-compiled/mn-MN.json'; +import daMessages from 'lang-compiled/da-DK.json'; export const messages = { 'en-US': enMessages, @@ -22,6 +23,7 @@ export const messages = { 'es-MX': esMXMessages, 'fr-FR': frMessages, 'mn-MN': mnMNMessages, + 'da-DK': daMessages, }; export const dateLocales = { @@ -29,6 +31,7 @@ export const dateLocales = { 'nl-NL': nl, 'zh-CN': zhCN, 'de-DE': de, + 'da-DK': da, 'ru-RU': ru, 'tr-TR': tr, 'ja-JP': ja, @@ -40,6 +43,7 @@ export const dateLocales = { export const menuOptions = [ { label: 'English', value: 'en-US', display: 'EN' }, { label: '中文', value: 'zh-CN', display: 'CN' }, + { label: 'Dansk', value: 'da-DK', display: 'DA' }, { label: 'Deutsch', value: 'de-DE', display: 'DE' }, { label: 'Español', value: 'es-MX', display: 'ES' }, { label: 'Français', value: 'fr-FR', display: 'FR' }, diff --git a/package.json b/package.json index 37209cea..547537f3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "umami", - "version": "0.41.0", + "version": "0.42.0", "description": "A simple, fast, website analytics alternative to Google Analytics. ", "author": "Mike Cao ", "license": "MIT", @@ -83,6 +83,7 @@ "redux-thunk": "^2.3.0", "request-ip": "^2.1.3", "thenby": "^1.3.4", + "timezone-support": "^2.0.2", "tinycolor2": "^1.4.1", "unfetch": "^4.1.0", "uuid": "^8.3.0" diff --git a/redux/actions/app.js b/redux/actions/app.js index 72636a74..cbb8285b 100644 --- a/redux/actions/app.js +++ b/redux/actions/app.js @@ -1,9 +1,10 @@ import { createSlice } from '@reduxjs/toolkit'; import { getItem } from 'lib/web'; +import { LOCALE_CONFIG } from 'lib/constants'; const app = createSlice({ name: 'app', - initialState: { locale: getItem('umami.locale') || 'en-US' }, + initialState: { locale: getItem(LOCALE_CONFIG) || 'en-US' }, reducers: { updateApp(state, action) { state = action.payload; diff --git a/yarn.lock b/yarn.lock index da2fa5cb..201c7e5d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2699,6 +2699,11 @@ commander@2, commander@^2.20.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@2.20.0: + version "2.20.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" + integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== + commander@^6.0.0, commander@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/commander/-/commander-6.1.0.tgz#f8d722b78103141006b66f4c7ba1e97315ba75bc" @@ -8608,6 +8613,13 @@ timers-browserify@^2.0.4: dependencies: setimmediate "^1.0.4" +timezone-support@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/timezone-support/-/timezone-support-2.0.2.tgz#801d6924478b1b60f09b90699ce1127a6044cbe7" + integrity sha512-J/1PyHCX76vOPuJzCyHMQMH2wTjXCJ30R5EXaS/QTi+xYsL0thS0pubDrHCWnfG4zU1jpPJtctnBBRCOpcJZeQ== + dependencies: + commander "2.20.0" + tiny-lru@7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/tiny-lru/-/tiny-lru-7.0.6.tgz#b0c3cdede1e5882aa2d1ae21cb2ceccf2a331f24"