Merge branch 'feature/auto-refresh' of kamen/sensors-dashboard into master
This commit is contained in:
commit
3094d13dc3
|
|
@ -2,6 +2,10 @@ kind: pipeline
|
|||
type: docker
|
||||
name: build & publish image
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
- master
|
||||
|
||||
steps:
|
||||
- name: build & publish
|
||||
image: plugins/docker
|
||||
|
|
|
|||
|
|
@ -56,3 +56,21 @@ button,
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
.current-value {
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -94,6 +94,11 @@
|
|||
rgba(0, 0, 0, 0) 0%,
|
||||
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;
|
||||
--button-remove-bg-color: transparent;
|
||||
--button-remove-fg-color: rgb(255, 118, 118);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import { useDashboardContext } from '../../contexts/DashboardContext'
|
|||
import { DashboardFilters } from './components/DashboardFilters'
|
||||
import { DashboardHeaderFilterToggle } from './components/DashboardHeaderFilterToggle'
|
||||
import { DashboardSwitch } from './components/DashboardSwitch'
|
||||
import { DashboardRefreshButton } from './components/DashboardRefreshButton'
|
||||
import { DashboardRefreshSelect } from './components/DashboardRefreshSelect'
|
||||
|
||||
export const DashboardHeader = () => {
|
||||
const { verticalMode, boxes, setBoxes, setFilter } = useDashboardContext()
|
||||
|
|
@ -73,6 +75,8 @@ export const DashboardHeader = () => {
|
|||
<DashboardSwitch />
|
||||
|
||||
<DashboardFilters onClose={() => setFiltersShown(false)} />
|
||||
|
||||
<DashboardRefreshSelect />
|
||||
</div>
|
||||
</div>
|
||||
{filtersShown && (
|
||||
|
|
@ -88,9 +92,7 @@ export const DashboardHeader = () => {
|
|||
<DashboardSwitch />
|
||||
<button onClick={handleNewBox}>Add box</button>
|
||||
<DashboardHeaderFilterToggle />
|
||||
<button onClick={handleRefresh}>
|
||||
<RefreshIcon /> Refresh all
|
||||
</button>
|
||||
<DashboardRefreshButton onRefresh={handleRefresh} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
@ -24,6 +24,11 @@ import {
|
|||
} from '../components/DashboardHeader/components/DashboardFilters'
|
||||
import { BoxDefinition } from '../types'
|
||||
import { useQueryString } from '@/utils/hooks/useQueryString'
|
||||
import {
|
||||
RefreshIntervalValue,
|
||||
getRefreshIntervalInMs,
|
||||
isValidRefreshInterval,
|
||||
} from '../utils/intervals'
|
||||
|
||||
type DashboardContextType = {
|
||||
filter: FilterValue
|
||||
|
|
@ -36,6 +41,8 @@ type DashboardContextType = {
|
|||
isDashboardSelected: boolean
|
||||
isDashboardLoading: boolean
|
||||
dashboard: DashboardInfo | undefined
|
||||
refreshInterval: RefreshIntervalValue | null
|
||||
setRefreshInterval: StateUpdater<RefreshIntervalValue | 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(() => {
|
||||
setQuery({
|
||||
...getQuery(),
|
||||
...(refreshInterval !== null && { refresh: refreshInterval }),
|
||||
dashboard: dashboardId.toString(),
|
||||
interval: filter.interval,
|
||||
...(filter.interval === 'custom' && {
|
||||
|
|
@ -148,7 +168,29 @@ export const DashboardContextProvider = ({
|
|||
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
|
||||
|
||||
|
|
@ -164,6 +206,8 @@ export const DashboardContextProvider = ({
|
|||
isDashboardSelected,
|
||||
isDashboardLoading: dashboard.isLoading,
|
||||
dashboard: dashboard.data,
|
||||
refreshInterval,
|
||||
setRefreshInterval,
|
||||
}),
|
||||
[
|
||||
filter,
|
||||
|
|
@ -175,6 +219,8 @@ export const DashboardContextProvider = ({
|
|||
isDashboardSelected,
|
||||
dashboard.isLoading,
|
||||
dashboard.data,
|
||||
refreshInterval,
|
||||
setRefreshInterval,
|
||||
]
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
}
|
||||
Loading…
Reference in New Issue