mirror of
https://github.com/PostHog/posthog.git
synced 2024-11-21 21:49:51 +01:00
feat(BI): Series settings (#24082)
Co-authored-by: Eric Duong <eric@posthog.com> Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
parent
668b792254
commit
af4ef0bd3c
@ -63,6 +63,8 @@ export interface LemonButtonPropsBase
|
||||
disabled?: boolean
|
||||
/** Like plain `disabled`, except we enforce a reason to be shown in the tooltip. */
|
||||
disabledReason?: string | null | false
|
||||
/** Class for the wrapping div when the button is disabled */
|
||||
disabledReasonWrapperClass?: string
|
||||
noPadding?: boolean
|
||||
size?: 'xsmall' | 'small' | 'medium' | 'large'
|
||||
'data-attr'?: string
|
||||
@ -116,6 +118,7 @@ export const LemonButton: React.FunctionComponent<LemonButtonProps & React.RefAt
|
||||
className,
|
||||
disabled,
|
||||
disabledReason,
|
||||
disabledReasonWrapperClass,
|
||||
loading,
|
||||
type = 'tertiary',
|
||||
status = 'default',
|
||||
@ -242,7 +245,11 @@ export const LemonButton: React.FunctionComponent<LemonButtonProps & React.RefAt
|
||||
workingButton = (
|
||||
<Tooltip title={tooltipContent} placement={tooltipPlacement}>
|
||||
{/* If the button is a `button` element and disabled, wrap it in a div so that the tooltip works */}
|
||||
{disabled && ButtonComponent === 'button' ? <div>{workingButton}</div> : workingButton}
|
||||
{disabled && ButtonComponent === 'button' ? (
|
||||
<div className={clsx(disabledReasonWrapperClass)}>{workingButton}</div>
|
||||
) : (
|
||||
workingButton
|
||||
)}
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
@ -289,8 +289,9 @@ limit 100`,
|
||||
|
||||
const HogQLForDataWarehouse: HogQLQuery = {
|
||||
kind: NodeKind.HogQLQuery,
|
||||
query: `select toDate(timestamp) as timestamp, event as event
|
||||
from events
|
||||
query: `select toDate(timestamp) as timestamp, count()
|
||||
from events
|
||||
group by timestamp
|
||||
limit 100`,
|
||||
explain: true,
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ import { ensureTooltip } from 'scenes/insights/views/LineGraph/LineGraph'
|
||||
import { themeLogic } from '~/layout/navigation-3000/themeLogic'
|
||||
import { ChartDisplayType, GraphType } from '~/types'
|
||||
|
||||
import { dataVisualizationLogic } from '../../dataVisualizationLogic'
|
||||
import { dataVisualizationLogic, formatDataWithSettings } from '../../dataVisualizationLogic'
|
||||
import { displayLogic } from '../../displayLogic'
|
||||
|
||||
Chart.register(annotationPlugin)
|
||||
@ -179,9 +179,9 @@ export const LineGraph = (): JSX.Element => {
|
||||
tooltipRoot.render(
|
||||
<div className="InsightTooltip">
|
||||
<LemonTable
|
||||
dataSource={yData.map(({ data, column }) => ({
|
||||
dataSource={yData.map(({ data, column, settings }) => ({
|
||||
series: column.name,
|
||||
data: data[referenceDataPoint.dataIndex],
|
||||
data: formatDataWithSettings(data[referenceDataPoint.dataIndex], settings),
|
||||
}))}
|
||||
columns={[
|
||||
{
|
||||
|
@ -1,12 +1,15 @@
|
||||
import { IconPlusSmall, IconTrash } from '@posthog/icons'
|
||||
import { LemonButton, LemonLabel, LemonSelect } from '@posthog/lemon-ui'
|
||||
import { IconGear, IconPlusSmall, IconTrash } from '@posthog/icons'
|
||||
import { LemonButton, LemonInput, LemonLabel, LemonSelect, Popover } from '@posthog/lemon-ui'
|
||||
import { useActions, useValues } from 'kea'
|
||||
import { Form } from 'kea-forms'
|
||||
import { LemonField } from 'lib/lemon-ui/LemonField'
|
||||
|
||||
import { dataVisualizationLogic } from '../dataVisualizationLogic'
|
||||
import { AxisSeries, dataVisualizationLogic } from '../dataVisualizationLogic'
|
||||
import { ySeriesLogic } from './ySeriesLogic'
|
||||
|
||||
export const SeriesTab = (): JSX.Element => {
|
||||
const { columns, xData, yData, responseLoading } = useValues(dataVisualizationLogic)
|
||||
const { updateXSeries, updateYSeries, addYSeries, deleteYSeries } = useActions(dataVisualizationLogic)
|
||||
const { updateXSeries, addYSeries } = useActions(dataVisualizationLogic)
|
||||
|
||||
const options = columns.map(({ name, label }) => ({
|
||||
value: name,
|
||||
@ -29,29 +32,8 @@ export const SeriesTab = (): JSX.Element => {
|
||||
}}
|
||||
/>
|
||||
<LemonLabel className="mt-4">Y-axis</LemonLabel>
|
||||
{(yData ?? [null]).map((series, index) => (
|
||||
<div className="flex gap-1 mb-1" key={series?.column.name}>
|
||||
<LemonSelect
|
||||
className="grow"
|
||||
value={series !== null ? series.column.label : 'None'}
|
||||
options={options}
|
||||
disabledReason={responseLoading ? 'Query loading...' : undefined}
|
||||
onChange={(value) => {
|
||||
const column = columns.find((n) => n.name === value)
|
||||
if (column) {
|
||||
updateYSeries(index, column.name)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<LemonButton
|
||||
key="delete"
|
||||
icon={<IconTrash />}
|
||||
status="danger"
|
||||
title="Delete Y-series"
|
||||
noPadding
|
||||
onClick={() => deleteYSeries(index)}
|
||||
/>
|
||||
</div>
|
||||
{yData.map((series, index) => (
|
||||
<YSeries series={series} index={index} key={series?.column.name} />
|
||||
))}
|
||||
<LemonButton
|
||||
className="mt-1"
|
||||
@ -65,3 +47,77 @@ export const SeriesTab = (): JSX.Element => {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const YSeries = ({ series, index }: { series: AxisSeries<number>; index: number }): JSX.Element => {
|
||||
const { columns, responseLoading, dataVisualizationProps } = useValues(dataVisualizationLogic)
|
||||
const { updateYSeries, deleteYSeries } = useActions(dataVisualizationLogic)
|
||||
|
||||
const seriesLogicProps = { series, seriesIndex: index, dataVisualizationProps }
|
||||
const seriesLogic = ySeriesLogic(seriesLogicProps)
|
||||
|
||||
const { isSettingsOpen, canOpenSettings } = useValues(seriesLogic)
|
||||
const { setSettingsOpen, submitFormatting } = useActions(seriesLogic)
|
||||
|
||||
const options = columns.map(({ name, label }) => ({
|
||||
value: name,
|
||||
label,
|
||||
}))
|
||||
|
||||
return (
|
||||
<div className="flex gap-1 mb-1">
|
||||
<LemonSelect
|
||||
className="grow"
|
||||
value={series !== null ? series.column.label : 'None'}
|
||||
options={options}
|
||||
disabledReason={responseLoading ? 'Query loading...' : undefined}
|
||||
onChange={(value) => {
|
||||
const column = columns.find((n) => n.name === value)
|
||||
if (column) {
|
||||
updateYSeries(index, column.name)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Popover
|
||||
overlay={
|
||||
<Form logic={ySeriesLogic} props={seriesLogicProps} formKey="formatting" className="m-2 space-y-2">
|
||||
<LemonField name="style" label="Style" className="gap-1">
|
||||
<LemonSelect
|
||||
options={[
|
||||
{ value: 'none', label: 'None' },
|
||||
{ value: 'number', label: 'Number' },
|
||||
{ value: 'percent', label: 'Percentage' },
|
||||
]}
|
||||
/>
|
||||
</LemonField>
|
||||
<LemonField name="prefix" label="Prefix">
|
||||
<LemonInput placeholder="$" />
|
||||
</LemonField>
|
||||
<LemonField name="suffix" label="Suffix">
|
||||
<LemonInput placeholder="USD" />
|
||||
</LemonField>
|
||||
</Form>
|
||||
}
|
||||
visible={isSettingsOpen}
|
||||
placement="bottom"
|
||||
onClickOutside={() => submitFormatting()}
|
||||
>
|
||||
<LemonButton
|
||||
key="seriesSettings"
|
||||
icon={<IconGear />}
|
||||
noPadding
|
||||
onClick={() => setSettingsOpen(true)}
|
||||
disabledReason={!canOpenSettings && 'Select a column first'}
|
||||
disabledReasonWrapperClass="flex"
|
||||
/>
|
||||
</Popover>
|
||||
<LemonButton
|
||||
key="delete"
|
||||
icon={<IconTrash />}
|
||||
status="danger"
|
||||
title="Delete Y-series"
|
||||
noPadding
|
||||
onClick={() => deleteYSeries(index)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,63 @@
|
||||
import { actions, connect, kea, key, path, props, reducers, selectors } from 'kea'
|
||||
import { forms } from 'kea-forms'
|
||||
|
||||
import {
|
||||
AxisSeries,
|
||||
dataVisualizationLogic,
|
||||
DataVisualizationLogicProps,
|
||||
EmptyYAxisSeries,
|
||||
} from '../dataVisualizationLogic'
|
||||
import type { ySeriesLogicType } from './ySeriesLogicType'
|
||||
|
||||
export interface YSeriesLogicProps {
|
||||
series: AxisSeries<number>
|
||||
seriesIndex: number
|
||||
dataVisualizationProps: DataVisualizationLogicProps
|
||||
}
|
||||
|
||||
export const ySeriesLogic = kea<ySeriesLogicType>([
|
||||
path(['queries', 'nodes', 'DataVisualization', 'Components', 'ySeriesLogic']),
|
||||
key((props) => props.series?.column?.name ?? `new-${props.seriesIndex}`),
|
||||
connect((props: YSeriesLogicProps) => ({
|
||||
actions: [dataVisualizationLogic(props.dataVisualizationProps), ['updateYSeries']],
|
||||
})),
|
||||
props({ series: EmptyYAxisSeries } as YSeriesLogicProps),
|
||||
actions({
|
||||
setSettingsOpen: (open: boolean) => ({ open }),
|
||||
}),
|
||||
reducers({
|
||||
isSettingsOpen: [
|
||||
false as boolean,
|
||||
{
|
||||
setSettingsOpen: (_, { open }) => open,
|
||||
},
|
||||
],
|
||||
}),
|
||||
selectors({
|
||||
canOpenSettings: [
|
||||
(_s, p) => [p.series],
|
||||
(series) => {
|
||||
return series !== EmptyYAxisSeries
|
||||
},
|
||||
],
|
||||
}),
|
||||
forms(({ actions, props }) => ({
|
||||
formatting: {
|
||||
defaults: {
|
||||
prefix: props.series?.settings?.formatting?.prefix ?? '',
|
||||
suffix: props.series?.settings?.formatting?.suffix ?? '',
|
||||
style: props.series?.settings?.formatting?.style ?? 'none',
|
||||
},
|
||||
submit: async (format) => {
|
||||
actions.updateYSeries(props.seriesIndex, props.series.column.name, {
|
||||
formatting: {
|
||||
prefix: format.prefix,
|
||||
suffix: format.suffix,
|
||||
style: format.style,
|
||||
},
|
||||
})
|
||||
actions.setSettingsOpen(false)
|
||||
},
|
||||
},
|
||||
})),
|
||||
])
|
@ -1,10 +1,11 @@
|
||||
import { actions, afterMount, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea'
|
||||
import { subscriptions } from 'kea-subscriptions'
|
||||
import mergeObject from 'lodash.merge'
|
||||
import { insightSceneLogic } from 'scenes/insights/insightSceneLogic'
|
||||
import { teamLogic } from 'scenes/teamLogic'
|
||||
|
||||
import { insightVizDataCollectionId } from '~/queries/nodes/InsightViz/InsightViz'
|
||||
import { AnyResponseType, ChartAxis, DataVisualizationNode } from '~/queries/schema'
|
||||
import { AnyResponseType, ChartAxis, ChartSettingsFormatting, DataVisualizationNode } from '~/queries/schema'
|
||||
import { QueryContext } from '~/queries/types'
|
||||
import { ChartDisplayType, InsightLogicProps, ItemMode } from '~/types'
|
||||
|
||||
@ -24,9 +25,14 @@ export interface Column {
|
||||
dataIndex: number
|
||||
}
|
||||
|
||||
export interface AxisSeriesSettings {
|
||||
formatting?: ChartSettingsFormatting
|
||||
}
|
||||
|
||||
export interface AxisSeries<T> {
|
||||
column: Column
|
||||
data: T[]
|
||||
settings?: AxisSeriesSettings
|
||||
}
|
||||
|
||||
export interface DataVisualizationLogicProps {
|
||||
@ -38,6 +44,50 @@ export interface DataVisualizationLogicProps {
|
||||
cachedResults?: AnyResponseType
|
||||
}
|
||||
|
||||
export interface SelectedYAxis {
|
||||
name: string
|
||||
settings: AxisSeriesSettings
|
||||
}
|
||||
|
||||
export const EmptyYAxisSeries: AxisSeries<number> = {
|
||||
column: {
|
||||
name: 'None',
|
||||
type: 'None',
|
||||
label: 'None',
|
||||
dataIndex: -1,
|
||||
},
|
||||
data: [],
|
||||
}
|
||||
|
||||
const DefaultAxisSettings: AxisSeriesSettings = {
|
||||
formatting: {
|
||||
prefix: '',
|
||||
suffix: '',
|
||||
},
|
||||
}
|
||||
|
||||
export const formatDataWithSettings = (data: number, settings?: AxisSeriesSettings): string => {
|
||||
let dataAsString = `${data}`
|
||||
|
||||
if (settings?.formatting?.style === 'number') {
|
||||
dataAsString = data.toLocaleString()
|
||||
}
|
||||
|
||||
if (settings?.formatting?.style === 'percent') {
|
||||
dataAsString = `${(data * 100).toLocaleString()}%`
|
||||
}
|
||||
|
||||
if (settings?.formatting?.prefix) {
|
||||
dataAsString = `${settings.formatting.prefix}${dataAsString}`
|
||||
}
|
||||
|
||||
if (settings?.formatting?.suffix) {
|
||||
dataAsString = `${dataAsString}${settings.formatting.suffix}`
|
||||
}
|
||||
|
||||
return dataAsString
|
||||
}
|
||||
|
||||
export const dataVisualizationLogic = kea<dataVisualizationLogicType>([
|
||||
key((props) => props.key),
|
||||
path(['queries', 'nodes', 'DataVisualization', 'dataVisualizationLogic']),
|
||||
@ -63,11 +113,12 @@ export const dataVisualizationLogic = kea<dataVisualizationLogicType>([
|
||||
updateXSeries: (columnName: string) => ({
|
||||
columnName,
|
||||
}),
|
||||
updateYSeries: (seriesIndex: number, columnName: string) => ({
|
||||
updateYSeries: (seriesIndex: number, columnName: string, settings?: AxisSeriesSettings) => ({
|
||||
seriesIndex,
|
||||
columnName,
|
||||
settings,
|
||||
}),
|
||||
addYSeries: (columnName?: string) => ({ columnName }),
|
||||
addYSeries: (columnName?: string, settings?: AxisSeriesSettings) => ({ columnName, settings }),
|
||||
deleteYSeries: (seriesIndex: number) => ({ seriesIndex }),
|
||||
clearAxis: true,
|
||||
setQuery: (node: DataVisualizationNode) => ({ node }),
|
||||
@ -88,28 +139,36 @@ export const dataVisualizationLogic = kea<dataVisualizationLogicType>([
|
||||
},
|
||||
],
|
||||
selectedYAxis: [
|
||||
null as (string | null)[] | null,
|
||||
null as (SelectedYAxis | null)[] | null,
|
||||
{
|
||||
clearAxis: () => null,
|
||||
addYSeries: (state, { columnName }) => {
|
||||
addYSeries: (state, { columnName, settings }) => {
|
||||
if (!state && columnName !== undefined) {
|
||||
return [columnName]
|
||||
return [{ name: columnName, settings: settings ?? DefaultAxisSettings }]
|
||||
}
|
||||
|
||||
if (!state) {
|
||||
return [null]
|
||||
}
|
||||
|
||||
return [...state, columnName === undefined ? null : columnName]
|
||||
return [
|
||||
...state,
|
||||
columnName === undefined
|
||||
? null
|
||||
: { name: columnName, settings: settings ?? DefaultAxisSettings },
|
||||
]
|
||||
},
|
||||
updateYSeries: (state, { seriesIndex, columnName }) => {
|
||||
updateYSeries: (state, { seriesIndex, columnName, settings }) => {
|
||||
if (!state) {
|
||||
return null
|
||||
}
|
||||
|
||||
const ySeries = [...state]
|
||||
|
||||
ySeries[seriesIndex] = columnName
|
||||
ySeries[seriesIndex] = {
|
||||
name: columnName,
|
||||
settings: mergeObject(ySeries[seriesIndex]?.settings ?? {}, settings),
|
||||
}
|
||||
return ySeries
|
||||
},
|
||||
deleteYSeries: (state, { seriesIndex }) => {
|
||||
@ -192,30 +251,22 @@ export const dataVisualizationLogic = kea<dataVisualizationLogicType>([
|
||||
],
|
||||
yData: [
|
||||
(state) => [state.selectedYAxis, state.response, state.columns],
|
||||
(ySeries, response, columns): null | AxisSeries<number>[] => {
|
||||
(ySeries, response, columns): AxisSeries<number>[] => {
|
||||
if (!response || ySeries === null || ySeries.length === 0) {
|
||||
return null
|
||||
return [EmptyYAxisSeries]
|
||||
}
|
||||
|
||||
const data: any[] = response?.['results'] ?? response?.['result'] ?? []
|
||||
|
||||
return ySeries
|
||||
.map((name): AxisSeries<number> | null => {
|
||||
if (!name) {
|
||||
return {
|
||||
column: {
|
||||
name: 'None',
|
||||
type: 'None',
|
||||
label: 'None',
|
||||
dataIndex: -1,
|
||||
},
|
||||
data: [],
|
||||
}
|
||||
.map((series): AxisSeries<number> | null => {
|
||||
if (!series) {
|
||||
return EmptyYAxisSeries
|
||||
}
|
||||
|
||||
const column = columns.find((n) => n.name === name)
|
||||
const column = columns.find((n) => n.name === series.name)
|
||||
if (!column) {
|
||||
return null
|
||||
return EmptyYAxisSeries
|
||||
}
|
||||
|
||||
return {
|
||||
@ -227,6 +278,7 @@ export const dataVisualizationLogic = kea<dataVisualizationLogicType>([
|
||||
return 0
|
||||
}
|
||||
}),
|
||||
settings: series.settings,
|
||||
}
|
||||
})
|
||||
.filter((series): series is AxisSeries<number> => Boolean(series))
|
||||
@ -260,6 +312,7 @@ export const dataVisualizationLogic = kea<dataVisualizationLogicType>([
|
||||
}
|
||||
},
|
||||
],
|
||||
dataVisualizationProps: [() => [(_, props) => props], (props): DataVisualizationLogicProps => props],
|
||||
}),
|
||||
listeners(({ props }) => ({
|
||||
setQuery: ({ node }) => {
|
||||
@ -290,7 +343,7 @@ export const dataVisualizationLogic = kea<dataVisualizationLogicType>([
|
||||
|
||||
if (yAxis && yAxis.length) {
|
||||
yAxis.forEach((axis) => {
|
||||
actions.addYSeries(axis.column)
|
||||
actions.addYSeries(axis.column, axis.settings)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -325,22 +378,23 @@ export const dataVisualizationLogic = kea<dataVisualizationLogicType>([
|
||||
},
|
||||
selectedXAxis: (value: string | null) => {
|
||||
if (props.setQuery) {
|
||||
const yColumns = values.selectedYAxis?.filter((n: string | null): n is string => Boolean(n)) ?? []
|
||||
const yColumns =
|
||||
values.selectedYAxis?.filter((n: SelectedYAxis | null): n is SelectedYAxis => Boolean(n)) ?? []
|
||||
const xColumn: ChartAxis | undefined = value !== null ? { column: value } : undefined
|
||||
|
||||
props.setQuery({
|
||||
...props.query,
|
||||
chartSettings: {
|
||||
...(props.query.chartSettings ?? {}),
|
||||
yAxis: yColumns.map((n) => ({ column: n })),
|
||||
yAxis: yColumns.map((n) => ({ column: n.name, settings: n.settings })),
|
||||
xAxis: xColumn,
|
||||
},
|
||||
})
|
||||
}
|
||||
},
|
||||
selectedYAxis: (value: (string | null)[] | null) => {
|
||||
selectedYAxis: (value: (SelectedYAxis | null)[] | null) => {
|
||||
if (props.setQuery) {
|
||||
const yColumns = value?.filter((n: string | null): n is string => Boolean(n)) ?? []
|
||||
const yColumns = value?.filter((n: SelectedYAxis | null): n is SelectedYAxis => Boolean(n)) ?? []
|
||||
const xColumn: ChartAxis | undefined =
|
||||
values.selectedXAxis !== null ? { column: values.selectedXAxis } : undefined
|
||||
|
||||
@ -348,7 +402,7 @@ export const dataVisualizationLogic = kea<dataVisualizationLogicType>([
|
||||
...props.query,
|
||||
chartSettings: {
|
||||
...(props.query.chartSettings ?? {}),
|
||||
yAxis: yColumns.map((n) => ({ column: n })),
|
||||
yAxis: yColumns.map((n) => ({ column: n.name, settings: n.settings })),
|
||||
xAxis: xColumn,
|
||||
},
|
||||
})
|
||||
|
@ -1993,6 +1993,15 @@
|
||||
"properties": {
|
||||
"column": {
|
||||
"type": "string"
|
||||
},
|
||||
"settings": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"formatting": {
|
||||
"$ref": "#/definitions/ChartSettingsFormatting"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": ["column"],
|
||||
@ -2013,6 +2022,43 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ChartSettings": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"goalLines": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/GoalLine"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"xAxis": {
|
||||
"$ref": "#/definitions/ChartAxis"
|
||||
},
|
||||
"yAxis": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/ChartAxis"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"ChartSettingsFormatting": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"prefix": {
|
||||
"type": "string"
|
||||
},
|
||||
"style": {
|
||||
"enum": ["none", "number", "percent"],
|
||||
"type": "string"
|
||||
},
|
||||
"suffix": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"ClickhouseQueryProgress": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
@ -2670,25 +2716,7 @@
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"chartSettings": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"goalLines": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/GoalLine"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"xAxis": {
|
||||
"$ref": "#/definitions/ChartAxis"
|
||||
},
|
||||
"yAxis": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/ChartAxis"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
"$ref": "#/definitions/ChartSettings"
|
||||
},
|
||||
"display": {
|
||||
"$ref": "#/definitions/ChartDisplayType"
|
||||
|
@ -551,9 +551,18 @@ export interface GoalLine {
|
||||
|
||||
export interface ChartAxis {
|
||||
column: string
|
||||
settings?: {
|
||||
formatting?: ChartSettingsFormatting
|
||||
}
|
||||
}
|
||||
|
||||
interface ChartSettings {
|
||||
export interface ChartSettingsFormatting {
|
||||
prefix?: string
|
||||
suffix?: string
|
||||
style?: 'none' | 'number' | 'percent'
|
||||
}
|
||||
|
||||
export interface ChartSettings {
|
||||
xAxis?: ChartAxis
|
||||
yAxis?: ChartAxis[]
|
||||
goalLines?: GoalLine[]
|
||||
|
@ -139,6 +139,7 @@
|
||||
"kea-test-utils": "^0.2.4",
|
||||
"kea-waitfor": "^0.2.1",
|
||||
"kea-window-values": "^3.0.0",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"maplibre-gl": "^3.5.1",
|
||||
"md5": "^2.3.0",
|
||||
"monaco-editor": "^0.49.0",
|
||||
@ -223,6 +224,7 @@
|
||||
"@types/image-blob-reduce": "^4.1.1",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/jest-image-snapshot": "^6.1.0",
|
||||
"@types/lodash.merge": "^4.6.9",
|
||||
"@types/md5": "^2.3.0",
|
||||
"@types/node": "^18.11.9",
|
||||
"@types/papaparse": "^5.3.8",
|
||||
|
@ -238,6 +238,9 @@ dependencies:
|
||||
kea-window-values:
|
||||
specifier: ^3.0.0
|
||||
version: 3.0.0(kea@3.1.5)
|
||||
lodash.merge:
|
||||
specifier: ^4.6.2
|
||||
version: 4.6.2
|
||||
maplibre-gl:
|
||||
specifier: ^3.5.1
|
||||
version: 3.5.1
|
||||
@ -488,6 +491,9 @@ devDependencies:
|
||||
'@types/jest-image-snapshot':
|
||||
specifier: ^6.1.0
|
||||
version: 6.1.0
|
||||
'@types/lodash.merge':
|
||||
specifier: ^4.6.9
|
||||
version: 4.6.9
|
||||
'@types/node':
|
||||
specifier: ^18.11.9
|
||||
version: 18.11.9
|
||||
@ -8355,6 +8361,12 @@ packages:
|
||||
resolution: {integrity: sha512-PecSzorDGdabF57OBeQO/xFbAkYWo88g4Xvnsx7LRwqLC17I7OoKtA3bQB9uXkY6UkMWCOsA8HSVpaoitscdXw==}
|
||||
dev: false
|
||||
|
||||
/@types/lodash.merge@4.6.9:
|
||||
resolution: {integrity: sha512-23sHDPmzd59kUgWyKGiOMO2Qb9YtqRO/x4IhkgNUiPQ1+5MUVqi6bCZeq9nBJ17msjIMbEIO5u+XW4Kz6aGUhQ==}
|
||||
dependencies:
|
||||
'@types/lodash': 4.14.188
|
||||
dev: true
|
||||
|
||||
/@types/lodash@4.14.188:
|
||||
resolution: {integrity: sha512-zmEmF5OIM3rb7SbLCFYoQhO4dGt2FRM9AMkxvA3LaADOF1n8in/zGJlWji9fmafLoNyz+FoL6FE0SLtGIArD7w==}
|
||||
dev: true
|
||||
@ -15659,7 +15671,6 @@ packages:
|
||||
|
||||
/lodash.merge@4.6.2:
|
||||
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||
dev: true
|
||||
|
||||
/lodash.once@4.1.1:
|
||||
resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==}
|
||||
|
@ -146,13 +146,6 @@ class StatusItem(BaseModel):
|
||||
value: str
|
||||
|
||||
|
||||
class ChartAxis(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra="forbid",
|
||||
)
|
||||
column: str
|
||||
|
||||
|
||||
class ChartDisplayType(StrEnum):
|
||||
ACTIONS_LINE_GRAPH = "ActionsLineGraph"
|
||||
ACTIONS_BAR = "ActionsBar"
|
||||
@ -166,6 +159,21 @@ class ChartDisplayType(StrEnum):
|
||||
WORLD_MAP = "WorldMap"
|
||||
|
||||
|
||||
class Style(StrEnum):
|
||||
NONE = "none"
|
||||
NUMBER = "number"
|
||||
PERCENT = "percent"
|
||||
|
||||
|
||||
class ChartSettingsFormatting(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra="forbid",
|
||||
)
|
||||
prefix: Optional[str] = None
|
||||
style: Optional[Style] = None
|
||||
suffix: Optional[str] = None
|
||||
|
||||
|
||||
class ClickhouseQueryProgress(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra="forbid",
|
||||
@ -1851,6 +1859,30 @@ class CachedWebTopClicksQueryResponse(BaseModel):
|
||||
types: Optional[list] = None
|
||||
|
||||
|
||||
class Settings(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra="forbid",
|
||||
)
|
||||
formatting: Optional[ChartSettingsFormatting] = None
|
||||
|
||||
|
||||
class ChartAxis(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra="forbid",
|
||||
)
|
||||
column: str
|
||||
settings: Optional[Settings] = None
|
||||
|
||||
|
||||
class ChartSettings(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra="forbid",
|
||||
)
|
||||
goalLines: Optional[list[GoalLine]] = None
|
||||
xAxis: Optional[ChartAxis] = None
|
||||
yAxis: Optional[list[ChartAxis]] = None
|
||||
|
||||
|
||||
class Response(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra="forbid",
|
||||
@ -2009,15 +2041,6 @@ class Response7(BaseModel):
|
||||
)
|
||||
|
||||
|
||||
class ChartSettings(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra="forbid",
|
||||
)
|
||||
goalLines: Optional[list[GoalLine]] = None
|
||||
xAxis: Optional[ChartAxis] = None
|
||||
yAxis: Optional[list[ChartAxis]] = None
|
||||
|
||||
|
||||
class DataWarehousePersonPropertyFilter(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra="forbid",
|
||||
|
Loading…
Reference in New Issue
Block a user