fix broken

This commit is contained in:
Nate Kelley 2025-05-28 23:24:44 -06:00
parent 2ba62e5abc
commit ee58591687
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
11 changed files with 69 additions and 46 deletions

View File

@ -1,4 +1,4 @@
import { DEFAULT_COLUMN_LABEL_FORMAT } from '@/api/asset_interfaces/metric';
import { DEFAULT_CHART_CONFIG, DEFAULT_COLUMN_LABEL_FORMAT } from '@/api/asset_interfaces/metric';
import type { ColumnLabelFormat, MetricChartProps } from '@/api/asset_interfaces/metric/charts';
import { Title } from '@/components/ui/typography';
import { useMount } from '@/hooks';
@ -28,7 +28,7 @@ export const BusterMetricChart: React.FC<BusterMetricChartProps> = React.memo(
const headerColumnLabelFormat: ColumnLabelFormat = useMemo(() => {
const isDerivedTitle = typeof metricHeader === 'object' && metricHeader?.columnId;
if (isDerivedTitle && columnLabelFormats[metricHeader.columnId]) {
return columnLabelFormats[metricHeader.columnId]!;
return columnLabelFormats[metricHeader.columnId] as ColumnLabelFormat;
}
return DEFAULT_COLUMN_LABEL_FORMAT;
}, [metricHeader, columnLabelFormats]);
@ -225,7 +225,7 @@ const fallbackAggregate = (
const isNumber =
columnLabelFormat.style === 'number' && columnLabelFormat.columnType === 'number';
const isValid = isNumber;
if (isValid) return aggregate;
if (isValid) return aggregate || DEFAULT_CHART_CONFIG.metricValueAggregate;
return 'first';
};

View File

@ -34,12 +34,15 @@ export function aggregateAndCreateDatasets<
...tooltipKeys
]);
const colFormats: Record<string, ColumnLabelFormatBase> = {};
allKeys.forEach((k) => {
for (const k of allKeys) {
colFormats[k] = columnLabelFormats[k] || {};
});
}
// Parse numeric values with replaceMissingDataWith
function parseNumeric(raw: any, fmt: ColumnLabelFormatBase): number | null {
function parseNumeric(
raw: string | number | null | undefined | Date | boolean,
fmt: ColumnLabelFormatBase
): number | null {
const rep = fmt.replaceMissingDataWith === null ? null : (fmt.replaceMissingDataWith ?? 0);
const n = Number(raw);
if (raw == null || raw === '' || Number.isNaN(n)) {
@ -51,7 +54,10 @@ export function aggregateAndCreateDatasets<
}
// Format tooltip values, treating missing as empty or replacement
function formatTooltip(raw: any, fmt: ColumnLabelFormatBase): string | number | boolean {
function formatTooltip(
raw: string | number | null | undefined | Date | boolean,
fmt: ColumnLabelFormatBase
): string | number | boolean {
if (raw == null || raw === '') {
if (fmt.replaceMissingDataWith !== undefined) {
const rep = fmt.replaceMissingDataWith;
@ -60,7 +66,7 @@ export function aggregateAndCreateDatasets<
return '';
}
if (typeof raw === 'boolean') return raw;
if (typeof raw === 'object') return raw;
if (typeof raw === 'object') return raw.toString();
const num = Number(raw);
return Number.isNaN(num) ? raw : num;
}
@ -71,17 +77,17 @@ export function aggregateAndCreateDatasets<
rows: T[]
): Array<{ id: string; rec: Record<string, string>; rows: T[] }> {
const map = new Map<string, { rec: Record<string, string>; rows: T[] }>();
rows.forEach((row) => {
for (const row of rows) {
const rec: Record<string, string> = {};
keys.forEach((k) => {
rec[k] = String((row as any)[k]);
});
for (const k of keys) {
rec[k] = String((row as Record<string, string | number | null | undefined>)[k]);
}
const id = keys.map((k) => rec[k]).join('|');
if (!map.has(id)) {
map.set(id, { rec, rows: [] });
}
map.get(id)?.rows.push(row);
});
}
return Array.from(map.entries()).map(([id, g]) => ({ id, rec: g.rec, rows: g.rows }));
}
@ -96,9 +102,9 @@ export function aggregateAndCreateDatasets<
// If there are categories, add them
if (catRec && catKeys.length > 0) {
catKeys.forEach((catKey) => {
for (const catKey of catKeys) {
labels.push({ key: catKey, value: catRec[catKey] });
});
}
} else if (yKeys.length + y2Keys.length === 1) {
// If no categories and only one y-axis, use the metric key
labels.push({ key: metric, value: '' });
@ -123,8 +129,8 @@ export function aggregateAndCreateDatasets<
// SCATTER: for each category group and each yKey
const scatterTicksKey: KV[] = xKeys.map((key) => ({ key, value: '' }));
catGroups.forEach(({ rec: catRec, rows }) => {
yKeys.forEach((yKey) => {
for (const { rec: catRec, rows } of catGroups) {
for (const yKey of yKeys) {
const fmtY = colFormats[yKey];
const axisType = 'y';
@ -144,7 +150,7 @@ export function aggregateAndCreateDatasets<
const tooltipArr = validRows.map((r) => {
const pts: KV[] = [];
if (tooltipKeys.length) {
tooltipKeys.forEach((k) => {
for (const k of tooltipKeys) {
const tooltip: KV = {
key: k,
value: formatTooltip(r[k], colFormats[k] || {})
@ -157,9 +163,9 @@ export function aggregateAndCreateDatasets<
}
pts.push(tooltip);
});
}
} else {
xKeys.forEach((k) => {
for (const k of xKeys) {
const tooltip: KV = {
key: k,
value: formatTooltip(r[k], colFormats[k] || {})
@ -172,14 +178,14 @@ export function aggregateAndCreateDatasets<
}
pts.push(tooltip);
});
}
pts.push({ key: yKey, value: formatTooltip(r[yKey], fmtY) });
if (sizeArr) {
if (sizeArr && sizeKey) {
pts.push({
key: sizeKey!,
value: formatTooltip(r[sizeKey!], colFormats[sizeKey!] || {})
key: sizeKey,
value: formatTooltip(r[sizeKey], colFormats[sizeKey] || {})
});
}
}
@ -207,8 +213,8 @@ export function aggregateAndCreateDatasets<
ticksForScatter,
...(sizeArr && { sizeData: sizeArr, sizeDataKey: sizeKey ?? undefined })
});
});
});
}
}
return {
datasets,
@ -219,9 +225,9 @@ export function aggregateAndCreateDatasets<
// NON-SCATTER
if (catKeys.length) {
// With categories
seriesMeta.forEach(({ key: metric, axisType }) => {
for (const { key: metric, axisType } of seriesMeta) {
const fmt = colFormats[metric];
catGroups.forEach(({ rec: catRec, rows: catRows }) => {
for (const { rec: catRec, rows: catRows } of catGroups) {
const xSub = groupRows(xKeys, catRows);
const xMap = new Map(xSub.map((g) => [g.id, g.rows]));
@ -229,11 +235,11 @@ export function aggregateAndCreateDatasets<
const grp = xMap.get(g.id) || [];
let sum = 0;
let sawNull = false;
grp.forEach((r) => {
for (const r of grp) {
const v = parseNumeric(r[metric], fmt);
if (v === null) sawNull = true;
else if (!Number.isNaN(v)) sum += v;
});
}
return sawNull ? null : sum;
});
@ -298,28 +304,28 @@ export function aggregateAndCreateDatasets<
axisType,
tooltipData: tooltipArr
});
});
});
}
}
} else {
// Without categories
seriesMeta.forEach(({ key: metric, axisType }) => {
for (const { key: metric, axisType } of seriesMeta) {
const fmt = colFormats[metric];
// Create a single dataset with all x-group data
const dataArr: (number | null)[] = [];
// Collect all values for the dataset
xGroups.forEach(({ rec, rows: grpRows }) => {
for (const { rec, rows: grpRows } of xGroups) {
let sum = 0;
let sawNull = false;
grpRows.forEach((r) => {
for (const r of grpRows) {
const v = parseNumeric(r[metric], fmt);
if (v === null) sawNull = true;
else if (!Number.isNaN(v)) sum += v;
});
}
const value = sawNull ? null : sum;
dataArr.push(value);
});
}
// Generate labels according to the rules
const labelArr = generateLabels(metric);
@ -362,7 +368,7 @@ export function aggregateAndCreateDatasets<
axisType,
tooltipData: tooltipArr
});
});
}
}
// Create ticks from the x-axis values

