mirror of https://github.com/buster-so/buster.git
Merge branch 'staging' into cursor/add-report-menu-options-and-stub-functions-e636
This commit is contained in:
commit
2f06ede92c
|
@ -11,23 +11,30 @@ export async function getReportHandler(
|
|||
): Promise<GetReportIndividualResponse> {
|
||||
const report = await getReport({ reportId, userId: user.id });
|
||||
|
||||
const platejsResult = await markdownToPlatejs(report.content);
|
||||
try {
|
||||
const platejsResult = await markdownToPlatejs(report.content);
|
||||
|
||||
if (platejsResult.error) {
|
||||
console.error('Error converting markdown to PlateJS:', platejsResult.error);
|
||||
if (platejsResult.error) {
|
||||
console.error('Error converting markdown to PlateJS:', platejsResult.error);
|
||||
throw new HTTPException(500, {
|
||||
message: 'Error converting markdown to PlateJS',
|
||||
});
|
||||
}
|
||||
|
||||
const content = platejsResult.elements ?? [];
|
||||
|
||||
const response: GetReportIndividualResponse = {
|
||||
...report,
|
||||
content,
|
||||
};
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error('Error converting markdown to PlateJS:', error);
|
||||
throw new HTTPException(500, {
|
||||
message: 'Error converting markdown to PlateJS',
|
||||
message: 'Error converting markdown',
|
||||
});
|
||||
}
|
||||
|
||||
const content = platejsResult.elements ?? [];
|
||||
|
||||
const response: GetReportIndividualResponse = {
|
||||
...report,
|
||||
content,
|
||||
};
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
const app = new Hono()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type { DraggableAttributes, DraggableSyntheticListeners } from '@dnd-kit/core';
|
||||
import React, { createContext } from 'react';
|
||||
import { createContext } from 'react';
|
||||
|
||||
interface Context {
|
||||
attributes: DraggableAttributes;
|
||||
|
|
|
@ -9,105 +9,101 @@ import type { DropdownItems } from '../dropdown';
|
|||
import Link from 'next/link';
|
||||
import { PreparingYourRequestLoader } from '../charts/LoadingComponents';
|
||||
|
||||
export const MetricCard = React.memo(
|
||||
React.forwardRef<
|
||||
HTMLDivElement,
|
||||
export const MetricCard = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
{
|
||||
className?: string;
|
||||
metricId: string;
|
||||
metricLink: string;
|
||||
isDragOverlay: boolean;
|
||||
readOnly: boolean;
|
||||
metricData: BusterMetricData | undefined;
|
||||
metric: Pick<BusterMetric, 'name' | 'time_frame' | 'chart_config' | 'description'> | undefined;
|
||||
renderChart: boolean;
|
||||
loading: boolean;
|
||||
error: string | undefined;
|
||||
animate: boolean;
|
||||
onInitialAnimationEnd?: () => void;
|
||||
attributes?: DraggableAttributes;
|
||||
listeners?: DraggableSyntheticListeners;
|
||||
threeDotMenuItems: DropdownItems;
|
||||
}
|
||||
>(
|
||||
(
|
||||
{
|
||||
className?: string;
|
||||
metricId: string;
|
||||
metricLink: string;
|
||||
isDragOverlay: boolean;
|
||||
readOnly: boolean;
|
||||
metricData: BusterMetricData | undefined;
|
||||
metric:
|
||||
| Pick<BusterMetric, 'name' | 'time_frame' | 'chart_config' | 'description'>
|
||||
| undefined;
|
||||
renderChart: boolean;
|
||||
loading: boolean;
|
||||
error: string | undefined;
|
||||
animate: boolean;
|
||||
onInitialAnimationEnd?: () => void;
|
||||
attributes?: DraggableAttributes;
|
||||
listeners?: DraggableSyntheticListeners;
|
||||
threeDotMenuItems: DropdownItems;
|
||||
}
|
||||
>(
|
||||
(
|
||||
{
|
||||
className = '',
|
||||
metricId,
|
||||
metricLink,
|
||||
readOnly,
|
||||
metricData,
|
||||
metric,
|
||||
isDragOverlay,
|
||||
renderChart = true,
|
||||
loading = false,
|
||||
animate = true,
|
||||
error,
|
||||
attributes,
|
||||
listeners,
|
||||
threeDotMenuItems,
|
||||
onInitialAnimationEnd
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const isTable = metric?.chart_config?.selectedChartType === 'table';
|
||||
const chartOptions = metric?.chart_config;
|
||||
const data = metricData?.data || null;
|
||||
const hideChart = isDragOverlay && data && data.length > 50;
|
||||
className = '',
|
||||
metricId,
|
||||
metricLink,
|
||||
readOnly,
|
||||
metricData,
|
||||
metric,
|
||||
isDragOverlay,
|
||||
renderChart = true,
|
||||
loading = false,
|
||||
animate = true,
|
||||
error,
|
||||
attributes,
|
||||
listeners,
|
||||
threeDotMenuItems,
|
||||
onInitialAnimationEnd
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const isTable = metric?.chart_config?.selectedChartType === 'table';
|
||||
const chartOptions = metric?.chart_config;
|
||||
const data = metricData?.data || null;
|
||||
const hideChart = isDragOverlay && data && data.length > 50;
|
||||
|
||||
const content = () => {
|
||||
if (renderChart && chartOptions && !hideChart) {
|
||||
return (
|
||||
<BusterChartDynamic
|
||||
data={data}
|
||||
loading={loading}
|
||||
error={error}
|
||||
onInitialAnimationEnd={onInitialAnimationEnd}
|
||||
animate={!isDragOverlay && animate}
|
||||
animateLegend={false}
|
||||
columnMetadata={metricData?.data_metadata?.column_metadata}
|
||||
readOnly={true}
|
||||
{...chartOptions}
|
||||
const content = () => {
|
||||
if (renderChart && chartOptions && !hideChart) {
|
||||
return (
|
||||
<BusterChartDynamic
|
||||
data={data}
|
||||
loading={loading}
|
||||
error={error}
|
||||
onInitialAnimationEnd={onInitialAnimationEnd}
|
||||
animate={!isDragOverlay && animate}
|
||||
animateLegend={false}
|
||||
columnMetadata={metricData?.data_metadata?.column_metadata}
|
||||
readOnly={true}
|
||||
{...chartOptions}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <PreparingYourRequestLoader />;
|
||||
};
|
||||
|
||||
return (
|
||||
<Card
|
||||
ref={ref}
|
||||
className={cn('metric-item flex h-full w-full flex-col overflow-auto', className)}>
|
||||
<Link className="swag flex" href={metricLink} prefetch {...attributes} {...listeners}>
|
||||
<CardHeader
|
||||
size="small"
|
||||
data-testid={`metric-item-${metricId}`}
|
||||
className="hover:bg-item-hover group relative min-h-13! w-full justify-center overflow-hidden border-b px-4 py-2">
|
||||
<MetricTitle
|
||||
name={metric?.name || ''}
|
||||
timeFrame={metric?.time_frame}
|
||||
metricLink={metricLink}
|
||||
isDragOverlay={false}
|
||||
readOnly={readOnly}
|
||||
description={metric?.description}
|
||||
threeDotMenuItems={threeDotMenuItems}
|
||||
/>
|
||||
);
|
||||
}
|
||||
</CardHeader>
|
||||
</Link>
|
||||
|
||||
return <PreparingYourRequestLoader />;
|
||||
};
|
||||
|
||||
return (
|
||||
<Card
|
||||
ref={ref}
|
||||
className={cn('metric-item flex h-full w-full flex-col overflow-auto', className)}>
|
||||
<Link className="swag flex" href={metricLink} prefetch {...attributes} {...listeners}>
|
||||
<CardHeader
|
||||
size="small"
|
||||
data-testid={`metric-item-${metricId}`}
|
||||
className="hover:bg-item-hover group relative min-h-13! w-full justify-center overflow-hidden border-b px-4 py-2">
|
||||
<MetricTitle
|
||||
name={metric?.name || ''}
|
||||
timeFrame={metric?.time_frame}
|
||||
metricLink={metricLink}
|
||||
isDragOverlay={false}
|
||||
readOnly={readOnly}
|
||||
description={metric?.description}
|
||||
threeDotMenuItems={threeDotMenuItems}
|
||||
/>
|
||||
</CardHeader>
|
||||
</Link>
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
'h-full w-full overflow-hidden bg-transparent',
|
||||
isTable ? '' : 'p-3',
|
||||
isDragOverlay ? 'pointer-events-none' : 'pointer-events-auto'
|
||||
)}>
|
||||
{content()}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
)
|
||||
<div
|
||||
className={cn(
|
||||
'h-full w-full overflow-hidden bg-transparent',
|
||||
isTable ? '' : 'p-3',
|
||||
isDragOverlay ? 'pointer-events-none' : 'pointer-events-auto'
|
||||
)}>
|
||||
{content()}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -2,39 +2,35 @@ import { cn } from '@/lib/classMerge';
|
|||
import React, { useState } from 'react';
|
||||
import { Dropdown, type DropdownItems } from '../dropdown';
|
||||
|
||||
export const MetricCardThreeMenuContainer = React.memo(
|
||||
({
|
||||
children,
|
||||
dropdownItems,
|
||||
className
|
||||
}: {
|
||||
className?: string;
|
||||
children: React.ReactNode;
|
||||
dropdownItems: DropdownItems;
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
export const MetricCardThreeMenuContainer = ({
|
||||
children,
|
||||
dropdownItems,
|
||||
className
|
||||
}: {
|
||||
className?: string;
|
||||
children: React.ReactNode;
|
||||
dropdownItems: DropdownItems;
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}}
|
||||
className={cn(
|
||||
// Use opacity and pointer-events instead of display:none to maintain positioning context
|
||||
'w-8.5 rounded transition-opacity duration-75',
|
||||
'pointer-events-none opacity-0 group-hover:pointer-events-auto group-hover:opacity-100',
|
||||
className,
|
||||
isOpen && 'pointer-events-auto opacity-100'
|
||||
)}>
|
||||
<div className="absolute top-3 right-1.5">
|
||||
<Dropdown items={dropdownItems} side="top" align="end" onOpenChange={setIsOpen}>
|
||||
{children}
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
return (
|
||||
<div
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}}
|
||||
className={cn(
|
||||
// Use opacity and pointer-events instead of display:none to maintain positioning context
|
||||
'-mr-2 hidden group-hover:block',
|
||||
'group-hover:pointer-events-auto',
|
||||
isOpen && 'pointer-events-auto block',
|
||||
className
|
||||
)}>
|
||||
<Dropdown items={dropdownItems} side="top" align="end" onOpenChange={setIsOpen}>
|
||||
{children}
|
||||
</Dropdown>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
MetricCardThreeMenuContainer.displayName = 'MetricCardThreeMenuContainer';
|
||||
|
|
|
@ -20,57 +20,55 @@ export const MetricTitle: React.FC<{
|
|||
isDragOverlay: boolean;
|
||||
readOnly?: boolean;
|
||||
threeDotMenuItems: DropdownItems;
|
||||
}> = React.memo(
|
||||
({
|
||||
readOnly = true,
|
||||
name,
|
||||
description,
|
||||
isDragOverlay,
|
||||
metricLink,
|
||||
timeFrame,
|
||||
threeDotMenuItems
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
}> = ({
|
||||
readOnly = true,
|
||||
name,
|
||||
description,
|
||||
isDragOverlay,
|
||||
metricLink,
|
||||
timeFrame,
|
||||
threeDotMenuItems
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
|
||||
useMount(() => {
|
||||
if (metricLink) router.prefetch(metricLink);
|
||||
});
|
||||
useMount(() => {
|
||||
if (metricLink) router.prefetch(metricLink);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={'flex h-full cursor-pointer flex-col space-y-0.5 overflow-hidden'}>
|
||||
<div className="flex w-full justify-between space-x-0.5 overflow-hidden">
|
||||
<Title
|
||||
as="h4"
|
||||
truncate
|
||||
className="text-md! whitespace-nowrap"
|
||||
style={{ fontSize: '14px' }}>
|
||||
{`${name}`}
|
||||
</Title>
|
||||
</div>
|
||||
|
||||
<div className="flex w-full items-center overflow-hidden">
|
||||
<Text
|
||||
className={`w-full ${timeFrame || description ? 'visible' : 'hidden'}`}
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
truncate={true}>
|
||||
{timeFrame || '_'}
|
||||
|
||||
{description && timeFrame && ' • '}
|
||||
|
||||
{description}
|
||||
</Text>
|
||||
</div>
|
||||
return (
|
||||
<div className="group flex h-full min-h-0 w-full flex-1 flex-nowrap space-x-0.5">
|
||||
<div className={'flex h-full flex-1 cursor-pointer flex-col space-y-0.5 overflow-hidden'}>
|
||||
<div className="flex w-full justify-between space-x-0.5 overflow-hidden">
|
||||
<Title
|
||||
as="h4"
|
||||
truncate
|
||||
className="text-md! whitespace-nowrap"
|
||||
style={{ fontSize: '14px' }}>
|
||||
{`${name}`}
|
||||
</Title>
|
||||
</div>
|
||||
|
||||
{isDragOverlay || readOnly ? null : (
|
||||
<MetricCardThreeMenuContainer dropdownItems={threeDotMenuItems}>
|
||||
<Button variant="ghost" className="bg-item-hover!" prefix={<DotsVertical />} />
|
||||
</MetricCardThreeMenuContainer>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
<div className="flex w-full items-center overflow-hidden">
|
||||
<Text
|
||||
className={`w-full ${timeFrame || description ? 'visible' : 'hidden'}`}
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
truncate={true}>
|
||||
{timeFrame || '_'}
|
||||
|
||||
{description && timeFrame && ' • '}
|
||||
|
||||
{description}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isDragOverlay || readOnly ? null : (
|
||||
<MetricCardThreeMenuContainer dropdownItems={threeDotMenuItems}>
|
||||
<Button variant="ghost" className="hover:bg-item-active" prefix={<DotsVertical />} />
|
||||
</MetricCardThreeMenuContainer>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
MetricTitle.displayName = 'MetricTitle';
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
'use client';
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
import React, { useMemo, useContext } from 'react';
|
||||
import { useMemoizedFn } from '@/hooks';
|
||||
import { useDashboardMetric } from './useDashboardMetric';
|
||||
import { assetParamsToRoute } from '@/lib/assets';
|
||||
import { MetricCard } from '@/components/ui/metric';
|
||||
import { useContext } from 'use-context-selector';
|
||||
import { SortableItemContext } from '@/components/ui/grid/SortableItemContext';
|
||||
import { useMetricCardThreeDotMenuItems } from './metricCardThreeDotMenuItems';
|
||||
|
||||
|
@ -48,10 +47,8 @@ const DashboardMetricItemBase: React.FC<{
|
|||
const animate =
|
||||
!initialAnimationEnded && !isDragOverlay && dataLength < 125 && numberOfMetrics <= 30;
|
||||
|
||||
const error: string | undefined = useMemo(
|
||||
() => metric?.error || metricDataError?.message || metricError?.message || undefined,
|
||||
[metric?.error, metricDataError, metricError]
|
||||
);
|
||||
const error: string | undefined =
|
||||
metric?.error || metricDataError?.message || metricError?.message || undefined;
|
||||
|
||||
const metricLink = useMemo(() => {
|
||||
return assetParamsToRoute({
|
||||
|
@ -93,4 +90,4 @@ const DashboardMetricItemBase: React.FC<{
|
|||
);
|
||||
};
|
||||
|
||||
export const DashboardMetricItem = React.memo(DashboardMetricItemBase);
|
||||
export const DashboardMetricItem = DashboardMetricItemBase;
|
||||
|
|
|
@ -16,6 +16,10 @@ const DEFAULT_OPTIONS = {
|
|||
anthropic: {
|
||||
disableParallelToolCalls: true,
|
||||
},
|
||||
openai: {
|
||||
disableParallelToolCalls: true,
|
||||
reasoningEffort: 'minimal',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue