diff --git a/assets/user.svg b/assets/user.svg index 133b1bc1..a46974d1 100644 --- a/assets/user.svg +++ b/assets/user.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/components/common/DateFilter.js b/components/common/DateFilter.js index 3c0431c4..4a40e3fd 100644 --- a/components/common/DateFilter.js +++ b/components/common/DateFilter.js @@ -1,99 +1,109 @@ -import Calendar from 'assets/calendar-alt.svg'; -import DatePickerForm from 'components/metrics/DatePickerForm'; import { endOfYear, isSameDay } from 'date-fns'; +import { useState } from 'react'; +import { Icon, Modal, Dropdown, Item } from 'react-basics'; +import { useIntl, defineMessages } from 'react-intl'; +import DatePickerForm from 'components/metrics/DatePickerForm'; import useLocale from 'hooks/useLocale'; import { dateFormat } from 'lib/date'; -import PropTypes from 'prop-types'; -import { useState } from 'react'; -import { Icon, Modal } from 'react-basics'; -import { FormattedMessage } from 'react-intl'; -import DropDown from './DropDown'; +import Calendar from 'assets/calendar-alt.svg'; -export const filterOptions = [ - { label: , value: '1day' }, - { - label: ( - - ), - value: '24hour', - }, - { - label: , - value: '-1day', - }, - { - label: , - value: '1week', - divider: true, - }, - { - label: ( - - ), - value: '7day', - }, - { - label: , - value: '1month', - divider: true, - }, - { - label: ( - - ), - value: '30day', - }, - { - label: ( - - ), - value: '90day', - }, - { label: , value: '1year' }, - { - label: , - value: 'all', - divider: true, - }, - { - label: , - value: 'custom', - divider: true, - }, -]; +const messages = defineMessages({ + today: { id: 'label.today', defaultMessage: 'Today' }, + lastHours: { id: 'label.last-hours', defaultMessage: 'Last {x} hours' }, + yesterday: { id: 'label.yesterday', defaultMessage: 'Yesterday' }, + thisWeek: { id: 'label.this-week', defaultMessage: 'This week' }, + lastDays: { id: 'label.last-days', defaultMessage: 'Last {x} days' }, + thisMonth: { id: 'label.this-month', defaultMessage: 'This month' }, + thisYear: { id: 'label.this-year', defaultMessage: 'This year' }, + allTime: { id: 'label.all-time', defaultMessage: 'All time' }, + customRange: { id: 'label.custom-range', defaultMessage: 'Custom-range' }, +}); -function DateFilter({ value, startDate, endDate, onChange, className, options }) { +function DateFilter({ value, startDate, endDate, onChange, className }) { + const { formatMessage } = useIntl(); const [showPicker, setShowPicker] = useState(false); - const displayValue = - value === 'custom' ? ( + + const options = [ + { label: formatMessage(messages.today), value: '1day' }, + { + label: formatMessage(messages.lastHours, { x: 24 }), + value: '24hour', + }, + { + label: formatMessage(messages.yesterday), + value: '-1day', + }, + { + label: formatMessage(messages.thisWeek), + value: '1week', + divider: true, + }, + { + label: formatMessage(messages.lastDays, { x: 7 }), + value: '7day', + }, + { + label: formatMessage(messages.thisMonth), + value: '1month', + divider: true, + }, + { + label: formatMessage(messages.lastDays, { x: 30 }), + value: '30day', + }, + { + label: formatMessage(messages.lastDays, { x: 90 }), + value: '90day', + }, + { label: formatMessage(messages.thisYear), value: '1year' }, + { + label: formatMessage(messages.allTime), + value: 'all', + divider: true, + }, + { + label: formatMessage(messages.customRange), + value: 'custom', + divider: true, + }, + ]; + + const renderValue = value => { + return value === 'custom' ? ( handleChange('custom')} /> ) : ( - value + options.find(e => e.value === value).label ); + }; - async function handleChange(value) { + const handleChange = async value => { if (value === 'custom') { setShowPicker(true); return; } onChange(value); - } + }; - function handlePickerChange(value) { + const handlePickerChange = value => { setShowPicker(false); onChange(value); - } + }; + + const handleClose = () => setShowPicker(false); return ( <> - + > + {({ label, value }) => {label}} + {showPicker && ( - + { ); }; -DateFilter.propTypes = { - value: PropTypes.string, - startDate: PropTypes.instanceOf(Date), - endDate: PropTypes.instanceOf(Date), - onChange: PropTypes.func, - className: PropTypes.string, -}; - export default DateFilter; diff --git a/components/metrics/WebsiteChart.js b/components/metrics/WebsiteChart.js index a1bb5289..daab7f72 100644 --- a/components/metrics/WebsiteChart.js +++ b/components/metrics/WebsiteChart.js @@ -66,8 +66,9 @@ export default function WebsiteChart({ async function handleDateChange(value) { if (value === 'all') { - const { data, ok } = await get(`/websites/${websiteId}`); - if (ok) { + const data = await get(`/websites/${websiteId}`); + + if (data) { setDateRange({ value, ...getDateRangeValues(new Date(data.createdAt), Date.now()) }); } } else { diff --git a/components/nav/Nav.js b/components/nav/Nav.js index a1aa61c1..4efb8258 100644 --- a/components/nav/Nav.js +++ b/components/nav/Nav.js @@ -1,12 +1,13 @@ -import User from 'assets/user.svg'; -import Team from 'assets/users.svg'; -import Website from 'assets/website.svg'; import classNames from 'classnames'; import Link from 'next/link'; import { useRouter } from 'next/router'; import { Icon, Item, Menu, Text } from 'react-basics'; -import styles from './Nav.module.css'; import useUser from 'hooks/useUser'; +import User from 'assets/user.svg'; +import Team from 'assets/users.svg'; +import Website from 'assets/website.svg'; +import Profile from 'assets/profile.svg'; +import styles from './Nav.module.css'; export default function Nav() { const user = useUser(); @@ -22,7 +23,7 @@ export default function Nav() { { icon: , label: 'Websites', url: '/settings/websites' }, { icon: , label: 'Users', url: '/settings/users' }, { icon: , label: 'Teams', url: '/settings/teams' }, - { icon: , label: 'Profile', url: '/settings/profile' }, + { icon: , label: 'Profile', url: '/settings/profile' }, ]; return ( diff --git a/components/nav/Nav.module.css b/components/nav/Nav.module.css index cfd8ba05..e3beb0ed 100644 --- a/components/nav/Nav.module.css +++ b/components/nav/Nav.module.css @@ -27,7 +27,7 @@ } .item a { - color: var(--base700); + color: var(--base600); display: flex; align-items: center; gap: 20px; @@ -42,5 +42,4 @@ .item.selected a { color: var(--base900); - background: var(--base100); } diff --git a/components/pages/settings/profile/ProfileSettings.js b/components/pages/settings/profile/ProfileSettings.js index 03f73a8a..deaaffb8 100644 --- a/components/pages/settings/profile/ProfileSettings.js +++ b/components/pages/settings/profile/ProfileSettings.js @@ -2,13 +2,12 @@ import Page from 'components/layout/Page'; import PageHeader from 'components/layout/PageHeader'; import ProfileDetails from 'components/settings/ProfileDetails'; import { useState } from 'react'; -import { Breadcrumbs, Icon, Item, Tabs, useToast, Modal, Button } from 'react-basics'; +import { Breadcrumbs, Icon, Item, useToast, Modal, Button } from 'react-basics'; import UserPasswordForm from 'components/pages/settings/users/UserPasswordForm'; -import Pen from 'assets/pen.svg'; +import Lock from 'assets/lock.svg'; export default function ProfileSettings() { const [edit, setEdit] = useState(false); - const [tab, setTab] = useState('general'); const { toast, showToast } = useToast(); const handleSave = () => { @@ -33,17 +32,14 @@ export default function ProfileSettings() { - - General - - {tab === 'general' && } + {edit && ( - + {close => } )} diff --git a/components/pages/settings/users/UserEditForm.js b/components/pages/settings/users/UserEditForm.js index ab16eca8..3e7f8d0d 100644 --- a/components/pages/settings/users/UserEditForm.js +++ b/components/pages/settings/users/UserEditForm.js @@ -12,7 +12,6 @@ import { useRef } from 'react'; import { useMutation } from '@tanstack/react-query'; import useApi from 'hooks/useApi'; import { ROLES } from 'lib/constants'; -import styles from './UserForm.module.css'; const items = [ { @@ -41,14 +40,7 @@ export default function UserEditForm({ data, onSave }) { }; return ( -
+ diff --git a/components/settings/LanguageSetting.js b/components/settings/LanguageSetting.js index eb3d698a..b20250e5 100644 --- a/components/settings/LanguageSetting.js +++ b/components/settings/LanguageSetting.js @@ -1,30 +1,28 @@ -import { FormattedMessage } from 'react-intl'; -import DropDown from 'components/common/DropDown'; -import { Button } from 'react-basics'; +import { useIntl, defineMessages } from 'react-intl'; +import { Button, Dropdown, Item, Flexbox } from 'react-basics'; import useLocale from 'hooks/useLocale'; import { DEFAULT_LOCALE } from 'lib/constants'; -import styles from './TimezoneSetting.module.css'; import { languages } from 'lib/lang'; +const messages = defineMessages({ + reset: { id: 'label.reset', defaultMessage: 'Reset' }, +}); + export default function LanguageSetting() { + const { formatMessage } = useIntl(); const { locale, saveLocale } = useLocale(); - const options = Object.keys(languages).map(key => ({ ...languages[key], value: key })); + const options = Object.keys(languages); function handleReset() { saveLocale(DEFAULT_LOCALE); } return ( - <> - - - + + + {item => {languages[item].label}} + + + ); } diff --git a/components/settings/ProfileDetails.js b/components/settings/ProfileDetails.js index c0aaf0e6..fb7ef6d7 100644 --- a/components/settings/ProfileDetails.js +++ b/components/settings/ProfileDetails.js @@ -1,53 +1,46 @@ +import { Form, FormRow } from 'react-basics'; +import { useIntl, defineMessages } from 'react-intl'; import TimezoneSetting from 'components/settings/TimezoneSetting'; +import DateRangeSetting from 'components/settings/DateRangeSetting'; +import LanguageSetting from 'components/settings/LanguageSetting'; +import ThemeSetting from 'components/settings/ThemeSetting'; import useUser from 'hooks/useUser'; -import { FormattedMessage } from 'react-intl'; -import DateRangeSetting from './DateRangeSetting'; -import LanguageSetting from './LanguageSetting'; -import styles from './ProfileDetails.module.css'; -import ThemeSetting from './ThemeSetting'; +const messages = defineMessages({ + username: { id: 'label.username', defaultMessage: 'Username' }, + role: { id: 'label.role', defaultMessage: 'Role' }, + timezone: { id: 'label.timezone', defaultMessage: 'Timezone' }, + dateRange: { id: 'label.default-date-range', defaultMessage: 'Default date range' }, + language: { id: 'label.language', defaultMessage: 'Language' }, + theme: { id: 'label.theme', defaultMessage: 'Theme' }, +}); export default function ProfileDetails() { const user = useUser(); + const { formatMessage } = useIntl(); if (!user) { return null; } - const { username } = user; + const { username, role } = user; return ( - <> -
-
- -
-
{username}
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- + + {username} + {role} + + + + + + + + + + + + + ); } diff --git a/components/settings/TimezoneSetting.js b/components/settings/TimezoneSetting.js index 8cac4d0d..7bed3db3 100644 --- a/components/settings/TimezoneSetting.js +++ b/components/settings/TimezoneSetting.js @@ -1,14 +1,17 @@ -import { FormattedMessage } from 'react-intl'; +import { Dropdown, Item, Button } from 'react-basics'; +import { useIntl, defineMessages } from 'react-intl'; import { listTimeZones } from 'timezone-support'; -import DropDown from 'components/common/DropDown'; -import { Button } from 'react-basics'; import useTimezone from 'hooks/useTimezone'; import { getTimezone } from 'lib/date'; -import styles from './TimezoneSetting.module.css'; + +const messages = defineMessages({ + reset: { id: 'label.reset', defaultMessage: 'Reset' }, +}); export default function TimezoneSetting() { + const { formatMessage } = useIntl(); const [timezone, saveTimezone] = useTimezone(); - const options = listTimeZones().map(n => ({ label: n, value: n })); + const options = listTimeZones(); function handleReset() { saveTimezone(getTimezone()); @@ -16,15 +19,10 @@ export default function TimezoneSetting() { return ( <> - - + + {item => {item}} + + ); } diff --git a/components/settings/TimezoneSetting.module.css b/components/settings/TimezoneSetting.module.css deleted file mode 100644 index 9561111d..00000000 --- a/components/settings/TimezoneSetting.module.css +++ /dev/null @@ -1,8 +0,0 @@ -.menu { - max-height: 300px; - overflow-y: auto; -} - -.button { - margin-left: 10px; -} diff --git a/components/settings/UserButton.js b/components/settings/UserButton.js index 542588d6..e20e0dd3 100644 --- a/components/settings/UserButton.js +++ b/components/settings/UserButton.js @@ -1,4 +1,3 @@ -import User from 'assets/user.svg'; import useConfig from 'hooks/useConfig'; import useUser from 'hooks/useUser'; import { AUTH_TOKEN } from 'lib/constants'; @@ -7,8 +6,9 @@ import { useRouter } from 'next/router'; import { useRef, useState } from 'react'; import { Button, Icon, Item, Menu, Popup, Text } from 'react-basics'; import { FormattedMessage } from 'react-intl'; +import useDocumentClick from 'hooks/useDocumentClick'; +import Profile from 'assets/profile.svg'; import styles from './UserButton.module.css'; -import useDocumentClick from '../../hooks/useDocumentClick'; export default function UserButton() { const [show, setShow] = useState(false); @@ -61,7 +61,7 @@ export default function UserButton() {
{show && ( diff --git a/hooks/useCountryNames.js b/hooks/useCountryNames.js index 7c15779f..6988e6cf 100644 --- a/hooks/useCountryNames.js +++ b/hooks/useCountryNames.js @@ -12,9 +12,9 @@ export default function useCountryNames(locale) { const { basePath } = useRouter(); async function loadData(locale) { - const { ok, data } = await get(`${basePath}/intl/country/${locale}.json`); + const data = await get(`${basePath}/intl/country/${locale}.json`); - if (ok) { + if (data) { countryNames[locale] = data; setList(countryNames[locale]); } else { diff --git a/hooks/useLanguageNames.js b/hooks/useLanguageNames.js index 86a358ac..3b153f28 100644 --- a/hooks/useLanguageNames.js +++ b/hooks/useLanguageNames.js @@ -12,9 +12,9 @@ export default function useLanguageNames(locale) { const { basePath } = useRouter(); async function loadData(locale) { - const { ok, data } = await get(`${basePath}/intl/language/${locale}.json`); + const data = await get(`${basePath}/intl/language/${locale}.json`); - if (ok) { + if (data) { languageNames[locale] = data; setList(languageNames[locale]); } else { diff --git a/hooks/useLocale.js b/hooks/useLocale.js index 06c3bc6b..6063f7ab 100644 --- a/hooks/useLocale.js +++ b/hooks/useLocale.js @@ -21,9 +21,9 @@ export default function useLocale() { const dateLocale = getDateLocale(locale); async function loadMessages(locale) { - const { ok, data } = await get(`${basePath}/intl/messages/${locale}.json`); + const data = await get(`${basePath}/intl/messages/${locale}.json`); - if (ok) { + if (data) { messages[locale] = data; } } diff --git a/pages/_app.js b/pages/_app.js index a4ba51a5..8bb8c07b 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -11,7 +11,13 @@ import 'styles/index.css'; import '@fontsource/inter/400.css'; import '@fontsource/inter/600.css'; -const client = new QueryClient(); +const client = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + }, + }, +}); export default function App({ Component, pageProps }) { const { locale, messages } = useLocale();