View File

@ -26,5 +26,6 @@ export const doesChartHaveValidAxis = ({
isTable: boolean;
}) => {
if (isTable) return true;
return AxisMethodCheckRecord[selectedChartType](selectedAxis!);
if (!selectedAxis) return false;
return AxisMethodCheckRecord[selectedChartType](selectedAxis);
};

View File

@ -315,6 +315,7 @@ const DropdownItemSelector = React.memo(
}: {
item: DropdownItems<T>[number];
index: number;
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
onSelect?: (value: any) => void; // Using any here to resolve the type mismatch
onSelectItem: (index: number) => void;
closeOnSelect: boolean;

View File

@ -56,6 +56,7 @@ export const SearchDropdown = React.memo(
{items.map((item, index) => (
<DropdownMenuItem
disabled={item.disabled}
// biome-ignore lint/suspicious/noArrayIndexKey: <explanation>
key={index}
className="group min-h-10"
onClick={() => onSelect(item)}>

View File

@ -34,6 +34,7 @@ export const ErrorClosableContainer: React.FC<{
<span>{error}</span>
</div>
<button
type="button"
onClick={() => {
onClose?.();
setClosed(true);

View File

@ -25,6 +25,7 @@ export const BusterDragColumnMarkers: React.FC<{
}}>
{Array.from({ length: NUMBER_OF_COLUMNS + 1 }).map((_, index) => (
<div
// biome-ignore lint/suspicious/noArrayIndexKey: <explanation>
key={index}
className={cn(
'bg-border',
@ -48,7 +49,7 @@ export const BusterDragColumnMarkers: React.FC<{
const geHideSnappedDot = (
isDraggingIndex: number | null,
index: number,
disabled,
disabled: boolean | undefined,
itemsLength: number
) => {
if (disabled || index < 3 || index > 9) return true;
@ -112,7 +113,10 @@ const hackForTesting = (
DEFAULT: 0
}
};
const offsetRecord = dragIndexRecord[isDraggingIndex as 1]!;
const offsetRecord = dragIndexRecord[isDraggingIndex as 1];
if (!offsetRecord) {
return {};
}
return {
left: `calc(${((dotIndex + 0) / NUMBER_OF_COLUMNS) * 100}% - ${offsetRecord?.[dotIndex as 4] || offsetRecord?.DEFAULT}px)`

View File

@ -5,6 +5,7 @@ import React, { createContext, useMemo } from 'react';
import { BusterSortableItemContent } from './_BusterSortableItemContent';
interface Context {
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
attributes: Record<string, any>;
listeners: DraggableSyntheticListeners;
// ref(node: HTMLElement | null): void;

View File

@ -37,7 +37,11 @@ export const BusterSortableOverlay: React.FC<{
const indexOfItem = r?.items.findIndex((item) => item.id === activeId);
if (r && indexOfItem !== undefined && indexOfItem !== -1) {
const widthOfGrid = document.querySelector('.buster-resizeable-grid')?.clientWidth!;
const widthOfGrid = document.querySelector('.buster-resizeable-grid')?.clientWidth;
if (widthOfGrid === undefined) {
// Handle the case where widthOfGrid is undefined
return { widthOfItem: undefined, useSnapToCenter: false };
}
let columnsOfItem = r.columnSizes?.[indexOfItem] || 4;
const useSnapToCenter = columnsOfItem === 12;
if (useSnapToCenter) {

View File

@ -20,7 +20,11 @@ export interface BusterListColumn {
width?: number;
minWidth?: number;
align?: 'left' | 'center' | 'right'; //TODO
render?: (value: any, record: any) => React.JSX.Element | string | React.ReactNode;
render?: (
value: string | number | boolean | null | undefined,
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
record: any
) => React.JSX.Element | string | React.ReactNode;
headerRender?: (title: string) => React.ReactNode;
ellipsis?: boolean;
}
@ -28,7 +32,7 @@ export interface BusterListColumn {
export type BusterListRow = BusterListRowItem;
export interface BusterListRowItem {
id: string;
data: Record<string, string | React.ReactNode | any> | null;
data: Record<string, string | React.ReactNode | number | boolean | null | undefined> | null;
onClick?: () => void;
link?: string;
onSelect?: () => void;

View File

@ -1,4 +1,4 @@
import { makeHumanReadble } from '@/lib/text';
export const defaultHeaderFormat = (v: any) => makeHumanReadble(v);
export const defaultCellFormat = (v: any) => v;
export const defaultHeaderFormat = (v: unknown) => makeHumanReadble(v);
export const defaultCellFormat = (v: unknown) => v;