Add auto refresh option

This commit is contained in:
Jan Zípek 2024-04-03 22:23:59 +02:00
parent c93b05f193
commit a25cb6dc68
Signed by: kamen
GPG Key ID: A17882625B33AC31
8 changed files with 249 additions and 4 deletions

View File

@ -56,3 +56,21 @@ button,
color: var(--button-remove-fg-color); color: var(--button-remove-fg-color);
} }
} }
.button-group {
display: flex;
button:first-child {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
button {
margin-right: 1px;
}
button:last-child {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
}

View File

@ -90,6 +90,57 @@
} }
} }
.dashboard-refresh {
button.auto-refresh-toggle {
padding: 0rem 0.5rem;
.svg-icon {
margin-left: 0.2rem;
}
}
}
.auto-refresh-popup {
display: none;
position: absolute;
z-index: 3;
top: 35px;
right: 10px;
width: 5rem;
&.show {
display: block;
}
.refresh-interval-option {
display: block;
margin-bottom: 2px;
text-align: center;
padding: 0.25rem;
cursor: pointer;
&:hover {
background-color: var(--input-bg-color);
}
}
}
.dashboard-refresh-select {
margin: 1rem 0;
border-top: 1px solid var(--box-border-color);
padding: 1rem 0;
display: flex;
align-items: center;
> label {
margin-right: 0.5rem;
}
> select {
flex: 1;
}
}
.filter-toggle { .filter-toggle {
.current-value { .current-value {
display: flex; display: flex;

View File

@ -94,6 +94,11 @@
rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0) 0%,
rgba(0, 0, 0, 0.2) 100% rgba(0, 0, 0, 0.2) 100%
); );
--filters-menu-shadow: linear-gradient(
90deg,
rgba(0, 0, 0, 0) 0%,
rgba(0, 0, 0, 0.2) 100%
);
--input-hint-color: #aaa; --input-hint-color: #aaa;
--button-remove-bg-color: transparent; --button-remove-bg-color: transparent;
--button-remove-fg-color: rgb(255, 118, 118); --button-remove-fg-color: rgb(255, 118, 118);

View File

@ -7,6 +7,8 @@ import { useDashboardContext } from '../../contexts/DashboardContext'
import { DashboardFilters } from './components/DashboardFilters' import { DashboardFilters } from './components/DashboardFilters'
import { DashboardHeaderFilterToggle } from './components/DashboardHeaderFilterToggle' import { DashboardHeaderFilterToggle } from './components/DashboardHeaderFilterToggle'
import { DashboardSwitch } from './components/DashboardSwitch' import { DashboardSwitch } from './components/DashboardSwitch'
import { DashboardRefreshButton } from './components/DashboardRefreshButton'
import { DashboardRefreshSelect } from './components/DashboardRefreshSelect'
export const DashboardHeader = () => { export const DashboardHeader = () => {
const { verticalMode, boxes, setBoxes, setFilter } = useDashboardContext() const { verticalMode, boxes, setBoxes, setFilter } = useDashboardContext()
@ -73,6 +75,8 @@ export const DashboardHeader = () => {
<DashboardSwitch /> <DashboardSwitch />
<DashboardFilters onClose={() => setFiltersShown(false)} /> <DashboardFilters onClose={() => setFiltersShown(false)} />
<DashboardRefreshSelect />
</div> </div>
</div> </div>
{filtersShown && ( {filtersShown && (
@ -88,9 +92,7 @@ export const DashboardHeader = () => {
<DashboardSwitch /> <DashboardSwitch />
<button onClick={handleNewBox}>Add box</button> <button onClick={handleNewBox}>Add box</button>
<DashboardHeaderFilterToggle /> <DashboardHeaderFilterToggle />
<button onClick={handleRefresh}> <DashboardRefreshButton onRefresh={handleRefresh} />
<RefreshIcon /> Refresh all
</button>
</> </>
)} )}
</div> </div>

View File

@ -0,0 +1,64 @@
import { ChevronDownIcon, RefreshIcon } from '@/icons'
import { useDashboardContext } from '@/pages/dashboard/contexts/DashboardContext'
import { getPossibleRefreshIntervals } from '@/pages/dashboard/utils/intervals'
import { useState } from 'preact/hooks'
type Props = {
onRefresh: () => void
}
export const DashboardRefreshButton = ({ onRefresh }: Props) => {
const { refreshInterval, setRefreshInterval } = useDashboardContext()
const [showAutoRefresh, setShowAutoRefresh] = useState(false)
return (
<>
<div className="button-group dashboard-refresh">
<button onClick={onRefresh}>
<RefreshIcon /> Refresh all
</button>
<button
className="auto-refresh-toggle"
onClick={() => setShowAutoRefresh((prev) => !prev)}
>
{refreshInterval} <ChevronDownIcon />
</button>
</div>
<div
className={
showAutoRefresh ? 'auto-refresh-popup show' : 'auto-refresh-popup'
}
>
<div className="box">
<div
className="refresh-interval-option"
onClick={() => {
setRefreshInterval(null)
setShowAutoRefresh(false)
}}
>
Off
</div>
{getPossibleRefreshIntervals().map((interval) => (
<div
className="refresh-interval-option"
key={interval}
onClick={() => {
setRefreshInterval(interval)
setShowAutoRefresh(false)
}}
>
{interval}
</div>
))}
</div>
</div>
{showAutoRefresh && (
<div
className="menu-overlay"
onClick={() => setShowAutoRefresh((v) => !v)}
></div>
)}
</>
)
}

View File

@ -0,0 +1,34 @@
import { useDashboardContext } from '@/pages/dashboard/contexts/DashboardContext'
import {
getPossibleRefreshIntervals,
isValidRefreshInterval,
} from '@/pages/dashboard/utils/intervals'
import { ChangeEvent } from 'preact/compat'
export const DashboardRefreshSelect = () => {
const { refreshInterval, setRefreshInterval } = useDashboardContext()
const handleChange = (e: ChangeEvent<HTMLSelectElement>) => {
const value = (e.target as HTMLSelectElement).value
if (value === 'off' || !isValidRefreshInterval(value)) {
setRefreshInterval(null)
} else {
setRefreshInterval(value)
}
}
return (
<div className={'dashboard-refresh-select'}>
<label>Auto refresh:</label>
<select value={refreshInterval ?? 'off'} onChange={handleChange}>
<option value="off">Off</option>
{getPossibleRefreshIntervals().map((interval) => (
<option value={interval} key={interval}>
{interval}
</option>
))}
</select>
</div>
)
}

View File

@ -24,6 +24,11 @@ import {
} from '../components/DashboardHeader/components/DashboardFilters' } from '../components/DashboardHeader/components/DashboardFilters'
import { BoxDefinition } from '../types' import { BoxDefinition } from '../types'
import { useQueryString } from '@/utils/hooks/useQueryString' import { useQueryString } from '@/utils/hooks/useQueryString'
import {
RefreshIntervalValue,
getRefreshIntervalInMs,
isValidRefreshInterval,
} from '../utils/intervals'
type DashboardContextType = { type DashboardContextType = {
filter: FilterValue filter: FilterValue
@ -36,6 +41,8 @@ type DashboardContextType = {
isDashboardSelected: boolean isDashboardSelected: boolean
isDashboardLoading: boolean isDashboardLoading: boolean
dashboard: DashboardInfo | undefined dashboard: DashboardInfo | undefined
refreshInterval: RefreshIntervalValue | null
setRefreshInterval: StateUpdater<RefreshIntervalValue | null>
} }
const DashboardContext = createContext<DashboardContextType | null>(null) const DashboardContext = createContext<DashboardContextType | null>(null)
@ -138,9 +145,22 @@ export const DashboardContextProvider = ({
} }
}) })
const [refreshInterval, setRefreshInterval] =
useState<RefreshIntervalValue | null>(() => {
const query = getQuery()
const queryRefresh = query['refresh']
if (!queryRefresh || !isValidRefreshInterval(queryRefresh)) {
return null
}
return queryRefresh
})
useEffect(() => { useEffect(() => {
setQuery({ setQuery({
...getQuery(), ...getQuery(),
...(refreshInterval !== null && { refresh: refreshInterval }),
dashboard: dashboardId.toString(), dashboard: dashboardId.toString(),
interval: filter.interval, interval: filter.interval,
...(filter.interval === 'custom' && { ...(filter.interval === 'custom' && {
@ -148,7 +168,29 @@ export const DashboardContextProvider = ({
to: filter.customTo.toISOString(), to: filter.customTo.toISOString(),
}), }),
}) })
}, [filter, dashboardId]) }, [filter, dashboardId, refreshInterval])
useEffect(() => {
if (refreshInterval === null) {
return
}
const intervalInMs = getRefreshIntervalInMs(refreshInterval)
const interval = setInterval(() => {
setFilter((v) => {
const range = intervalToRange(v.interval, v.customFrom, v.customTo)
return {
...v,
customFrom: range[0],
customTo: range[1],
}
})
}, intervalInMs)
return () => clearInterval(interval)
}, [refreshInterval])
const verticalMode = viewport.width < 800 const verticalMode = viewport.width < 800
@ -164,6 +206,8 @@ export const DashboardContextProvider = ({
isDashboardSelected, isDashboardSelected,
isDashboardLoading: dashboard.isLoading, isDashboardLoading: dashboard.isLoading,
dashboard: dashboard.data, dashboard: dashboard.data,
refreshInterval,
setRefreshInterval,
}), }),
[ [
filter, filter,
@ -175,6 +219,8 @@ export const DashboardContextProvider = ({
isDashboardSelected, isDashboardSelected,
dashboard.isLoading, dashboard.isLoading,
dashboard.data, dashboard.data,
refreshInterval,
setRefreshInterval,
] ]
) )

View File

@ -0,0 +1,25 @@
export type RefreshIntervalValue = keyof typeof INTERVALS_TO_MS
const INTERVALS = ['5s', '10s', '30s', '1m', '5m', '10m', '30m', '1h'] as const
const INTERVALS_TO_MS = {
'5s': 5000,
'10s': 10000,
'30s': 30000,
'1m': 60000,
'5m': 300000,
'10m': 600000,
'30m': 1800000,
'1h': 3600000,
}
export const getPossibleRefreshIntervals = () => INTERVALS
export const isValidRefreshInterval = (
interval: string
): interval is RefreshIntervalValue =>
INTERVALS.includes(interval as RefreshIntervalValue)
export const getRefreshIntervalInMs = (interval: RefreshIntervalValue) => {
return INTERVALS_TO_MS[interval]
}