Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
6aec597
delegate parse filters straight to ApiQueryParser
RobertJoonas Feb 20, 2026
ec0c526
add missing dimensions parsing
RobertJoonas Feb 20, 2026
1b9db59
API v2: add partial_time_labels and present_index to meta
RobertJoonas Mar 4, 2026
bec63c3
API v2 comparisons
RobertJoonas Mar 16, 2026
698082e
allow fixing now in the new endpoint via conn.private
RobertJoonas Mar 9, 2026
9eabcb5
fix tests
RobertJoonas Mar 10, 2026
7178837
fix and transform main_graph_test to target the new endpoint
RobertJoonas Mar 18, 2026
3dcfad6
remove comparisons from legacy timeseries
RobertJoonas Mar 9, 2026
1ab9e59
remove main_graph from Api.StatsController
RobertJoonas Mar 10, 2026
f3f99d4
fix comparisons tests
RobertJoonas Mar 30, 2026
a0507e5
Merge remote-tracking branch 'origin/master' into main-graph-v2-backend
RobertJoonas Mar 31, 2026
44f64ad
Merge remote-tracking branch 'origin/master' into main-graph-v2-backend
RobertJoonas Apr 1, 2026
bd2b9a2
partial time labels to empty list
RobertJoonas Apr 1, 2026
c41641d
Merge remote-tracking branch 'origin/master' into main-graph-v2-backend
RobertJoonas Apr 6, 2026
dd8f2d6
fix partial time labels
RobertJoonas Apr 6, 2026
768da80
include.empty_metrics
RobertJoonas Apr 9, 2026
9b1bfa0
Merge remote-tracking branch 'origin/master' into main-graph-v2-backend
RobertJoonas Apr 9, 2026
938c9a4
make sure CE test is not trying to insert revenue goal
RobertJoonas Apr 9, 2026
37411cf
add missing tests and parsing include.empty_metrics
RobertJoonas Apr 13, 2026
591e538
add meta.comparison_partial_time_labels
RobertJoonas Apr 13, 2026
448f3e4
fix CI (ce_test, codespell, credo)
RobertJoonas Apr 13, 2026
bb79cab
Merge remote-tracking branch 'origin/master' into main-graph-v2-backend
RobertJoonas Apr 13, 2026
098bc77
Refactor main graph to d3.js (#6159)
apata Apr 15, 2026
a7c17c6
Draw points after lines, underline before line
apata Apr 15, 2026
4e534cf
Change order of drawing main and comparison series, make comparison s…
apata Apr 15, 2026
3259ca5
backend: limit time:minute to 24h periods
RobertJoonas Apr 15, 2026
7c922cd
account for DST as well with minute interval limit
RobertJoonas Apr 15, 2026
84d961b
Fix issue with x axis on Firefox (weird results measuring empty svg t…
apata Apr 15, 2026
f04027c
Fix invalid type
apata Apr 15, 2026
e674247
move valid intervals and tests to FE
RobertJoonas Apr 16, 2026
991da1a
add back present index and make dashed line only for that
RobertJoonas Apr 16, 2026
8d13c08
Move points highlighting to props-based effect
apata Apr 15, 2026
bb02788
Every graph handler is set in separate effect
apata Apr 16, 2026
4176c48
Clarify generic type
apata Apr 16, 2026
97af247
Remove unnecessary dependency
apata Apr 16, 2026
e049517
Works
apata Apr 16, 2026
6e6da31
Make tooltip persistent on mobile
apata Apr 16, 2026
3870848
Clarify graph
apata Apr 16, 2026
8378fc8
Change order in which points are drawn
apata Apr 17, 2026
840fab6
Fix format
apata Apr 17, 2026
f630a67
Fix tests
apata Apr 17, 2026
f160fd7
Clarify tooltip
apata Apr 17, 2026
bba5584
extract intervals.ts as a separate module
RobertJoonas Apr 17, 2026
9564e0d
add Interval enum + fix zoomToPeriod
RobertJoonas Apr 17, 2026
532d760
fix tooltips showing in the correct x,y
RobertJoonas Apr 19, 2026
11d9df1
Fix intervals
RobertJoonas Apr 19, 2026
542e008
Reattach graph event handlers on data change
apata Apr 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .codespellignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ referer
referers
statics
firs
IST
56 changes: 56 additions & 0 deletions assets/js/dashboard/components/graph-tooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React, { ReactNode, useLayoutEffect, useRef, useState } from 'react'
import {
Transition,
TransitionClasses,
TransitionEvents
} from '@headlessui/react'

export const GraphTooltipWrapper = ({
x,
y,
maxX,
minWidth,
children,
className,
transition
}: {
x: number
y: number
maxX: number
minWidth: number
children: ReactNode
className?: string
transition?: TransitionClasses & TransitionEvents
}) => {
const ref = useRef<HTMLDivElement>(null)
const xOffsetFromCursor = 12
const yOffsetFromCursor = 24
const [measuredWidth, setMeasuredWidth] = useState(minWidth)
// clamp to prevent left/right overflow
const rawLeft = x + xOffsetFromCursor
const tooltipLeft = Math.max(0, Math.min(rawLeft, maxX - measuredWidth))

useLayoutEffect(() => {
if (!ref.current) {
return
}
setMeasuredWidth(ref.current.offsetWidth)
}, [children, className, minWidth])

return (
<Transition as={React.Fragment} appear show {...transition}>
<div
ref={ref}
className={className}
style={{
minWidth,
left: tooltipLeft,
top: y,
transform: `translateY(-100%) translateY(-${yOffsetFromCursor}px)`
}}
>
{children}
</div>
</Transition>
)
}
106 changes: 106 additions & 0 deletions assets/js/dashboard/components/graph.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { getSuggestedXTickValues, getXDomain } from './graph'
import * as d3 from 'd3'

describe(`${getXDomain.name}`, () => {
it('returns [0, 1] for a single bucket to avoid a zero-width domain', () => {
expect(getXDomain(1)).toEqual([0, 1])
})
it('returns [0, bucketCount - 1] for multiple buckets', () => {
expect(getXDomain(5)).toEqual([0, 4])
})
})

const anyRange = [0, 100]
describe(`${getSuggestedXTickValues.name}`, () => {
it('handles 1 bucket', () => {
const data = new Array(1).fill(0)
expect(
getSuggestedXTickValues(
d3.scaleLinear(getXDomain(data.length), anyRange),
data.length
)
).toEqual([[0, 1]])
})

it('handles 2 buckets', () => {
const data = new Array(2).fill(0)
expect(
getSuggestedXTickValues(
d3.scaleLinear(getXDomain(data.length), anyRange),
data.length
)
).toEqual([[0, 1]])
})

it('handles 7 buckets', () => {
const data = new Array(7).fill(0)
expect(
getSuggestedXTickValues(
d3.scaleLinear(getXDomain(data.length), anyRange),
data.length
)
).toEqual([
[0, 1, 2, 3, 4, 5, 6],
[0, 2, 4, 6],
[0, 5]
])
})

it('handles 24 buckets (day by hours)', () => {
const data = new Array(24).fill(0)
expect(
getSuggestedXTickValues(
d3.scaleLinear(getXDomain(data.length), anyRange),
data.length
)
).toEqual([
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22],
[0, 5, 10, 15, 20],
[0, 10, 20],
[0, 20]
])
})

it('handles 28 buckets', () => {
const data = new Array(28).fill(0)
expect(
getSuggestedXTickValues(
d3.scaleLinear(getXDomain(data.length), anyRange),
data.length
)
).toEqual([
[0, 5, 10, 15, 20, 25],
[0, 10, 20],
[0, 20]
])
})

it('handles 91 buckets', () => {
const data = new Array(91).fill(0)
expect(
getSuggestedXTickValues(
d3.scaleLinear(getXDomain(data.length), anyRange),
data.length
)
).toEqual([
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90],
[0, 20, 40, 60, 80],
[0, 50],
[0]
])
})

it('handles 700 buckets', () => {
const data = new Array(700).fill(0)
expect(
getSuggestedXTickValues(
d3.scaleLinear(getXDomain(data.length), anyRange),
data.length
)
).toEqual([
[0, 100, 200, 300, 400, 500, 600],
[0, 200, 400, 600],
[0, 500]
])
})
})
Loading
Loading