From ce1df315d3a0669ba96a244e96f49c190cd8dbb8 Mon Sep 17 00:00:00 2001 From: Artur Pata Date: Tue, 25 Mar 2025 12:16:54 +0200 Subject: [PATCH] Remove format pragma, run prettier on /assets (#5237) --- assets/.prettierignore | 1 + assets/.prettierrc.json | 1 - assets/css/app.css | 2 - assets/css/chartjs.css | 1 - assets/css/flatpickr-colors.css | 1 - assets/css/loader.css | 1 - assets/css/modal.css | 1 - assets/css/storybook.css | 6 +- assets/css/tooltip.css | 1 - assets/js/app.js | 31 +- assets/js/dashboard.tsx | 2 - assets/js/dashboard/api.ts | 1 - assets/js/dashboard/components/combobox.js | 2 - .../dashboard/components/drilldown-link.tsx | 2 - .../js/dashboard/components/error-panel.tsx | 2 - .../components/filter-operator-selector.js | 2 - assets/js/dashboard/components/lazy-loader.js | 10 +- assets/js/dashboard/components/notice.js | 59 ++- assets/js/dashboard/components/popover.tsx | 1 - .../js/dashboard/components/search-input.tsx | 2 - .../js/dashboard/components/sort-button.tsx | 2 - assets/js/dashboard/components/table.tsx | 2 - assets/js/dashboard/custom-hooks.js | 23 +- assets/js/dashboard/dashboard-keybinds.tsx | 1 - .../dashboard/error/error-boundary.test.tsx | 2 - assets/js/dashboard/error/error-boundary.tsx | 2 - .../error/something-went-wrong.test.tsx | 2 - .../dashboard/error/something-went-wrong.tsx | 2 - assets/js/dashboard/extra/funnel-tooltip.js | 14 +- assets/js/dashboard/extra/funnel.js | 137 +++-- assets/js/dashboard/fade-in.js | 8 +- .../filtering/segments-context.test.tsx | 2 - .../dashboard/filtering/segments-context.tsx | 1 - .../js/dashboard/filtering/segments.test.ts | 2 - assets/js/dashboard/filtering/segments.ts | 2 - assets/js/dashboard/hooks/api-client.ts | 2 - .../js/dashboard/hooks/use-order-by.test.ts | 2 - assets/js/dashboard/hooks/use-order-by.ts | 2 - assets/js/dashboard/hooks/use-previous.ts | 12 +- assets/js/dashboard/index.tsx | 2 - assets/js/dashboard/keybinding.tsx | 1 - assets/js/dashboard/last-load-context.tsx | 1 - assets/js/dashboard/nav-menu/filter-menu.tsx | 2 - assets/js/dashboard/nav-menu/filter-pill.tsx | 2 - .../dashboard/nav-menu/filter-pills-list.tsx | 2 - .../dashboard/nav-menu/filters-bar.test.tsx | 2 - assets/js/dashboard/nav-menu/filters-bar.tsx | 2 - .../nav-menu/nav-menu-components.tsx | 2 - .../query-periods/comparison-period-menu.tsx | 2 - .../date-range-calendar.test.tsx | 2 - .../query-periods/date-range-calendar.tsx | 1 - .../query-periods/move-period-arrows.tsx | 1 - .../query-periods/query-dates.test.tsx | 2 - .../query-periods/query-period-menu.tsx | 2 - .../query-periods/query-periods-picker.tsx | 2 - .../query-periods/shared-menu-items.tsx | 2 - .../segments/searchable-segments-section.tsx | 2 - .../nav-menu/segments/segment-menu.tsx | 2 - assets/js/dashboard/nav-menu/top-bar.test.tsx | 2 - assets/js/dashboard/nav-menu/top-bar.tsx | 2 - .../navigation/routeless-modals-context.tsx | 1 - .../dashboard/navigation/use-app-navigate.tsx | 1 - .../use-definite-location-state.tsx | 2 - assets/js/dashboard/query-context.tsx | 1 - .../js/dashboard/query-time-periods.test.ts | 2 - assets/js/dashboard/query-time-periods.ts | 1 - assets/js/dashboard/query.ts | 2 - assets/js/dashboard/router.tsx | 1 - .../segments/routeless-segment-modals.tsx | 2 - .../segments/segment-authorship.test.tsx | 2 - .../dashboard/segments/segment-authorship.tsx | 2 - .../segments/segment-modals.test.tsx | 2 - .../js/dashboard/segments/segment-modals.tsx | 2 - assets/js/dashboard/site-context.test.tsx | 1 - assets/js/dashboard/site-context.tsx | 1 - assets/js/dashboard/stats/bar.js | 29 +- .../dashboard/stats/behaviours/conversions.js | 45 +- .../stats/behaviours/goal-conversions.js | 68 ++- assets/js/dashboard/stats/behaviours/index.js | 258 +++++++--- assets/js/dashboard/stats/behaviours/props.js | 116 +++-- assets/js/dashboard/stats/current-visitors.js | 2 - assets/js/dashboard/stats/devices/index.js | 239 ++++++--- .../dashboard/stats/graph/date-formatter.js | 25 +- .../js/dashboard/stats/graph/graph-tooltip.js | 141 ++++-- assets/js/dashboard/stats/graph/graph-util.js | 113 +++-- .../dashboard/stats/graph/interval-picker.js | 78 +-- assets/js/dashboard/stats/graph/line-graph.js | 127 +++-- .../dashboard/stats/graph/sampling-notice.js | 28 +- .../js/dashboard/stats/graph/stats-export.js | 53 +- assets/js/dashboard/stats/graph/top-stats.js | 2 - .../js/dashboard/stats/graph/visitor-graph.js | 129 +++-- .../stats/graph/with-imported-switch.js | 30 +- .../imported-query-unsupported-warning.js | 26 +- .../stats/locations/geolocation-notice.tsx | 1 - assets/js/dashboard/stats/locations/index.js | 479 ++++++++++-------- .../dashboard/stats/locations/map-tooltip.tsx | 1 - assets/js/dashboard/stats/locations/map.tsx | 1 - .../stats/modals/breakdown-modal.tsx | 2 - .../stats/modals/breakdown-table.tsx | 2 - .../js/dashboard/stats/modals/conversions.js | 64 ++- .../modals/devices/browser-versions-modal.js | 58 ++- .../stats/modals/devices/browsers-modal.js | 58 ++- .../stats/modals/devices/choose-metrics.js | 17 +- .../operating-system-versions-modal.js | 53 +- .../modals/devices/operating-systems-modal.js | 53 +- .../stats/modals/devices/screen-sizes.js | 44 +- .../js/dashboard/stats/modals/entry-pages.js | 69 ++- .../js/dashboard/stats/modals/exit-pages.js | 69 ++- .../stats/modals/filter-modal-group.js | 39 +- .../stats/modals/filter-modal-props-row.js | 2 - .../stats/modals/filter-modal-row.js | 2 - .../js/dashboard/stats/modals/filter-modal.js | 74 ++- .../stats/modals/google-keywords.tsx | 2 - .../dashboard/stats/modals/locations-modal.js | 98 ++-- assets/js/dashboard/stats/modals/modal.js | 72 +-- assets/js/dashboard/stats/modals/pages.js | 64 ++- assets/js/dashboard/stats/modals/props.js | 70 ++- .../stats/modals/referrer-drilldown.js | 82 +-- .../js/dashboard/stats/modals/rocket-icon.js | 24 +- assets/js/dashboard/stats/modals/sources.js | 129 +++-- assets/js/dashboard/stats/more-link.js | 4 +- assets/js/dashboard/stats/pages/index.js | 103 ++-- .../stats/reports/change-arrow.test.tsx | 2 - .../dashboard/stats/reports/change-arrow.tsx | 2 - assets/js/dashboard/stats/reports/list.tsx | 2 - .../stats/reports/metric-formatter.ts | 2 - .../stats/reports/metric-value.test.tsx | 2 - .../dashboard/stats/reports/metric-value.tsx | 2 - assets/js/dashboard/stats/reports/metrics.js | 2 - assets/js/dashboard/stats/sources/index.js | 20 +- .../dashboard/stats/sources/referrer-list.js | 53 +- .../dashboard/stats/sources/search-terms.tsx | 2 - .../js/dashboard/stats/sources/source-list.js | 179 ++++--- assets/js/dashboard/theme-context.tsx | 1 - assets/js/dashboard/user-context.tsx | 1 - assets/js/dashboard/util/date.js | 2 - assets/js/dashboard/util/date.test.ts | 2 - assets/js/dashboard/util/filter-text.test.tsx | 22 +- assets/js/dashboard/util/filter-text.tsx | 2 - assets/js/dashboard/util/filters.js | 2 - assets/js/dashboard/util/filters.test.ts | 2 - assets/js/dashboard/util/money.ts | 8 +- .../dashboard/util/number-formatter.test.ts | 6 +- assets/js/dashboard/util/number-formatter.ts | 18 +- .../dashboard/util/realtime-update-timer.js | 6 +- .../dashboard/util/seconds-since-last-load.js | 4 +- assets/js/dashboard/util/storage.js | 12 +- assets/js/dashboard/util/tooltip.tsx | 70 ++- .../js/dashboard/util/url-search-params-v1.ts | 2 - .../util/url-search-params-v2.test.ts | 2 - .../js/dashboard/util/url-search-params-v2.ts | 1 - .../dashboard/util/url-search-params.test.ts | 2 - assets/js/dashboard/util/url-search-params.ts | 1 - assets/js/dashboard/util/url.test.ts | 2 - assets/js/dashboard/util/url.ts | 1 - assets/js/embed.content.js | 6 +- assets/js/embed.host.js | 13 +- assets/js/liveview/combo-box.js | 34 +- assets/js/liveview/dropdown.js | 4 +- assets/js/liveview/live_socket.js | 38 +- assets/js/liveview/phx_events.js | 8 +- assets/js/polyfills/closest.js | 25 +- assets/js/storybook.js | 37 +- assets/package.json | 2 +- assets/test-utils/app-context-providers.tsx | 2 - assets/test-utils/extend-expect.ts | 2 +- assets/test-utils/index.ts | 2 - assets/test-utils/mock-api.ts | 2 - assets/test-utils/reset-state.ts | 2 - 169 files changed, 2602 insertions(+), 1667 deletions(-) diff --git a/assets/.prettierignore b/assets/.prettierignore index 3c40ba6b1d..74a2872d27 100644 --- a/assets/.prettierignore +++ b/assets/.prettierignore @@ -4,3 +4,4 @@ static/images/ .*rc *.json *.config.js +js/types/query-api.d.ts diff --git a/assets/.prettierrc.json b/assets/.prettierrc.json index 5c42a1aa9b..81c51f9894 100644 --- a/assets/.prettierrc.json +++ b/assets/.prettierrc.json @@ -1,6 +1,5 @@ { "singleQuote": true, - "insertPragma": true, "trailingComma": "none", "semi": false } diff --git a/assets/css/app.css b/assets/css/app.css index 0e1401d092..eb78a69a13 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -1,5 +1,3 @@ -/* @format */ - @import 'tailwindcss/base'; @import 'flatpickr/dist/flatpickr.min.css'; @import './modal.css'; diff --git a/assets/css/chartjs.css b/assets/css/chartjs.css index 4c417bce39..569d39dd96 100644 --- a/assets/css/chartjs.css +++ b/assets/css/chartjs.css @@ -1,4 +1,3 @@ -/* @format */ #chartjs-tooltip { background-color: rgb(25 30 56); position: absolute; diff --git a/assets/css/flatpickr-colors.css b/assets/css/flatpickr-colors.css index 44dbdf34c9..8336029144 100644 --- a/assets/css/flatpickr-colors.css +++ b/assets/css/flatpickr-colors.css @@ -1,4 +1,3 @@ -/* @format */ /* stylelint-disable media-feature-range-notation */ /* stylelint-disable selector-class-pattern */ diff --git a/assets/css/loader.css b/assets/css/loader.css index 010266d7af..2c041f35e1 100644 --- a/assets/css/loader.css +++ b/assets/css/loader.css @@ -1,4 +1,3 @@ -/* @format */ .loading { width: 50px; height: 50px; diff --git a/assets/css/modal.css b/assets/css/modal.css index e89898a4d7..500ffab9a0 100644 --- a/assets/css/modal.css +++ b/assets/css/modal.css @@ -1,4 +1,3 @@ -/* @format */ /* stylelint-disable selector-class-pattern */ .modal { display: none; diff --git a/assets/css/storybook.css b/assets/css/storybook.css index e245aca2aa..e78c04fc7d 100644 --- a/assets/css/storybook.css +++ b/assets/css/storybook.css @@ -1,6 +1,6 @@ -@import "tailwindcss/base"; -@import "tailwindcss/components"; -@import "tailwindcss/utilities"; +@import 'tailwindcss/base'; +@import 'tailwindcss/components'; +@import 'tailwindcss/utilities'; /* * Put your component styling within the Tailwind utilities layer. diff --git a/assets/css/tooltip.css b/assets/css/tooltip.css index 9f54f47a08..124d89dad2 100644 --- a/assets/css/tooltip.css +++ b/assets/css/tooltip.css @@ -1,4 +1,3 @@ -/* @format */ [tooltip] { position: relative; display: inline-block; diff --git a/assets/js/app.js b/assets/js/app.js index ac96c343bc..4224829b81 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -1,10 +1,10 @@ -import "./polyfills/closest" +import './polyfills/closest' import 'abortcontroller-polyfill/dist/polyfill-patch-fetch' import Alpine from 'alpinejs' -import "./liveview/live_socket" -import comboBox from "./liveview/combo-box" -import dropdown from "./liveview/dropdown" -import "./liveview/phx_events" +import './liveview/live_socket' +import comboBox from './liveview/combo-box' +import dropdown from './liveview/dropdown' +import './liveview/phx_events' Alpine.data('dropdown', dropdown) Alpine.data('comboBox', comboBox) @@ -30,14 +30,14 @@ if (document.querySelectorAll('[data-modal]').length > 0) { const triggers = document.querySelectorAll('[data-dropdown-trigger]') for (const trigger of triggers) { - trigger.addEventListener('click', function(e) { + trigger.addEventListener('click', function (e) { e.stopPropagation() e.currentTarget.nextElementSibling.classList.remove('hidden') }) } if (triggers.length > 0) { - document.addEventListener('click', function(e) { + document.addEventListener('click', function (e) { const dropdown = e.target.closest('[data-dropdown]') if (dropdown && e.target.tagName === 'A') { @@ -45,7 +45,7 @@ if (triggers.length > 0) { } }) - document.addEventListener('click', function(e) { + document.addEventListener('click', function (e) { const clickedInDropdown = e.target.closest('[data-dropdown]') if (!clickedInDropdown) { @@ -61,7 +61,9 @@ const changelogNotification = document.getElementById('changelog-notification') if (changelogNotification) { showChangelogNotification(changelogNotification) - fetch('https://plausible.io/changes.txt', { headers: { 'Content-Type': 'text/plain' } }) + fetch('https://plausible.io/changes.txt', { + headers: { 'Content-Type': 'text/plain' } + }) .then((res) => res.text()) .then((res) => { localStorage.lastChangelogUpdate = new Date(res).getTime() @@ -87,9 +89,11 @@ function showChangelogNotification(el) { ` const link = el.getElementsByTagName('a')[0] - link.addEventListener('click', function() { + link.addEventListener('click', function () { localStorage.lastChangelogClick = Date.now() - setTimeout(() => { link.remove() }, 100) + setTimeout(() => { + link.remove() + }, 100) }) } } @@ -97,7 +101,7 @@ function showChangelogNotification(el) { const embedButton = document.getElementById('generate-embed') if (embedButton) { - embedButton.addEventListener('click', function(_e) { + embedButton.addEventListener('click', function (_e) { const baseUrl = document.getElementById('base-url').value const embedCode = document.getElementById('embed-code') const theme = document.getElementById('theme').value.toLowerCase() @@ -116,7 +120,8 @@ if (embedButton) { ` } catch (e) { console.error(e) - embedCode.value = 'ERROR: Please enter a valid URL in the shared link field' + embedCode.value = + 'ERROR: Please enter a valid URL in the shared link field' } }) } diff --git a/assets/js/dashboard.tsx b/assets/js/dashboard.tsx index 5232786a1e..8232cb50ae 100644 --- a/assets/js/dashboard.tsx +++ b/assets/js/dashboard.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React, { ReactNode } from 'react' import { createRoot } from 'react-dom/client' import 'url-search-params-polyfill' diff --git a/assets/js/dashboard/api.ts b/assets/js/dashboard/api.ts index 85aebbfe48..570ffed152 100644 --- a/assets/js/dashboard/api.ts +++ b/assets/js/dashboard/api.ts @@ -1,4 +1,3 @@ -/** @format */ import { DashboardQuery } from './query' import { formatISO } from './util/date' import { serializeApiFilters } from './util/filters' diff --git a/assets/js/dashboard/components/combobox.js b/assets/js/dashboard/components/combobox.js index 2a194185a3..c414ad8c88 100644 --- a/assets/js/dashboard/components/combobox.js +++ b/assets/js/dashboard/components/combobox.js @@ -1,5 +1,3 @@ -/** @format */ - import React, { Fragment, useState, diff --git a/assets/js/dashboard/components/drilldown-link.tsx b/assets/js/dashboard/components/drilldown-link.tsx index 76d218ba5d..7e66ab643e 100644 --- a/assets/js/dashboard/components/drilldown-link.tsx +++ b/assets/js/dashboard/components/drilldown-link.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React from 'react' import { AppNavigationLink, diff --git a/assets/js/dashboard/components/error-panel.tsx b/assets/js/dashboard/components/error-panel.tsx index 39832ae115..f010aacc60 100644 --- a/assets/js/dashboard/components/error-panel.tsx +++ b/assets/js/dashboard/components/error-panel.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React from 'react' import classNames from 'classnames' import { diff --git a/assets/js/dashboard/components/filter-operator-selector.js b/assets/js/dashboard/components/filter-operator-selector.js index f9289d85d9..c7afb46526 100644 --- a/assets/js/dashboard/components/filter-operator-selector.js +++ b/assets/js/dashboard/components/filter-operator-selector.js @@ -1,5 +1,3 @@ -/** @format */ - import React, { Fragment, useRef } from 'react' import { diff --git a/assets/js/dashboard/components/lazy-loader.js b/assets/js/dashboard/components/lazy-loader.js index e0832c97db..774774c368 100644 --- a/assets/js/dashboard/components/lazy-loader.js +++ b/assets/js/dashboard/components/lazy-loader.js @@ -4,7 +4,7 @@ import { useInView } from 'react-intersection-observer' export default function LazyLoader(props) { const [hasBecomeVisibleYet, setHasBecomeVisibleYet] = useState(false) const { ref, inView } = useInView({ - threshold: 0, + threshold: 0 }) useEffect(() => { @@ -12,12 +12,8 @@ export default function LazyLoader(props) { setHasBecomeVisibleYet(true) if (props.onVisible) props.onVisible() } - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, [inView]) - return ( -
- {props.children} -
- ) + return
{props.children}
} diff --git a/assets/js/dashboard/components/notice.js b/assets/js/dashboard/components/notice.js index 9a90a16b90..1089f42579 100644 --- a/assets/js/dashboard/components/notice.js +++ b/assets/js/dashboard/components/notice.js @@ -1,27 +1,57 @@ -import React from "react" -import { sectionTitles } from "../stats/behaviours" +import React from 'react' +import { sectionTitles } from '../stats/behaviours' import * as api from '../api' -import { useSiteContext } from "../site-context" +import { useSiteContext } from '../site-context' -export function FeatureSetupNotice({ feature, title, info, callToAction, onHideAction }) { +export function FeatureSetupNotice({ + feature, + title, + info, + callToAction, + onHideAction +}) { const site = useSiteContext() const sectionTitle = sectionTitles[feature] const requestHideSection = () => { - if (window.confirm(`Are you sure you want to hide ${sectionTitle}? You can make it visible again in your site settings later.`)) { - api.mutation(`/api/${encodeURIComponent(site.domain)}/disable-feature`, { method: 'PUT', body: { feature: feature } }) + if ( + window.confirm( + `Are you sure you want to hide ${sectionTitle}? You can make it visible again in your site settings later.` + ) + ) { + api + .mutation(`/api/${encodeURIComponent(site.domain)}/disable-feature`, { + method: 'PUT', + body: { feature: feature } + }) .then(() => onHideAction()) - .catch((error) => {if (!(error instanceof api.ApiError)) {throw error}}) - + .catch((error) => { + if (!(error instanceof api.ApiError)) { + throw error + } + }) } } function renderCallToAction() { return ( -

{callToAction.action}

- - +

+ {callToAction.action} +

+ +
) @@ -31,14 +61,15 @@ export function FeatureSetupNotice({ feature, title, info, callToAction, onHideA return ( ) } return ( -
+
{title} @@ -55,4 +86,4 @@ export function FeatureSetupNotice({ feature, title, info, callToAction, onHideA
) -} \ No newline at end of file +} diff --git a/assets/js/dashboard/components/popover.tsx b/assets/js/dashboard/components/popover.tsx index 0d9d859dff..55738abb66 100644 --- a/assets/js/dashboard/components/popover.tsx +++ b/assets/js/dashboard/components/popover.tsx @@ -1,4 +1,3 @@ -/** @format */ import { TransitionClasses } from '@headlessui/react' import classNames from 'classnames' diff --git a/assets/js/dashboard/components/search-input.tsx b/assets/js/dashboard/components/search-input.tsx index 245dd8533e..50a1e204d6 100644 --- a/assets/js/dashboard/components/search-input.tsx +++ b/assets/js/dashboard/components/search-input.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React, { ChangeEventHandler, useCallback, useState, useRef } from 'react' import { isModifierPressed, Keybind } from '../keybinding' import { useDebounce } from '../custom-hooks' diff --git a/assets/js/dashboard/components/sort-button.tsx b/assets/js/dashboard/components/sort-button.tsx index 21cd21e80c..eaf5561933 100644 --- a/assets/js/dashboard/components/sort-button.tsx +++ b/assets/js/dashboard/components/sort-button.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React, { ReactNode } from 'react' import { cycleSortDirection, SortDirection } from '../hooks/use-order-by' import classNames from 'classnames' diff --git a/assets/js/dashboard/components/table.tsx b/assets/js/dashboard/components/table.tsx index d8486bae6a..d3b204093c 100644 --- a/assets/js/dashboard/components/table.tsx +++ b/assets/js/dashboard/components/table.tsx @@ -1,5 +1,3 @@ -/** @format */ - import classNames from 'classnames' import React, { ReactNode } from 'react' import { SortDirection } from '../hooks/use-order-by' diff --git a/assets/js/dashboard/custom-hooks.js b/assets/js/dashboard/custom-hooks.js index bcd53af9e1..08db25a195 100644 --- a/assets/js/dashboard/custom-hooks.js +++ b/assets/js/dashboard/custom-hooks.js @@ -1,4 +1,4 @@ -import { useEffect, useRef, useCallback } from 'react'; +import { useEffect, useRef, useCallback } from 'react' // A custom hook that behaves like `useEffect`, but // the function does not run on the initial render. @@ -22,15 +22,20 @@ export function useDebounce(fn, delay = DEBOUNCE_DELAY) { useEffect(() => { return () => { - if (timerRef.current) { clearTimeout(timerRef.current) } + if (timerRef.current) { + clearTimeout(timerRef.current) + } } }, []) - return useCallback((...args) => { - clearTimeout(timerRef.current) + return useCallback( + (...args) => { + clearTimeout(timerRef.current) - timerRef.current = setTimeout(() => { - fn(...args) - }, delay) - }, [fn, delay]) -} \ No newline at end of file + timerRef.current = setTimeout(() => { + fn(...args) + }, delay) + }, + [fn, delay] + ) +} diff --git a/assets/js/dashboard/dashboard-keybinds.tsx b/assets/js/dashboard/dashboard-keybinds.tsx index b4d3ceb468..ad4afee625 100644 --- a/assets/js/dashboard/dashboard-keybinds.tsx +++ b/assets/js/dashboard/dashboard-keybinds.tsx @@ -1,4 +1,3 @@ -/* @format */ import React from 'react' import { NavigateKeybind } from './keybinding' import { useRoutelessModalsContext } from './navigation/routeless-modals-context' diff --git a/assets/js/dashboard/error/error-boundary.test.tsx b/assets/js/dashboard/error/error-boundary.test.tsx index b86980d1f1..37676a1cc7 100644 --- a/assets/js/dashboard/error/error-boundary.test.tsx +++ b/assets/js/dashboard/error/error-boundary.test.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React, { useState } from 'react' import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' diff --git a/assets/js/dashboard/error/error-boundary.tsx b/assets/js/dashboard/error/error-boundary.tsx index 914b33e20b..e116401b00 100644 --- a/assets/js/dashboard/error/error-boundary.tsx +++ b/assets/js/dashboard/error/error-boundary.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React, { ReactNode, ReactElement } from 'react' type ErrorBoundaryProps = { diff --git a/assets/js/dashboard/error/something-went-wrong.test.tsx b/assets/js/dashboard/error/something-went-wrong.test.tsx index 387286d6c3..e5ee0d0a72 100644 --- a/assets/js/dashboard/error/something-went-wrong.test.tsx +++ b/assets/js/dashboard/error/something-went-wrong.test.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React from 'react' import { render, screen } from '@testing-library/react' import { GoToSites, SomethingWentWrongMessage } from './something-went-wrong' diff --git a/assets/js/dashboard/error/something-went-wrong.tsx b/assets/js/dashboard/error/something-went-wrong.tsx index 436ab8a70b..252b6da2f1 100644 --- a/assets/js/dashboard/error/something-went-wrong.tsx +++ b/assets/js/dashboard/error/something-went-wrong.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React, { ReactNode } from 'react' import RocketIcon from '../stats/modals/rocket-icon' import { useInRouterContext } from 'react-router-dom' diff --git a/assets/js/dashboard/extra/funnel-tooltip.js b/assets/js/dashboard/extra/funnel-tooltip.js index 296d28d456..0ad20afb95 100644 --- a/assets/js/dashboard/extra/funnel-tooltip.js +++ b/assets/js/dashboard/extra/funnel-tooltip.js @@ -2,7 +2,7 @@ export default function FunnelTooltip(palette, funnel) { return (context) => { const tooltipModel = context.tooltip const dataIndex = tooltipModel.dataPoints[0].dataIndex - const offset = document.getElementById("funnel").getBoundingClientRect() + const offset = document.getElementById('funnel').getBoundingClientRect() let tooltipEl = document.getElementById('chartjs-tooltip') if (!tooltipEl) { @@ -14,7 +14,8 @@ export default function FunnelTooltip(palette, funnel) { } if (tooltipEl && offset && window.innerWidth < 768) { - tooltipEl.style.top = offset.y + offset.height + window.scrollY + 15 + 'px' + tooltipEl.style.top = + offset.y + offset.height + window.scrollY + 15 + 'px' tooltipEl.style.left = offset.x + 'px' tooltipEl.style.right = null tooltipEl.style.opacity = 1 @@ -25,15 +26,14 @@ export default function FunnelTooltip(palette, funnel) { return } - if (tooltipModel.body) { const currentStep = funnel.steps[dataIndex] - const previousStep = (dataIndex > 0) ? funnel.steps[dataIndex - 1] : null + const previousStep = dataIndex > 0 ? funnel.steps[dataIndex - 1] : null tooltipEl.innerHTML = `
- - {funnel.steps.map(renderBar)} - + {funnel.steps.map(renderBar)} ) } @@ -358,7 +385,9 @@ export default function Funnel({ funnelName, tabs }) { setVisible(true)}> {renderInner()} - {!isSmallScreen && } + {!isSmallScreen && ( + + )}
) } diff --git a/assets/js/dashboard/fade-in.js b/assets/js/dashboard/fade-in.js index 021ed8bf19..65fe4d6e58 100644 --- a/assets/js/dashboard/fade-in.js +++ b/assets/js/dashboard/fade-in.js @@ -1,11 +1,11 @@ -import React from 'react'; +import React from 'react' -export default function FadeIn({className, show, children}) { +export default function FadeIn({ className, show, children }) { return (
{children}
-) + ) } diff --git a/assets/js/dashboard/filtering/segments-context.test.tsx b/assets/js/dashboard/filtering/segments-context.test.tsx index af3309955a..01889aa5d3 100644 --- a/assets/js/dashboard/filtering/segments-context.test.tsx +++ b/assets/js/dashboard/filtering/segments-context.test.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React from 'react' import { render, screen, fireEvent } from '@testing-library/react' import { SegmentsContextProvider, useSegmentsContext } from './segments-context' diff --git a/assets/js/dashboard/filtering/segments-context.tsx b/assets/js/dashboard/filtering/segments-context.tsx index 0037aab22b..cbc15233e3 100644 --- a/assets/js/dashboard/filtering/segments-context.tsx +++ b/assets/js/dashboard/filtering/segments-context.tsx @@ -1,4 +1,3 @@ -/** @format */ import React, { createContext, ReactNode, diff --git a/assets/js/dashboard/filtering/segments.test.ts b/assets/js/dashboard/filtering/segments.test.ts index d896fefeca..c8c3ba602a 100644 --- a/assets/js/dashboard/filtering/segments.test.ts +++ b/assets/js/dashboard/filtering/segments.test.ts @@ -1,5 +1,3 @@ -/** @format */ - import { remapToApiFilters } from '../util/filters' import { formatSegmentIdAsLabelKey, diff --git a/assets/js/dashboard/filtering/segments.ts b/assets/js/dashboard/filtering/segments.ts index 37750e5b41..791fc8e2a0 100644 --- a/assets/js/dashboard/filtering/segments.ts +++ b/assets/js/dashboard/filtering/segments.ts @@ -1,5 +1,3 @@ -/** @format */ - import { DashboardQuery, Filter } from '../query' import { cleanLabels, remapFromApiFilters } from '../util/filters' import { plainFilterText } from '../util/filter-text' diff --git a/assets/js/dashboard/hooks/api-client.ts b/assets/js/dashboard/hooks/api-client.ts index 628d43a19f..7db4063436 100644 --- a/assets/js/dashboard/hooks/api-client.ts +++ b/assets/js/dashboard/hooks/api-client.ts @@ -1,5 +1,3 @@ -/** @format */ - import { useEffect } from 'react' import { useQueryClient, diff --git a/assets/js/dashboard/hooks/use-order-by.test.ts b/assets/js/dashboard/hooks/use-order-by.test.ts index 71ba2f5743..40e63fcf42 100644 --- a/assets/js/dashboard/hooks/use-order-by.test.ts +++ b/assets/js/dashboard/hooks/use-order-by.test.ts @@ -1,5 +1,3 @@ -/** @format */ - import { Metric } from '../stats/reports/metrics' import { OrderBy, diff --git a/assets/js/dashboard/hooks/use-order-by.ts b/assets/js/dashboard/hooks/use-order-by.ts index d9c2773b56..28bde044af 100644 --- a/assets/js/dashboard/hooks/use-order-by.ts +++ b/assets/js/dashboard/hooks/use-order-by.ts @@ -1,5 +1,3 @@ -/** @format */ - import { useCallback, useEffect, useMemo, useState } from 'react' import { Metric } from '../stats/reports/metrics' import { getDomainScopedStorageKey, getItem, setItem } from '../util/storage' diff --git a/assets/js/dashboard/hooks/use-previous.ts b/assets/js/dashboard/hooks/use-previous.ts index 84de4c36c6..24ab85ad42 100644 --- a/assets/js/dashboard/hooks/use-previous.ts +++ b/assets/js/dashboard/hooks/use-previous.ts @@ -1,15 +1,15 @@ -import { useRef, useEffect } from 'react'; +import { useRef, useEffect } from 'react' function usePrevious(value: T): T | undefined { - const ref = useRef(undefined); + const ref = useRef(undefined) useEffect(() => { // Update the ref with the current value after render - ref.current = value; - }, [value]); + ref.current = value + }, [value]) // Return the previous value - return ref.current; + return ref.current } -export default usePrevious; +export default usePrevious diff --git a/assets/js/dashboard/index.tsx b/assets/js/dashboard/index.tsx index a815451243..f4c51038cf 100644 --- a/assets/js/dashboard/index.tsx +++ b/assets/js/dashboard/index.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React, { useMemo, useState } from 'react' import VisitorGraph from './stats/graph/visitor-graph' import Sources from './stats/sources' diff --git a/assets/js/dashboard/keybinding.tsx b/assets/js/dashboard/keybinding.tsx index 52199ec27b..ddcc9e9db1 100644 --- a/assets/js/dashboard/keybinding.tsx +++ b/assets/js/dashboard/keybinding.tsx @@ -1,4 +1,3 @@ -/* @format */ import React, { ReactNode, RefObject, useCallback, useEffect } from 'react' import { AppNavigationTarget, diff --git a/assets/js/dashboard/last-load-context.tsx b/assets/js/dashboard/last-load-context.tsx index 65bf9595a3..0f043c9a30 100644 --- a/assets/js/dashboard/last-load-context.tsx +++ b/assets/js/dashboard/last-load-context.tsx @@ -1,4 +1,3 @@ -/* @format */ import React, { createContext, useEffect, diff --git a/assets/js/dashboard/nav-menu/filter-menu.tsx b/assets/js/dashboard/nav-menu/filter-menu.tsx index f4580d4712..50e9ee36b7 100644 --- a/assets/js/dashboard/nav-menu/filter-menu.tsx +++ b/assets/js/dashboard/nav-menu/filter-menu.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React, { useMemo, useRef } from 'react' import { FILTER_MODAL_TO_FILTER_GROUP, diff --git a/assets/js/dashboard/nav-menu/filter-pill.tsx b/assets/js/dashboard/nav-menu/filter-pill.tsx index 690477a44d..916441da6d 100644 --- a/assets/js/dashboard/nav-menu/filter-pill.tsx +++ b/assets/js/dashboard/nav-menu/filter-pill.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React, { ReactNode } from 'react' import { AppNavigationLink, diff --git a/assets/js/dashboard/nav-menu/filter-pills-list.tsx b/assets/js/dashboard/nav-menu/filter-pills-list.tsx index d501b290d0..746d5a908f 100644 --- a/assets/js/dashboard/nav-menu/filter-pills-list.tsx +++ b/assets/js/dashboard/nav-menu/filter-pills-list.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React, { DetailedHTMLProps, HTMLAttributes } from 'react' import { useQueryContext } from '../query-context' import { FilterPill, FilterPillProps } from './filter-pill' diff --git a/assets/js/dashboard/nav-menu/filters-bar.test.tsx b/assets/js/dashboard/nav-menu/filters-bar.test.tsx index e8d86e970b..3c08dc5de8 100644 --- a/assets/js/dashboard/nav-menu/filters-bar.test.tsx +++ b/assets/js/dashboard/nav-menu/filters-bar.test.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React from 'react' import { render, screen } from '../../../test-utils' import userEvent from '@testing-library/user-event' diff --git a/assets/js/dashboard/nav-menu/filters-bar.tsx b/assets/js/dashboard/nav-menu/filters-bar.tsx index 20ab9ef121..69baf56716 100644 --- a/assets/js/dashboard/nav-menu/filters-bar.tsx +++ b/assets/js/dashboard/nav-menu/filters-bar.tsx @@ -1,5 +1,3 @@ -/** @format */ - import { EllipsisHorizontalIcon } from '@heroicons/react/24/solid' import classNames from 'classnames' import React, { useRef, useState, useLayoutEffect, ReactNode } from 'react' diff --git a/assets/js/dashboard/nav-menu/nav-menu-components.tsx b/assets/js/dashboard/nav-menu/nav-menu-components.tsx index 71607ab147..2f82c63ff4 100644 --- a/assets/js/dashboard/nav-menu/nav-menu-components.tsx +++ b/assets/js/dashboard/nav-menu/nav-menu-components.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React from 'react' export const MenuSeparator = () => ( diff --git a/assets/js/dashboard/nav-menu/query-periods/comparison-period-menu.tsx b/assets/js/dashboard/nav-menu/query-periods/comparison-period-menu.tsx index 875a8a365d..5ca9d47492 100644 --- a/assets/js/dashboard/nav-menu/query-periods/comparison-period-menu.tsx +++ b/assets/js/dashboard/nav-menu/query-periods/comparison-period-menu.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React, { useRef } from 'react' import { clearedComparisonSearch } from '../../query' import classNames from 'classnames' diff --git a/assets/js/dashboard/nav-menu/query-periods/date-range-calendar.test.tsx b/assets/js/dashboard/nav-menu/query-periods/date-range-calendar.test.tsx index bb33ed3e2c..6d16e8560a 100644 --- a/assets/js/dashboard/nav-menu/query-periods/date-range-calendar.test.tsx +++ b/assets/js/dashboard/nav-menu/query-periods/date-range-calendar.test.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React from 'react' import { render, screen } from '@testing-library/react' import { DateRangeCalendar } from './date-range-calendar' diff --git a/assets/js/dashboard/nav-menu/query-periods/date-range-calendar.tsx b/assets/js/dashboard/nav-menu/query-periods/date-range-calendar.tsx index 88be90ea10..b7da0621ee 100644 --- a/assets/js/dashboard/nav-menu/query-periods/date-range-calendar.tsx +++ b/assets/js/dashboard/nav-menu/query-periods/date-range-calendar.tsx @@ -1,4 +1,3 @@ -/* @format */ import React, { useLayoutEffect, useRef } from 'react' import DatePicker from 'react-flatpickr' diff --git a/assets/js/dashboard/nav-menu/query-periods/move-period-arrows.tsx b/assets/js/dashboard/nav-menu/query-periods/move-period-arrows.tsx index 7df7613699..78a73ee8a7 100644 --- a/assets/js/dashboard/nav-menu/query-periods/move-period-arrows.tsx +++ b/assets/js/dashboard/nav-menu/query-periods/move-period-arrows.tsx @@ -1,4 +1,3 @@ -/* @format */ import React, { useMemo } from 'react' import { shiftQueryPeriod, getDateForShiftedPeriod } from '../../query' import classNames from 'classnames' diff --git a/assets/js/dashboard/nav-menu/query-periods/query-dates.test.tsx b/assets/js/dashboard/nav-menu/query-periods/query-dates.test.tsx index ee0581259c..116d078fda 100644 --- a/assets/js/dashboard/nav-menu/query-periods/query-dates.test.tsx +++ b/assets/js/dashboard/nav-menu/query-periods/query-dates.test.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React from 'react' import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' diff --git a/assets/js/dashboard/nav-menu/query-periods/query-period-menu.tsx b/assets/js/dashboard/nav-menu/query-periods/query-period-menu.tsx index c4bfb342dc..6f6ad86979 100644 --- a/assets/js/dashboard/nav-menu/query-periods/query-period-menu.tsx +++ b/assets/js/dashboard/nav-menu/query-periods/query-period-menu.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React, { useMemo, useRef } from 'react' import classNames from 'classnames' import { useQueryContext } from '../../query-context' diff --git a/assets/js/dashboard/nav-menu/query-periods/query-periods-picker.tsx b/assets/js/dashboard/nav-menu/query-periods/query-periods-picker.tsx index 684f4a12f5..ce5960368f 100644 --- a/assets/js/dashboard/nav-menu/query-periods/query-periods-picker.tsx +++ b/assets/js/dashboard/nav-menu/query-periods/query-periods-picker.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React, { useRef } from 'react' import classNames from 'classnames' import { useQueryContext } from '../../query-context' diff --git a/assets/js/dashboard/nav-menu/query-periods/shared-menu-items.tsx b/assets/js/dashboard/nav-menu/query-periods/shared-menu-items.tsx index a7844091d6..813e188d09 100644 --- a/assets/js/dashboard/nav-menu/query-periods/shared-menu-items.tsx +++ b/assets/js/dashboard/nav-menu/query-periods/shared-menu-items.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React, { ReactNode, RefObject } from 'react' import classNames from 'classnames' import { popover } from '../../components/popover' diff --git a/assets/js/dashboard/nav-menu/segments/searchable-segments-section.tsx b/assets/js/dashboard/nav-menu/segments/searchable-segments-section.tsx index d2dfb30135..8985370288 100644 --- a/assets/js/dashboard/nav-menu/segments/searchable-segments-section.tsx +++ b/assets/js/dashboard/nav-menu/segments/searchable-segments-section.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React, { useEffect, useState } from 'react' import { useQueryContext } from '../../query-context' import { useSiteContext } from '../../site-context' diff --git a/assets/js/dashboard/nav-menu/segments/segment-menu.tsx b/assets/js/dashboard/nav-menu/segments/segment-menu.tsx index 187cc7441a..b52d9eb515 100644 --- a/assets/js/dashboard/nav-menu/segments/segment-menu.tsx +++ b/assets/js/dashboard/nav-menu/segments/segment-menu.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React, { useEffect } from 'react' import classNames from 'classnames' import { Popover, Transition } from '@headlessui/react' diff --git a/assets/js/dashboard/nav-menu/top-bar.test.tsx b/assets/js/dashboard/nav-menu/top-bar.test.tsx index 762e080c35..735f9feb2e 100644 --- a/assets/js/dashboard/nav-menu/top-bar.test.tsx +++ b/assets/js/dashboard/nav-menu/top-bar.test.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React from 'react' import { render, diff --git a/assets/js/dashboard/nav-menu/top-bar.tsx b/assets/js/dashboard/nav-menu/top-bar.tsx index 3116e6879e..5aef3e0deb 100644 --- a/assets/js/dashboard/nav-menu/top-bar.tsx +++ b/assets/js/dashboard/nav-menu/top-bar.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React, { ReactNode, useRef } from 'react' import SiteSwitcher from '../site-switcher' import { useSiteContext } from '../site-context' diff --git a/assets/js/dashboard/navigation/routeless-modals-context.tsx b/assets/js/dashboard/navigation/routeless-modals-context.tsx index 32e43aef35..a88d5a0449 100644 --- a/assets/js/dashboard/navigation/routeless-modals-context.tsx +++ b/assets/js/dashboard/navigation/routeless-modals-context.tsx @@ -1,4 +1,3 @@ -/* @format */ import React, { createContext, ReactNode, useContext, useState } from 'react' import { RoutelessSegmentModal } from '../segments/routeless-segment-modals' diff --git a/assets/js/dashboard/navigation/use-app-navigate.tsx b/assets/js/dashboard/navigation/use-app-navigate.tsx index d809404c93..fa2fe9257f 100644 --- a/assets/js/dashboard/navigation/use-app-navigate.tsx +++ b/assets/js/dashboard/navigation/use-app-navigate.tsx @@ -1,4 +1,3 @@ -/* @format */ import React, { forwardRef, useCallback } from 'react' import { Link, diff --git a/assets/js/dashboard/navigation/use-definite-location-state.tsx b/assets/js/dashboard/navigation/use-definite-location-state.tsx index db66d6e5f5..c39fefbdf0 100644 --- a/assets/js/dashboard/navigation/use-definite-location-state.tsx +++ b/assets/js/dashboard/navigation/use-definite-location-state.tsx @@ -1,5 +1,3 @@ -/** @format */ - import { useEffect, useMemo, useState } from 'react' import { useLocation } from 'react-router-dom' import { useAppNavigate } from './use-app-navigate' diff --git a/assets/js/dashboard/query-context.tsx b/assets/js/dashboard/query-context.tsx index fb0f37cc0d..27144258db 100644 --- a/assets/js/dashboard/query-context.tsx +++ b/assets/js/dashboard/query-context.tsx @@ -1,4 +1,3 @@ -/* @format */ import React, { createContext, useMemo, useContext, ReactNode } from 'react' import { useLocation } from 'react-router' import { useMountedEffect } from './custom-hooks' diff --git a/assets/js/dashboard/query-time-periods.test.ts b/assets/js/dashboard/query-time-periods.test.ts index a8244c6b0b..b002bc2dbe 100644 --- a/assets/js/dashboard/query-time-periods.test.ts +++ b/assets/js/dashboard/query-time-periods.test.ts @@ -1,5 +1,3 @@ -/** @format */ - import { ComparisonMode, getDashboardTimeSettings, diff --git a/assets/js/dashboard/query-time-periods.ts b/assets/js/dashboard/query-time-periods.ts index cdc2d25fb8..5463dc482b 100644 --- a/assets/js/dashboard/query-time-periods.ts +++ b/assets/js/dashboard/query-time-periods.ts @@ -1,4 +1,3 @@ -/* @format */ import { useEffect } from 'react' import { clearedComparisonSearch, diff --git a/assets/js/dashboard/query.ts b/assets/js/dashboard/query.ts index ab0154d42a..b76951ff50 100644 --- a/assets/js/dashboard/query.ts +++ b/assets/js/dashboard/query.ts @@ -1,5 +1,3 @@ -/** @format */ - import { nowForSite, formatISO, diff --git a/assets/js/dashboard/router.tsx b/assets/js/dashboard/router.tsx index 95136faed7..eff35b3e73 100644 --- a/assets/js/dashboard/router.tsx +++ b/assets/js/dashboard/router.tsx @@ -1,4 +1,3 @@ -/** @format */ import React from 'react' import { createBrowserRouter, Outlet, useRouteError } from 'react-router-dom' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' diff --git a/assets/js/dashboard/segments/routeless-segment-modals.tsx b/assets/js/dashboard/segments/routeless-segment-modals.tsx index f99a129247..6053bc9692 100644 --- a/assets/js/dashboard/segments/routeless-segment-modals.tsx +++ b/assets/js/dashboard/segments/routeless-segment-modals.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React from 'react' import { CreateSegmentModal, diff --git a/assets/js/dashboard/segments/segment-authorship.test.tsx b/assets/js/dashboard/segments/segment-authorship.test.tsx index af7eb65ba0..a366ecd6d6 100644 --- a/assets/js/dashboard/segments/segment-authorship.test.tsx +++ b/assets/js/dashboard/segments/segment-authorship.test.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React from 'react' import { render, screen } from '@testing-library/react' import { SegmentAuthorship } from './segment-authorship' diff --git a/assets/js/dashboard/segments/segment-authorship.tsx b/assets/js/dashboard/segments/segment-authorship.tsx index 605cc2bb3d..b3b561022a 100644 --- a/assets/js/dashboard/segments/segment-authorship.tsx +++ b/assets/js/dashboard/segments/segment-authorship.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React from 'react' import { SavedSegmentPublic, SavedSegment } from '../filtering/segments' import { dateForSite, formatDayShort } from '../util/date' diff --git a/assets/js/dashboard/segments/segment-modals.test.tsx b/assets/js/dashboard/segments/segment-modals.test.tsx index cf0b7f636d..3adc096d31 100644 --- a/assets/js/dashboard/segments/segment-modals.test.tsx +++ b/assets/js/dashboard/segments/segment-modals.test.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React from 'react' import { render, screen } from '@testing-library/react' import { SegmentModal } from './segment-modals' diff --git a/assets/js/dashboard/segments/segment-modals.tsx b/assets/js/dashboard/segments/segment-modals.tsx index a1c372fb82..9714e07470 100644 --- a/assets/js/dashboard/segments/segment-modals.tsx +++ b/assets/js/dashboard/segments/segment-modals.tsx @@ -1,5 +1,3 @@ -/** @format */ - import React, { ReactNode, useState } from 'react' import ModalWithRouting from '../stats/modals/modal' import { diff --git a/assets/js/dashboard/site-context.test.tsx b/assets/js/dashboard/site-context.test.tsx index 71309c173f..58fe05213c 100644 --- a/assets/js/dashboard/site-context.test.tsx +++ b/assets/js/dashboard/site-context.test.tsx @@ -1,4 +1,3 @@ -/** @format */ import React, { HTMLAttributes } from 'react' import { render, screen } from '@testing-library/react' import { parseSiteFromDataset, PlausibleSite } from './site-context' diff --git a/assets/js/dashboard/site-context.tsx b/assets/js/dashboard/site-context.tsx index 406c4a1763..56c97dc6dd 100644 --- a/assets/js/dashboard/site-context.tsx +++ b/assets/js/dashboard/site-context.tsx @@ -1,4 +1,3 @@ -/** @format */ import React, { createContext, ReactNode, useContext } from 'react' export function parseSiteFromDataset(dataset: DOMStringMap): PlausibleSite { diff --git a/assets/js/dashboard/stats/bar.js b/assets/js/dashboard/stats/bar.js index 6146e682c1..b6848a0ba3 100644 --- a/assets/js/dashboard/stats/bar.js +++ b/assets/js/dashboard/stats/bar.js @@ -1,29 +1,34 @@ -import React from 'react'; +import React from 'react' function barWidth(count, all, plot) { - let maxVal = all[0][plot]; + let maxVal = all[0][plot] for (const val of all) { if (val > maxVal) maxVal = val[plot] } - return count / maxVal * 100 + return (count / maxVal) * 100 } -export default function Bar({count, all, bg, maxWidthDeduction, children, plot = "visitors"}) { +export default function Bar({ + count, + all, + bg, + maxWidthDeduction, + children, + plot = 'visitors' +}) { const width = barWidth(count, all, plot) - const style = maxWidthDeduction ? {maxWidth: `calc(100% - ${maxWidthDeduction})`} : {} + const style = maxWidthDeduction + ? { maxWidth: `calc(100% - ${maxWidthDeduction})` } + : {} return ( -
+
-
+ style={{ width: `${width}%` }} + >
{children}
) diff --git a/assets/js/dashboard/stats/behaviours/conversions.js b/assets/js/dashboard/stats/behaviours/conversions.js index 5e32b00e65..37c00517ae 100644 --- a/assets/js/dashboard/stats/behaviours/conversions.js +++ b/assets/js/dashboard/stats/behaviours/conversions.js @@ -1,15 +1,15 @@ -import React from 'react'; -import * as api from '../../api'; -import * as url from '../../util/url'; +import React from 'react' +import * as api from '../../api' +import * as url from '../../util/url' -import * as metrics from '../reports/metrics'; -import ListReport from '../reports/list'; -import { useSiteContext } from '../../site-context'; -import { useQueryContext } from '../../query-context'; -import { conversionsRoute } from '../../router'; +import * as metrics from '../reports/metrics' +import ListReport from '../reports/list' +import { useSiteContext } from '../../site-context' +import { useQueryContext } from '../../query-context' +import { conversionsRoute } from '../../router' export default function Conversions({ afterFetchData, onGoalFilterClick }) { - const site = useSiteContext(); + const site = useSiteContext() const { query } = useQueryContext() function fetchConversions() { @@ -18,19 +18,27 @@ export default function Conversions({ afterFetchData, onGoalFilterClick }) { function getFilterInfo(listItem) { return { - prefix: "goal", - filter: ["is", "goal", [listItem.name]], + prefix: 'goal', + filter: ['is', 'goal', [listItem.name]] } } function chooseMetrics() { return [ - metrics.createVisitors({ renderLabel: (_query) => "Uniques", meta: { plot: true } }), - metrics.createEvents({ renderLabel: (_query) => "Total", meta: { hiddenOnMobile: true } }), + metrics.createVisitors({ + renderLabel: (_query) => 'Uniques', + meta: { plot: true } + }), + metrics.createEvents({ + renderLabel: (_query) => 'Total', + meta: { hiddenOnMobile: true } + }), metrics.createConversionRate(), - BUILD_EXTRA && metrics.createTotalRevenue({ meta: { hiddenOnMobile: true } }), - BUILD_EXTRA && metrics.createAverageRevenue({ meta: { hiddenOnMobile: true } }) - ].filter(metric => !!metric) + BUILD_EXTRA && + metrics.createTotalRevenue({ meta: { hiddenOnMobile: true } }), + BUILD_EXTRA && + metrics.createAverageRevenue({ meta: { hiddenOnMobile: true } }) + ].filter((metric) => !!metric) } /*global BUILD_EXTRA*/ @@ -42,7 +50,10 @@ export default function Conversions({ afterFetchData, onGoalFilterClick }) { keyLabel="Goal" onClick={onGoalFilterClick} metrics={chooseMetrics()} - detailsLinkProps={{ path: conversionsRoute.path, search: (search) => search }} + detailsLinkProps={{ + path: conversionsRoute.path, + search: (search) => search + }} color="bg-red-50" colMinWidth={90} /> diff --git a/assets/js/dashboard/stats/behaviours/goal-conversions.js b/assets/js/dashboard/stats/behaviours/goal-conversions.js index 8350d1fc14..d2dda5cc92 100644 --- a/assets/js/dashboard/stats/behaviours/goal-conversions.js +++ b/assets/js/dashboard/stats/behaviours/goal-conversions.js @@ -1,21 +1,28 @@ -import React from "react" +import React from 'react' import Conversions from './conversions' -import ListReport from "../reports/list" +import ListReport from '../reports/list' import * as metrics from '../reports/metrics' -import * as url from "../../util/url" -import * as api from "../../api" -import { EVENT_PROPS_PREFIX, getGoalFilter, FILTER_OPERATIONS } from "../../util/filters" -import { useSiteContext } from "../../site-context" -import { useQueryContext } from "../../query-context" -import { customPropsRoute } from "../../router" +import * as url from '../../util/url' +import * as api from '../../api' +import { + EVENT_PROPS_PREFIX, + getGoalFilter, + FILTER_OPERATIONS +} from '../../util/filters' +import { useSiteContext } from '../../site-context' +import { useQueryContext } from '../../query-context' +import { customPropsRoute } from '../../router' export const SPECIAL_GOALS = { - '404': { title: '404 Pages', prop: 'path' }, + 404: { title: '404 Pages', prop: 'path' }, 'Outbound Link: Click': { title: 'Outbound Links', prop: 'url' }, 'Cloaked Link: Click': { title: 'Cloaked Links', prop: 'url' }, 'File Download': { title: 'File Downloads', prop: 'url' }, - 'WP Search Queries': { title: 'WordPress Search Queries', prop: 'search_query' }, - 'WP Form Completions': { title: 'WordPress Form Completions', prop: 'path' }, + 'WP Search Queries': { + title: 'WordPress Search Queries', + prop: 'search_query' + }, + 'WP Form Completions': { title: 'WordPress Form Completions', prop: 'path' } } function getSpecialGoal(query) { @@ -28,7 +35,6 @@ function getSpecialGoal(query) { return SPECIAL_GOALS[clauses[0]] || null } return null - } export function specialTitleWhenGoalFilter(query, defaultTitle) { @@ -36,8 +42,8 @@ export function specialTitleWhenGoalFilter(query, defaultTitle) { } function SpecialPropBreakdown({ prop, afterFetchData }) { - const site = useSiteContext(); - const { query } = useQueryContext(); + const site = useSiteContext() + const { query } = useQueryContext() function fetchData() { return api.get(url.apiPath(site, `/custom-prop-values/${prop}`), query) @@ -54,16 +60,22 @@ function SpecialPropBreakdown({ prop, afterFetchData }) { function getFilterInfo(listItem) { return { prefix: EVENT_PROPS_PREFIX, - filter: ["is", `${EVENT_PROPS_PREFIX}${prop}`, [listItem['name']]] + filter: ['is', `${EVENT_PROPS_PREFIX}${prop}`, [listItem['name']]] } } function chooseMetrics() { return [ - metrics.createVisitors({ renderLabel: (_query) => "Visitors", meta: { plot: true } }), - metrics.createEvents({ renderLabel: (_query) => "Events", meta: { hiddenOnMobile: true } }), + metrics.createVisitors({ + renderLabel: (_query) => 'Visitors', + meta: { plot: true } + }), + metrics.createEvents({ + renderLabel: (_query) => 'Events', + meta: { hiddenOnMobile: true } + }), metrics.createConversionRate() - ].filter(metric => !!metric) + ].filter((metric) => !!metric) } return ( @@ -73,7 +85,11 @@ function SpecialPropBreakdown({ prop, afterFetchData }) { getFilterInfo={getFilterInfo} keyLabel={prop} metrics={chooseMetrics()} - detailsLinkProps={{ path: customPropsRoute.path, params: {propKey: url.maybeEncodeRouteParam(prop)}, search: (search) => search }} + detailsLinkProps={{ + path: customPropsRoute.path, + params: { propKey: url.maybeEncodeRouteParam(prop) }, + search: (search) => search + }} getExternalLinkUrl={getExternalLinkUrlFactory()} maybeHideDetails={true} color="bg-red-50" @@ -87,8 +103,18 @@ export default function GoalConversions({ afterFetchData, onGoalFilterClick }) { const specialGoal = getSpecialGoal(query) if (specialGoal) { - return + return ( + + ) } else { - return + return ( + + ) } } diff --git a/assets/js/dashboard/stats/behaviours/index.js b/assets/js/dashboard/stats/behaviours/index.js index 72d7cccd9f..43d8b8c74d 100644 --- a/assets/js/dashboard/stats/behaviours/index.js +++ b/assets/js/dashboard/stats/behaviours/index.js @@ -1,10 +1,19 @@ -import React, { Fragment, useState, useEffect, useCallback, useRef } from 'react' +import React, { + Fragment, + useState, + useEffect, + useCallback, + useRef +} from 'react' import { Menu, Transition } from '@headlessui/react' import { ChevronDownIcon } from '@heroicons/react/20/solid' import classNames from 'classnames' import * as storage from '../../util/storage' import ImportedQueryUnsupportedWarning from '../imported-query-unsupported-warning' -import GoalConversions, { specialTitleWhenGoalFilter, SPECIAL_GOALS } from './goal-conversions' +import GoalConversions, { + specialTitleWhenGoalFilter, + SPECIAL_GOALS +} from './goal-conversions' import Properties from './props' import { FeatureSetupNotice } from '../../components/notice' import { hasConversionGoalFilter } from '../../util/filters' @@ -26,7 +35,8 @@ function maybeRequire() { const Funnel = maybeRequire().default -const ACTIVE_CLASS = 'inline-block h-5 text-indigo-700 dark:text-indigo-500 font-bold active-prop-heading truncate text-left' +const ACTIVE_CLASS = + 'inline-block h-5 text-indigo-700 dark:text-indigo-500 font-bold active-prop-heading truncate text-left' const DEFAULT_CLASS = 'hover:text-indigo-600 cursor-pointer truncate text-left' export const CONVERSIONS = 'conversions' @@ -40,21 +50,29 @@ export const sectionTitles = { } export default function Behaviours({ importedDataInView }) { - const { query } = useQueryContext(); - const site = useSiteContext(); - const user = useUserContext(); - const buttonRef = useRef(); - const adminAccess = ['owner', 'admin', 'editor', 'super_admin'].includes(user.role) + const { query } = useQueryContext() + const site = useSiteContext() + const user = useUserContext() + const buttonRef = useRef() + const adminAccess = ['owner', 'admin', 'editor', 'super_admin'].includes( + user.role + ) const tabKey = storage.getDomainScopedStorageKey('behavioursTab', site.domain) - const funnelKey = storage.getDomainScopedStorageKey('behavioursTabFunnel', site.domain) + const funnelKey = storage.getDomainScopedStorageKey( + 'behavioursTabFunnel', + site.domain + ) const [enabledModes, setEnabledModes] = useState(getEnabledModes()) const [mode, setMode] = useState(defaultMode()) const [loading, setLoading] = useState(true) - const [funnelNames, _setFunnelNames] = useState(site.funnels.map(({ name }) => name)) + const [funnelNames, _setFunnelNames] = useState( + site.funnels.map(({ name }) => name) + ) const [selectedFunnel, setSelectedFunnel] = useState(defaultSelectedFunnel()) - const [showingPropsForGoalFilter, setShowingPropsForGoalFilter] = useState(false) + const [showingPropsForGoalFilter, setShowingPropsForGoalFilter] = + useState(false) const [skipImportedReason, setSkipImportedReason] = useState(null) @@ -63,11 +81,16 @@ export default function Behaviours({ importedDataInView }) { const isSpecialGoal = Object.keys(SPECIAL_GOALS).includes(goalName) const isPageviewGoal = goalName.startsWith('Visit ') - if (!isSpecialGoal && !isPageviewGoal && enabledModes.includes(PROPS) && site.hasProps) { + if ( + !isSpecialGoal && + !isPageviewGoal && + enabledModes.includes(PROPS) && + site.hasProps + ) { setShowingPropsForGoalFilter(true) setMode(PROPS) } - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, []) useEffect(() => { @@ -76,18 +99,22 @@ export default function Behaviours({ importedDataInView }) { setShowingPropsForGoalFilter(false) setMode(CONVERSIONS) } - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, [hasConversionGoalFilter(query)]) useEffect(() => { setMode(defaultMode()) - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, [enabledModes]) useEffect(() => setLoading(true), [query, mode]) function disableMode(mode) { - setEnabledModes(enabledModes.filter((m) => { return m !== mode })) + setEnabledModes( + enabledModes.filter((m) => { + return m !== mode + }) + ) } function setFunnel(selectedFunnel) { @@ -118,52 +145,69 @@ export default function Behaviours({ importedDataInView }) { } function tabFunnelPicker() { - return - -
- - Funnels - -
+ return ( + + +
+ + + Funnels + + +
- - -
- {funnelNames.map((funnelName) => { - return ( - - {({ active }) => ( - - {funnelName} - - )} - - ) - })} -
-
-
-
+ + +
+ {funnelNames.map((funnelName) => { + return ( + + {({ active }) => ( + + {funnelName} + + )} + + ) + })} +
+
+
+
+ ) } function tabSwitcher(toMode, displayName) { - const className = classNames({ [ACTIVE_CLASS]: mode == toMode, [DEFAULT_CLASS]: mode !== toMode }) + const className = classNames({ + [ACTIVE_CLASS]: mode == toMode, + [DEFAULT_CLASS]: mode !== toMode + }) const setTab = () => { storage.setItem(tabKey, toMode) setMode(toMode) @@ -181,7 +225,9 @@ export default function Behaviours({ importedDataInView }) {
{isEnabled(CONVERSIONS) && tabSwitcher(CONVERSIONS, 'Goals')} {isEnabled(PROPS) && tabSwitcher(PROPS, 'Properties')} - {isEnabled(FUNNELS) && Funnel && (hasFunnels() ? tabFunnelPicker() : tabSwitcher(FUNNELS, 'Funnels'))} + {isEnabled(FUNNELS) && + Funnel && + (hasFunnels() ? tabFunnelPicker() : tabSwitcher(FUNNELS, 'Funnels'))}
) } @@ -193,37 +239,45 @@ export default function Behaviours({ importedDataInView }) { function renderConversions() { if (site.hasGoals) { - return - } - else if (adminAccess) { + return ( + + ) + } else if (adminAccess) { return ( ) + } else { + return noDataYet() } - else { return noDataYet() } } function renderFunnels() { if (Funnel === null) { return featureUnavailable() - } - else if (Funnel && selectedFunnel && site.funnelsAvailable) { + } else if (Funnel && selectedFunnel && site.funnelsAvailable) { return - } - else if (Funnel && adminAccess) { + } else if (Funnel && adminAccess) { let callToAction if (site.funnelsAvailable) { - callToAction = { action: 'Set up funnels', link: `/${encodeURIComponent(site.domain)}/settings/funnels` } + callToAction = { + action: 'Set up funnels', + link: `/${encodeURIComponent(site.domain)}/settings/funnels` + } } else { callToAction = { action: 'Upgrade', link: '/billing/choose-plan' } } @@ -232,13 +286,16 @@ export default function Behaviours({ importedDataInView }) { ) + } else { + return noDataYet() } - else { return noDataYet() } } function renderProps() { @@ -248,7 +305,10 @@ export default function Behaviours({ importedDataInView }) { let callToAction if (site.propsAvailable) { - callToAction = { action: 'Set up props', link: `/${encodeURIComponent(site.domain)}/settings/properties` } + callToAction = { + action: 'Set up props', + link: `/${encodeURIComponent(site.domain)}/settings/properties` + } } else { callToAction = { action: 'Upgrade', link: '/billing/choose-plan' } } @@ -257,12 +317,16 @@ export default function Behaviours({ importedDataInView }) { ) - } else { return noDataYet() } + } else { + return noDataYet() + } } function noDataYet() { @@ -282,7 +346,9 @@ export default function Behaviours({ importedDataInView }) { } function onHideAction(mode) { - return () => { disableMode(mode) } + return () => { + disableMode(mode) + } } function renderContent() { @@ -297,13 +363,21 @@ export default function Behaviours({ importedDataInView }) { } function defaultMode() { - if (enabledModes.length === 0) { return null } + if (enabledModes.length === 0) { + return null + } const storedMode = storage.getItem(tabKey) - if (storedMode && enabledModes.includes(storedMode)) { return storedMode } + if (storedMode && enabledModes.includes(storedMode)) { + return storedMode + } - if (enabledModes.includes(CONVERSIONS)) { return CONVERSIONS } - if (enabledModes.includes(PROPS)) { return PROPS } + if (enabledModes.includes(CONVERSIONS)) { + return CONVERSIONS + } + if (enabledModes.includes(PROPS)) { + return PROPS + } return FUNNELS } @@ -317,7 +391,7 @@ export default function Behaviours({ importedDataInView }) { // If the feature is not supported by the site owner's subscription, // it only makes sense to display the feature tab to the owner itself // as only they can upgrade to make the feature available. - const callToActionIsMissing = !isAvailable && user.role !== 'owner' + const callToActionIsMissing = !isAvailable && user.role !== 'owner' if (!isOptedOut && !callToActionIsMissing) { enabledModes.push(feature) @@ -345,11 +419,27 @@ export default function Behaviours({ importedDataInView }) { function renderImportedQueryUnsupportedWarning() { if (mode === CONVERSIONS) { - return + return ( + + ) } else if (mode === PROPS) { - return + return ( + + ) } else { - return + return ( + + ) } } diff --git a/assets/js/dashboard/stats/behaviours/props.js b/assets/js/dashboard/stats/behaviours/props.js index f7625dc776..11ccdf7b5b 100644 --- a/assets/js/dashboard/stats/behaviours/props.js +++ b/assets/js/dashboard/stats/behaviours/props.js @@ -1,20 +1,24 @@ -import React, { useCallback, useEffect, useState } from "react"; -import ListReport, { MIN_HEIGHT } from "../reports/list"; -import Combobox from '../../components/combobox'; -import * as metrics from '../reports/metrics'; -import * as api from '../../api'; -import * as url from '../../util/url'; -import * as storage from "../../util/storage"; -import { EVENT_PROPS_PREFIX, getGoalFilter, FILTER_OPERATIONS, hasConversionGoalFilter } from "../../util/filters"; -import classNames from "classnames"; -import { useQueryContext } from "../../query-context"; -import { useSiteContext } from "../../site-context"; -import { customPropsRoute } from "../../router"; - +import React, { useCallback, useEffect, useState } from 'react' +import ListReport, { MIN_HEIGHT } from '../reports/list' +import Combobox from '../../components/combobox' +import * as metrics from '../reports/metrics' +import * as api from '../../api' +import * as url from '../../util/url' +import * as storage from '../../util/storage' +import { + EVENT_PROPS_PREFIX, + getGoalFilter, + FILTER_OPERATIONS, + hasConversionGoalFilter +} from '../../util/filters' +import classNames from 'classnames' +import { useQueryContext } from '../../query-context' +import { useSiteContext } from '../../site-context' +import { customPropsRoute } from '../../router' export default function Properties({ afterFetchData }) { - const { query } = useQueryContext(); - const site = useSiteContext(); + const { query } = useQueryContext() + const site = useSiteContext() const propKeyStorageName = `prop_key__${site.domain}` const propKeyStorageNameForGoal = () => { @@ -39,8 +43,8 @@ export default function Properties({ afterFetchData }) { setPropKeyLoading(true) setPropKey(null) - fetchPropKeyOptions()("").then((propKeys) => { - const propKeyValues = propKeys.map(entry => entry.value) + fetchPropKeyOptions()('').then((propKeys) => { + const propKeyValues = propKeys.map((entry) => entry.value) if (propKeyValues.length > 0) { const storedPropKey = getPropKeyFromStorage() @@ -54,35 +58,45 @@ export default function Properties({ afterFetchData }) { setPropKeyLoading(false) }) - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, [query]) function getPropKeyFromStorage() { if (singleGoalFilterApplied()) { const storedForGoal = storage.getItem(propKeyStorageNameForGoal()) - if (storedForGoal) { return storedForGoal } + if (storedForGoal) { + return storedForGoal + } } return storage.getItem(propKeyStorageName) } function fetchProps() { - return api.get(url.apiPath(site, `/custom-prop-values/${encodeURIComponent(propKey)}`), query) + return api.get( + url.apiPath(site, `/custom-prop-values/${encodeURIComponent(propKey)}`), + query + ) } const fetchPropKeyOptions = useCallback(() => { return (input) => { - return api.get(url.apiPath(site, "/suggestions/prop_key"), query, { q: input.trim() }) + return api.get(url.apiPath(site, '/suggestions/prop_key'), query, { + q: input.trim() + }) } - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, [query]) function onPropKeySelect() { return (selectedOptions) => { - const newPropKey = selectedOptions.length === 0 ? null : selectedOptions[0].value + const newPropKey = + selectedOptions.length === 0 ? null : selectedOptions[0].value if (newPropKey) { - const storageName = singleGoalFilterApplied() ? propKeyStorageNameForGoal() : propKeyStorageName + const storageName = singleGoalFilterApplied() + ? propKeyStorageNameForGoal() + : propKeyStorageName storage.setItem(storageName, newPropKey) } @@ -93,13 +107,21 @@ export default function Properties({ afterFetchData }) { /*global BUILD_EXTRA*/ function chooseMetrics() { return [ - metrics.createVisitors({ renderLabel: (_query) => "Visitors", meta: { plot: true } }), - metrics.createEvents({ renderLabel: (_query) => "Events", meta: { hiddenOnMobile: true } }), + metrics.createVisitors({ + renderLabel: (_query) => 'Visitors', + meta: { plot: true } + }), + metrics.createEvents({ + renderLabel: (_query) => 'Events', + meta: { hiddenOnMobile: true } + }), hasConversionGoalFilter(query) && metrics.createConversionRate(), !hasConversionGoalFilter(query) && metrics.createPercentage(), - BUILD_EXTRA && metrics.createTotalRevenue({ meta: { hiddenOnMobile: true } }), - BUILD_EXTRA && metrics.createAverageRevenue({ meta: { hiddenOnMobile: true } }) - ].filter(metric => !!metric) + BUILD_EXTRA && + metrics.createTotalRevenue({ meta: { hiddenOnMobile: true } }), + BUILD_EXTRA && + metrics.createAverageRevenue({ meta: { hiddenOnMobile: true } }) + ].filter((metric) => !!metric) } function renderBreakdown() { @@ -110,7 +132,11 @@ export default function Properties({ afterFetchData }) { getFilterInfo={getFilterInfo} keyLabel={propKey} metrics={chooseMetrics()} - detailsLinkProps={{ path: customPropsRoute.path, params: { propKey }, search: (search) => search }} + detailsLinkProps={{ + path: customPropsRoute.path, + params: { propKey }, + search: (search) => search + }} maybeHideDetails={true} color="bg-red-50" colMinWidth={90} @@ -120,22 +146,38 @@ export default function Properties({ afterFetchData }) { const getFilterInfo = (listItem) => ({ prefix: `${EVENT_PROPS_PREFIX}${propKey}`, - filter: ["is", `${EVENT_PROPS_PREFIX}${propKey}`, [listItem.name]] + filter: ['is', `${EVENT_PROPS_PREFIX}${propKey}`, [listItem.name]] }) const comboboxDisabled = !propKeyLoading && !propKey - const comboboxPlaceholder = comboboxDisabled ? 'No custom properties found' : '' + const comboboxPlaceholder = comboboxDisabled + ? 'No custom properties found' + : '' const comboboxValues = propKey ? [{ value: propKey, label: propKey }] : [] - const boxClass = classNames('pl-2 pr-8 py-1 bg-transparent dark:text-gray-300 rounded-md shadow-sm border border-gray-300 dark:border-gray-500', { - 'pointer-events-none': comboboxDisabled - }) + const boxClass = classNames( + 'pl-2 pr-8 py-1 bg-transparent dark:text-gray-300 rounded-md shadow-sm border border-gray-300 dark:border-gray-500', + { + 'pointer-events-none': comboboxDisabled + } + ) const COMBOBOX_HEIGHT = 40 return ( -
+
- +
{propKey && renderBreakdown()}
diff --git a/assets/js/dashboard/stats/current-visitors.js b/assets/js/dashboard/stats/current-visitors.js index e6c8985411..4df0bc23c4 100644 --- a/assets/js/dashboard/stats/current-visitors.js +++ b/assets/js/dashboard/stats/current-visitors.js @@ -1,5 +1,3 @@ -/** @format */ - import React, { useCallback, useEffect, useState } from 'react' import { AppNavigationLink } from '../navigation/use-app-navigate' import * as api from '../api' diff --git a/assets/js/dashboard/stats/devices/index.js b/assets/js/dashboard/stats/devices/index.js index 1452ab5bcd..ca9dd8ae2f 100644 --- a/assets/js/dashboard/stats/devices/index.js +++ b/assets/js/dashboard/stats/devices/index.js @@ -1,32 +1,36 @@ -import React, { useEffect, useState } from 'react'; -import * as storage from '../../util/storage'; -import { getFiltersByKeyPrefix, hasConversionGoalFilter, isFilteringOnFixedValue } from '../../util/filters'; -import ListReport from '../reports/list'; -import * as metrics from '../reports/metrics'; -import * as api from '../../api'; -import * as url from '../../util/url'; -import ImportedQueryUnsupportedWarning from '../imported-query-unsupported-warning'; -import { useQueryContext } from '../../query-context'; -import { useSiteContext } from '../../site-context'; +import React, { useEffect, useState } from 'react' +import * as storage from '../../util/storage' +import { + getFiltersByKeyPrefix, + hasConversionGoalFilter, + isFilteringOnFixedValue +} from '../../util/filters' +import ListReport from '../reports/list' +import * as metrics from '../reports/metrics' +import * as api from '../../api' +import * as url from '../../util/url' +import ImportedQueryUnsupportedWarning from '../imported-query-unsupported-warning' +import { useQueryContext } from '../../query-context' +import { useSiteContext } from '../../site-context' import { browsersRoute, browserVersionsRoute, operatingSystemsRoute, operatingSystemVersionsRoute, screenSizesRoute -} from '../../router'; +} from '../../router' // Icons copied from https://github.com/alrra/browser-logos const BROWSER_ICONS = { - 'Chrome': 'chrome.svg', - 'curl': 'curl.svg', - 'Safari': 'safari.png', - 'Firefox': 'firefox.svg', + Chrome: 'chrome.svg', + curl: 'curl.svg', + Safari: 'safari.png', + Firefox: 'firefox.svg', 'Microsoft Edge': 'edge.svg', - 'Vivaldi': 'vivaldi.svg', - 'Opera': 'opera.svg', + Vivaldi: 'vivaldi.svg', + Opera: 'opera.svg', 'Samsung Browser': 'samsung-internet.svg', - 'Chromium': 'chromium.svg', + Chromium: 'chromium.svg', 'UC Browser': 'uc.svg', 'Yandex Browser': 'yandex.png', // Only PNG available in browser-logos // Logos underneath this line are not available in browser-logos. Grabbed from random places on the internets. @@ -34,7 +38,7 @@ const BROWSER_ICONS = { 'MIUI Browser': 'miui.webp', 'Huawei Browser Mobile': 'huawei.png', 'QQ Browser': 'qq.png', - 'Ecosia': 'ecosia.png', + Ecosia: 'ecosia.png', 'vivo Browser': 'vivo.png' } @@ -51,8 +55,8 @@ export function browserIconFor(browser) { } function Browsers({ afterFetchData }) { - const site = useSiteContext(); - const { query } = useQueryContext(); + const site = useSiteContext() + const { query } = useQueryContext() function fetchData() { return api.get(url.apiPath(site, '/browsers'), query) } @@ -60,7 +64,7 @@ function Browsers({ afterFetchData }) { function getFilterInfo(listItem) { return { prefix: 'browser', - filter: ["is", "browser", [listItem['name']]] + filter: ['is', 'browser', [listItem['name']]] } } @@ -73,7 +77,7 @@ function Browsers({ afterFetchData }) { metrics.createVisitors({ meta: { plot: true } }), hasConversionGoalFilter(query) && metrics.createConversionRate(), !hasConversionGoalFilter(query) && metrics.createPercentage() - ].filter(metric => !!metric) + ].filter((metric) => !!metric) } return ( @@ -84,14 +88,17 @@ function Browsers({ afterFetchData }) { keyLabel="Browser" metrics={chooseMetrics()} renderIcon={renderIcon} - detailsLinkProps={{ path: browsersRoute.path, search: (search) => search }} + detailsLinkProps={{ + path: browsersRoute.path, + search: (search) => search + }} /> ) } function BrowserVersions({ afterFetchData }) { - const { query } = useQueryContext(); - const site = useSiteContext(); + const { query } = useQueryContext() + const site = useSiteContext() function fetchData() { return api.get(url.apiPath(site, '/browser-versions'), query) } @@ -101,12 +108,12 @@ function BrowserVersions({ afterFetchData }) { } function getFilterInfo(listItem) { - if (getSingleFilter(query, "browser") == '(not set)') { + if (getSingleFilter(query, 'browser') == '(not set)') { return null } return { prefix: 'browser_version', - filter: ["is", "browser_version", [listItem.version]] + filter: ['is', 'browser_version', [listItem.version]] } } @@ -115,7 +122,7 @@ function BrowserVersions({ afterFetchData }) { metrics.createVisitors({ meta: { plot: true } }), hasConversionGoalFilter(query) && metrics.createConversionRate(), !hasConversionGoalFilter(query) && metrics.createPercentage() - ].filter(metric => !!metric) + ].filter((metric) => !!metric) } return ( @@ -126,46 +133,45 @@ function BrowserVersions({ afterFetchData }) { keyLabel="Browser version" metrics={chooseMetrics()} renderIcon={renderIcon} - detailsLinkProps={{ path: browserVersionsRoute.path, search: (search) => search }} + detailsLinkProps={{ + path: browserVersionsRoute.path, + search: (search) => search + }} /> ) } // Icons copied from https://github.com/ngeenx/operating-system-logos const OS_ICONS = { - 'iOS': 'ios.png', - 'Mac': 'mac.png', - 'Windows': 'windows.png', + iOS: 'ios.png', + Mac: 'mac.png', + Windows: 'windows.png', 'Windows Phone': 'windows.png', - 'Android': 'android.png', + Android: 'android.png', 'GNU/Linux': 'gnu_linux.png', - 'Ubuntu': 'ubuntu.png', + Ubuntu: 'ubuntu.png', 'Chrome OS': 'chrome_os.png', - 'iPadOS': 'ipad_os.png', + iPadOS: 'ipad_os.png', 'Fire OS': 'fire_os.png', - 'HarmonyOS': 'harmony_os.png', - 'Tizen': 'tizen.png', - 'PlayStation': 'playstation.png', - 'KaiOS': 'kai_os.png', - 'Fedora': 'fedora.png', - 'FreeBSD': 'freebsd.png', + HarmonyOS: 'harmony_os.png', + Tizen: 'tizen.png', + PlayStation: 'playstation.png', + KaiOS: 'kai_os.png', + Fedora: 'fedora.png', + FreeBSD: 'freebsd.png' } export function osIconFor(os) { const filename = OS_ICONS[os] || 'fallback.svg' return ( - + ) } function OperatingSystems({ afterFetchData }) { - const { query } = useQueryContext(); - const site = useSiteContext(); + const { query } = useQueryContext() + const site = useSiteContext() function fetchData() { return api.get(url.apiPath(site, '/operating-systems'), query) } @@ -173,7 +179,7 @@ function OperatingSystems({ afterFetchData }) { function getFilterInfo(listItem) { return { prefix: 'os', - filter: ["is", "os", [listItem['name']]] + filter: ['is', 'os', [listItem['name']]] } } @@ -181,8 +187,9 @@ function OperatingSystems({ afterFetchData }) { return [ metrics.createVisitors({ meta: { plot: true } }), hasConversionGoalFilter(query) && metrics.createConversionRate(), - !hasConversionGoalFilter(query) && metrics.createPercentage({ meta: { hiddenonMobile: true } }) - ].filter(metric => !!metric) + !hasConversionGoalFilter(query) && + metrics.createPercentage({ meta: { hiddenonMobile: true } }) + ].filter((metric) => !!metric) } function renderIcon(listItem) { @@ -197,14 +204,17 @@ function OperatingSystems({ afterFetchData }) { renderIcon={renderIcon} keyLabel="Operating system" metrics={chooseMetrics()} - detailsLinkProps={{ path: operatingSystemsRoute.path, search: (search) => search }} + detailsLinkProps={{ + path: operatingSystemsRoute.path, + search: (search) => search + }} /> ) } function OperatingSystemVersions({ afterFetchData }) { - const { query } = useQueryContext(); - const site = useSiteContext(); + const { query } = useQueryContext() + const site = useSiteContext() function fetchData() { return api.get(url.apiPath(site, '/operating-system-versions'), query) @@ -215,12 +225,12 @@ function OperatingSystemVersions({ afterFetchData }) { } function getFilterInfo(listItem) { - if (getSingleFilter(query, "os") == '(not set)') { + if (getSingleFilter(query, 'os') == '(not set)') { return null } return { prefix: 'os_version', - filter: ["is", "os_version", [listItem.version]] + filter: ['is', 'os_version', [listItem.version]] } } @@ -229,7 +239,7 @@ function OperatingSystemVersions({ afterFetchData }) { metrics.createVisitors({ meta: { plot: true } }), hasConversionGoalFilter(query) && metrics.createConversionRate(), !hasConversionGoalFilter(query) && metrics.createPercentage() - ].filter(metric => !!metric) + ].filter((metric) => !!metric) } return ( @@ -240,15 +250,17 @@ function OperatingSystemVersions({ afterFetchData }) { getFilterInfo={getFilterInfo} keyLabel="Operating System Version" metrics={chooseMetrics()} - detailsLinkProps={{ path: operatingSystemVersionsRoute.path, search: (search) => search }} + detailsLinkProps={{ + path: operatingSystemVersionsRoute.path, + search: (search) => search + }} /> ) - } function ScreenSizes({ afterFetchData }) { - const { query } = useQueryContext(); - const site = useSiteContext(); + const { query } = useQueryContext() + const site = useSiteContext() function fetchData() { return api.get(url.apiPath(site, '/screen-sizes'), query) @@ -261,7 +273,7 @@ function ScreenSizes({ afterFetchData }) { function getFilterInfo(listItem) { return { prefix: 'screen', - filter: ["is", "screen", [listItem['name']]] + filter: ['is', 'screen', [listItem['name']]] } } @@ -270,7 +282,7 @@ function ScreenSizes({ afterFetchData }) { metrics.createVisitors({ meta: { plot: true } }), hasConversionGoalFilter(query) && metrics.createConversionRate(), !hasConversionGoalFilter(query) && metrics.createPercentage() - ].filter(metric => !!metric) + ].filter((metric) => !!metric) } return ( @@ -281,7 +293,10 @@ function ScreenSizes({ afterFetchData }) { keyLabel="Screen size" metrics={chooseMetrics()} renderIcon={renderIcon} - detailsLinkProps={{ path: screenSizesRoute.path, search: (search) => search }} + detailsLinkProps={{ + path: screenSizesRoute.path, + search: (search) => search + }} /> ) } @@ -290,21 +305,94 @@ export function screenSizeIconFor(screenSize) { let svg = null if (screenSize === 'Mobile') { - svg = + svg = ( + + + + + ) } else if (screenSize === 'Tablet') { - svg = + svg = ( + + + + + ) } else if (screenSize === 'Laptop') { - svg = + svg = ( + + + + + ) } else if (screenSize === 'Desktop') { - svg = + svg = ( + + + + + + ) } return {svg} } export default function Devices() { - const { query } = useQueryContext(); - const site = useSiteContext(); + const { query } = useQueryContext() + const site = useSiteContext() const tabKey = `deviceTab__${site.domain}` const storedTab = storage.getItem(tabKey) @@ -347,9 +435,7 @@ export default function Devices() { if (isActive) { return ( - ) @@ -370,7 +456,10 @@ export default function Devices() {

Devices

- +
{renderPill('Browser', 'browser')} diff --git a/assets/js/dashboard/stats/graph/date-formatter.js b/assets/js/dashboard/stats/graph/date-formatter.js index 2d3a5def4a..690dc36f5a 100644 --- a/assets/js/dashboard/stats/graph/date-formatter.js +++ b/assets/js/dashboard/stats/graph/date-formatter.js @@ -1,6 +1,8 @@ -import {parseUTCDate, formatMonthYYYY, formatDayShort} from '../../util/date' +import { parseUTCDate, formatMonthYYYY, formatDayShort } from '../../util/date' -const browserDateFormat = Intl.DateTimeFormat(navigator.language, { hour: 'numeric' }) +const browserDateFormat = Intl.DateTimeFormat(navigator.language, { + hour: 'numeric' +}) const is12HourClock = function () { return browserDateFormat.resolvedOptions().hour12 @@ -19,7 +21,9 @@ const monthIntervalFormatter = { const weekIntervalFormatter = { long(isoDate, options) { const formatted = this.short(isoDate, options) - return options.isBucketPartial ? `Partial week of ${formatted}` : `Week of ${formatted}` + return options.isBucketPartial + ? `Partial week of ${formatted}` + : `Week of ${formatted}` }, short(isoDate, options) { return formatDayShort(parseUTCDate(isoDate), options.shouldShowYear) @@ -98,9 +102,20 @@ const factory = { * @param {boolean} config.shouldShowYear - Should the year be appended to the date? * Defaults to false. Rendering year string is a newer opt-in feature to be enabled where needed. */ -export default function dateFormatter({ interval, longForm, period, isPeriodFull, shouldShowYear = false }) { +export default function dateFormatter({ + interval, + longForm, + period, + isPeriodFull, + shouldShowYear = false +}) { const displayMode = longForm ? 'long' : 'short' - const options = { period: period, interval: interval, isBucketPartial: !isPeriodFull, shouldShowYear } + const options = { + period: period, + interval: interval, + isBucketPartial: !isPeriodFull, + shouldShowYear + } return function (isoDate, _index, _ticks) { return factory[interval][displayMode](isoDate, options) } diff --git a/assets/js/dashboard/stats/graph/graph-tooltip.js b/assets/js/dashboard/stats/graph/graph-tooltip.js index c022db5137..2dfb0ce9ab 100644 --- a/assets/js/dashboard/stats/graph/graph-tooltip.js +++ b/assets/js/dashboard/stats/graph/graph-tooltip.js @@ -5,64 +5,101 @@ import { METRIC_LABELS } from './graph-util' import { MetricFormatterShort } from '../reports/metric-formatter' import { ChangeArrow } from '../reports/change-arrow' -const renderBucketLabel = function(query, graphData, label, comparison = false) { +const renderBucketLabel = function ( + query, + graphData, + label, + comparison = false +) { let isPeriodFull = graphData.full_intervals?.[label] if (comparison) isPeriodFull = true const formattedLabel = dateFormatter({ - interval: graphData.interval, longForm: true, period: query.period, isPeriodFull, + interval: graphData.interval, + longForm: true, + period: query.period, + isPeriodFull })(label) if (query.period === 'realtime') { return dateFormatter({ - interval: graphData.interval, longForm: true, period: query.period, + interval: graphData.interval, + longForm: true, + period: query.period })(label) } if (graphData.interval === 'hour' || graphData.interval == 'minute') { - const date = dateFormatter({ interval: "day", longForm: true, period: query.period })(label) + const date = dateFormatter({ + interval: 'day', + longForm: true, + period: query.period + })(label) return `${date}, ${formattedLabel}` } return formattedLabel } -const calculatePercentageDifference = function(oldValue, newValue) { +const calculatePercentageDifference = function (oldValue, newValue) { if (oldValue == 0 && newValue > 0) { return 100 } else if (oldValue == 0 && newValue == 0) { return 0 } else { - return Math.round((newValue - oldValue) / oldValue * 100) + return Math.round(((newValue - oldValue) / oldValue) * 100) } } -const buildTooltipData = function(query, graphData, metric, tooltipModel) { - const data = tooltipModel.dataPoints.find((dataPoint) => dataPoint.dataset.yAxisID == "y") - const comparisonData = tooltipModel.dataPoints.find((dataPoint) => dataPoint.dataset.yAxisID == "yComparison") +const buildTooltipData = function (query, graphData, metric, tooltipModel) { + const data = tooltipModel.dataPoints.find( + (dataPoint) => dataPoint.dataset.yAxisID == 'y' + ) + const comparisonData = tooltipModel.dataPoints.find( + (dataPoint) => dataPoint.dataset.yAxisID == 'yComparison' + ) - const label = data && renderBucketLabel(query, graphData, graphData.labels[data.dataIndex]) - const comparisonLabel = comparisonData && renderBucketLabel(query, graphData, graphData.comparison_labels[comparisonData.dataIndex], true) + const label = + data && + renderBucketLabel(query, graphData, graphData.labels[data.dataIndex]) + const comparisonLabel = + comparisonData && + renderBucketLabel( + query, + graphData, + graphData.comparison_labels[comparisonData.dataIndex], + true + ) const value = graphData.plot[data.dataIndex] const formatter = MetricFormatterShort[metric] const comparisonValue = graphData.comparison_plot?.[comparisonData.dataIndex] - const comparisonDifference = label && comparisonData && calculatePercentageDifference(comparisonValue, value) + const comparisonDifference = + label && + comparisonData && + calculatePercentageDifference(comparisonValue, value) const formattedValue = formatter(value) const formattedComparisonValue = comparisonData && formatter(comparisonValue) - return { label, formattedValue, comparisonLabel, formattedComparisonValue, comparisonDifference } + return { + label, + formattedValue, + comparisonLabel, + formattedComparisonValue, + comparisonDifference + } } - let tooltipRoot export default function GraphTooltip(graphData, metric, query) { return (context) => { const tooltipModel = context.tooltip - const offset = document.getElementById("main-graph-canvas").getBoundingClientRect() + const offset = document + .getElementById('main-graph-canvas') + .getBoundingClientRect() let tooltipEl = document.getElementById('chartjs-tooltip') if (!tooltipEl) { @@ -75,7 +112,8 @@ export default function GraphTooltip(graphData, metric, query) { } if (tooltipEl && offset && window.innerWidth < 768) { - tooltipEl.style.top = offset.y + offset.height + window.scrollY + 15 + 'px' + tooltipEl.style.top = + offset.y + offset.height + window.scrollY + 15 + 'px' tooltipEl.style.left = offset.x + 'px' tooltipEl.style.right = null tooltipEl.style.opacity = 1 @@ -87,41 +125,64 @@ export default function GraphTooltip(graphData, metric, query) { } if (tooltipModel.body) { - const tooltipData = buildTooltipData(query, graphData, metric, tooltipModel) + const tooltipData = buildTooltipData( + query, + graphData, + metric, + tooltipModel + ) tooltipRoot.render( ) } diff --git a/assets/js/dashboard/stats/graph/graph-util.js b/assets/js/dashboard/stats/graph/graph-util.js index cdac657387..a51226b062 100644 --- a/assets/js/dashboard/stats/graph/graph-util.js +++ b/assets/js/dashboard/stats/graph/graph-util.js @@ -1,17 +1,17 @@ export const METRIC_LABELS = { - 'visitors': 'Visitors', - 'pageviews': 'Pageviews', - 'events': 'Total Conversions', - 'views_per_visit': 'Views per Visit', - 'visits': 'Visits', - 'bounce_rate': 'Bounce Rate', - 'visit_duration': 'Visit Duration', - 'conversions': 'Converted Visitors', - 'conversion_rate': 'Conversion Rate', - 'average_revenue': 'Average Revenue', - 'total_revenue': 'Total Revenue', - 'scroll_depth': 'Scroll Depth', - 'time_on_page': 'Time on Page', + visitors: 'Visitors', + pageviews: 'Pageviews', + events: 'Total Conversions', + views_per_visit: 'Views per Visit', + visits: 'Visits', + bounce_rate: 'Bounce Rate', + visit_duration: 'Visit Duration', + conversions: 'Converted Visitors', + conversion_rate: 'Conversion Rate', + average_revenue: 'Average Revenue', + total_revenue: 'Total Revenue', + scroll_depth: 'Scroll Depth', + time_on_page: 'Time on Page' } function plottable(dataArray) { @@ -25,54 +25,73 @@ function plottable(dataArray) { }) } -const buildComparisonDataset = function(comparisonPlot) { +const buildComparisonDataset = function (comparisonPlot) { if (!comparisonPlot) return [] - return [{ - data: plottable(comparisonPlot), - borderColor: 'rgba(60,70,110,0.2)', - pointBackgroundColor: 'rgba(60,70,110,0.2)', - pointHoverBackgroundColor: 'rgba(60, 70, 110)', - yAxisID: 'yComparison', - }] + return [ + { + data: plottable(comparisonPlot), + borderColor: 'rgba(60,70,110,0.2)', + pointBackgroundColor: 'rgba(60,70,110,0.2)', + pointHoverBackgroundColor: 'rgba(60, 70, 110)', + yAxisID: 'yComparison' + } + ] } -const buildDashedDataset = function(plot, presentIndex) { +const buildDashedDataset = function (plot, presentIndex) { if (!presentIndex) return [] - const dashedPart = plot.slice(presentIndex - 1, presentIndex + 1); - const dashedPlot = (new Array(presentIndex - 1)).concat(dashedPart) + const dashedPart = plot.slice(presentIndex - 1, presentIndex + 1) + const dashedPlot = new Array(presentIndex - 1).concat(dashedPart) - return [{ - data: plottable(dashedPlot), - borderDash: [3, 3], - borderColor: 'rgba(101,116,205)', - pointHoverBackgroundColor: 'rgba(71, 87, 193)', - yAxisID: 'y', - }] + return [ + { + data: plottable(dashedPlot), + borderDash: [3, 3], + borderColor: 'rgba(101,116,205)', + pointHoverBackgroundColor: 'rgba(71, 87, 193)', + yAxisID: 'y' + } + ] } -const buildMainPlotDataset = function(plot, presentIndex) { +const buildMainPlotDataset = function (plot, presentIndex) { const data = presentIndex ? plot.slice(0, presentIndex) : plot - return [{ - data: plottable(data), - borderColor: 'rgba(101,116,205)', - pointBackgroundColor: 'rgba(101,116,205)', - pointHoverBackgroundColor: 'rgba(71, 87, 193)', - yAxisID: 'y', - }] + return [ + { + data: plottable(data), + borderColor: 'rgba(101,116,205)', + pointBackgroundColor: 'rgba(101,116,205)', + pointHoverBackgroundColor: 'rgba(71, 87, 193)', + yAxisID: 'y' + } + ] } -export const buildDataSet = (plot, comparisonPlot, present_index, ctx, label) => { - var gradient = ctx.createLinearGradient(0, 0, 0, 300); - var prev_gradient = ctx.createLinearGradient(0, 0, 0, 300); - gradient.addColorStop(0, 'rgba(101,116,205, 0.2)'); - gradient.addColorStop(1, 'rgba(101,116,205, 0)'); - prev_gradient.addColorStop(0, 'rgba(101,116,205, 0.075)'); - prev_gradient.addColorStop(1, 'rgba(101,116,205, 0)'); +export const buildDataSet = ( + plot, + comparisonPlot, + present_index, + ctx, + label +) => { + var gradient = ctx.createLinearGradient(0, 0, 0, 300) + var prev_gradient = ctx.createLinearGradient(0, 0, 0, 300) + gradient.addColorStop(0, 'rgba(101,116,205, 0.2)') + gradient.addColorStop(1, 'rgba(101,116,205, 0)') + prev_gradient.addColorStop(0, 'rgba(101,116,205, 0.075)') + prev_gradient.addColorStop(1, 'rgba(101,116,205, 0)') - const defaultOptions = { label, borderWidth: 3, pointBorderColor: "transparent", pointHoverRadius: 4, backgroundColor: gradient, fill: true } + const defaultOptions = { + label, + borderWidth: 3, + pointBorderColor: 'transparent', + pointHoverRadius: 4, + backgroundColor: gradient, + fill: true + } const dataset = [ ...buildMainPlotDataset(plot, present_index), diff --git a/assets/js/dashboard/stats/graph/interval-picker.js b/assets/js/dashboard/stats/graph/interval-picker.js index c1a07e0007..c5c4b89e08 100644 --- a/assets/js/dashboard/stats/graph/interval-picker.js +++ b/assets/js/dashboard/stats/graph/interval-picker.js @@ -1,21 +1,26 @@ -import React, { Fragment, useRef } from 'react'; -import { Menu, Transition } from '@headlessui/react'; -import { ChevronDownIcon } from '@heroicons/react/20/solid'; -import classNames from 'classnames'; -import * as storage from '../../util/storage'; -import { BlurMenuButtonOnEscape, isModifierPressed, isTyping, Keybind } from '../../keybinding'; -import { useQueryContext } from '../../query-context'; -import { useSiteContext } from '../../site-context'; -import { useMatch } from 'react-router-dom'; -import { rootRoute } from '../../router'; -import { popover } from '../../components/popover'; +import React, { Fragment, useRef } from 'react' +import { Menu, Transition } from '@headlessui/react' +import { ChevronDownIcon } from '@heroicons/react/20/solid' +import classNames from 'classnames' +import * as storage from '../../util/storage' +import { + BlurMenuButtonOnEscape, + isModifierPressed, + isTyping, + Keybind +} from '../../keybinding' +import { useQueryContext } from '../../query-context' +import { useSiteContext } from '../../site-context' +import { useMatch } from 'react-router-dom' +import { rootRoute } from '../../router' +import { popover } from '../../components/popover' const INTERVAL_LABELS = { - 'minute': 'Minutes', - 'hour': 'Hours', - 'day': 'Days', - 'week': 'Weeks', - 'month': 'Months' + minute: 'Minutes', + hour: 'Hours', + day: 'Days', + week: 'Weeks', + month: 'Months' } function validIntervals(site, query) { @@ -36,11 +41,11 @@ function validIntervals(site, query) { function getDefaultInterval(query, validIntervals) { const defaultByPeriod = { - 'day': 'hour', + day: 'hour', '7d': 'day', '6mo': 'month', '12mo': 'month', - 'year': 'month' + year: 'month' } if (query.period === 'custom') { @@ -74,7 +79,7 @@ function storeInterval(period, domain, interval) { storage.setItem(`interval__${period}__${domain}`, interval) } -export const getCurrentInterval = function(site, query) { +export const getCurrentInterval = function (site, query) { const options = validIntervals(site, query) const storedInterval = getStoredInterval(query.period, site.domain) @@ -89,16 +94,15 @@ export const getCurrentInterval = function(site, query) { export function IntervalPicker({ onIntervalUpdate }) { const menuElement = useRef(null) - const {query} = useQueryContext(); - const site = useSiteContext(); + const { query } = useQueryContext() + const site = useSiteContext() const dashboardRouteMatch = useMatch(rootRoute.path) - + if (query.period == 'realtime') return null const options = validIntervals(site, query) const currentInterval = getCurrentInterval(site, query) - function updateInterval(interval) { storeInterval(query.period, site.domain, interval) onIntervalUpdate(interval) @@ -106,13 +110,23 @@ export function IntervalPicker({ onIntervalUpdate }) { function renderDropdownItem(option) { return ( - updateInterval(option)} key={option} disabled={option == currentInterval}> + updateInterval(option)} + key={option} + disabled={option == currentInterval} + > {({ active }) => ( - + {INTERVAL_LABELS[option]} )} @@ -144,11 +158,7 @@ export function IntervalPicker({ onIntervalUpdate }) {