create additional refs and playground

This commit is contained in:
Nate Kelley 2025-07-29 17:34:49 -06:00
parent 14e1680f43
commit 8a5ee5b417
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
5 changed files with 93 additions and 63 deletions

View File

@ -7,6 +7,8 @@ import type { ReportElements } from '@buster/server-shared/reports';
import { useQuery } from '@tanstack/react-query';
import { mainApiV2 } from '@/api/buster_rest/instances';
import { useDebounceEffect } from '@/hooks';
import { useThemesConfig } from '@/components/ui/report/ThemeWrapper/useThemesConfig';
import { cn } from '@/lib/utils';
const ReportEditor = dynamic(
() => import('@/components/ui/report/ReportEditor').then((mod) => mod.ReportEditor),
@ -14,6 +16,66 @@ const ReportEditor = dynamic(
);
// Status indicator component with dynamic backgrounds
export const ReportPlayground: React.FC = () => {
const [markdown, setMarkdown] = useState<string>('');
const [hasBeenSuccessFullAtLeastOnce, setHasBeenSuccessFullAtLeastOnce] = useState(false);
const { data, refetch, isLoading, isFetched, error } = useQuery({
queryKey: ['report-playground', markdown],
queryFn: () => {
return mainApiV2
.post<{ elements: ReportElements }>('/temp/validate-markdown', { markdown })
.then((res) => {
setHasBeenSuccessFullAtLeastOnce(true);
return res.data;
});
},
enabled: false
});
useDebounceEffect(
() => {
if (markdown.length > 0) {
refetch();
}
},
[markdown, refetch],
{ wait: 150 }
);
const usedValue: ReportElements = hasBeenSuccessFullAtLeastOnce ? data?.elements || [] : value;
return (
<div className="grid max-h-screen min-h-screen grid-cols-[270px_1fr] gap-5 rounded border p-7">
<div className="flex h-full flex-col space-y-5">
<InputTextArea
className="flex-1 resize-none"
placeholder="Put markdown here"
value={markdown}
onChange={(e) => setMarkdown(e.target.value)}
/>
<ValidationStatus isLoading={isLoading} error={error} isFetched={isFetched} data={data} />
{data && (
<div className="max-h-[28vh] min-h-0 flex-1">
<div className="flex h-full flex-col">
<h3 className="mb-2 text-sm font-medium text-gray-700">Successful Response:</h3>
<pre className="flex-1 overflow-auto rounded border bg-gray-50 p-3 font-mono text-xs">
{JSON.stringify(data.elements, null, 2)}
</pre>
</div>
</div>
)}
<ThemePicker />
</div>
<div className="bg-background h-full max-h-[calc(100vh-56px)] overflow-y-auto rounded border shadow">
<ReportEditor value={usedValue} readOnly={false} />
</div>
</div>
);
};
interface ValidationStatusProps {
isLoading: boolean;
error: unknown;
@ -92,62 +154,37 @@ const ValidationStatus: React.FC<ValidationStatusProps> = ({
);
};
export const ReportPlayground: React.FC = () => {
const [markdown, setMarkdown] = useState<string>('');
const [hasBeenSuccessFullAtLeastOnce, setHasBeenSuccessFullAtLeastOnce] = useState(false);
const ThemePicker = React.memo(() => {
const { activeTheme, setActiveTheme, allThemes } = useThemesConfig();
const { data, refetch, isLoading, isFetched, error } = useQuery({
queryKey: ['report-playground', markdown],
queryFn: () => {
return mainApiV2
.post<{ elements: ReportElements }>('/temp/validate-markdown', { markdown })
.then((res) => {
setHasBeenSuccessFullAtLeastOnce(true);
return res.data;
});
},
enabled: false
});
useDebounceEffect(
() => {
if (markdown.length > 0) {
refetch();
}
},
[markdown, refetch],
{ wait: 150 }
);
const usedValue: ReportElements = hasBeenSuccessFullAtLeastOnce ? data?.elements || [] : value;
const themesList = Object.values(allThemes);
return (
<div className="grid max-h-screen min-h-screen grid-cols-[400px_1fr] gap-5 rounded border p-7">
<div className="flex h-full flex-col space-y-5">
<InputTextArea
className="flex-1 resize-none"
placeholder="Put markdown here"
value={markdown}
onChange={(e) => setMarkdown(e.target.value)}
/>
<ValidationStatus isLoading={isLoading} error={error} isFetched={isFetched} data={data} />
{data && (
<div className="max-h-[28vh] min-h-0 flex-1">
<div className="flex h-full flex-col">
<h3 className="mb-2 text-sm font-medium text-gray-700">Successful Response:</h3>
<pre className="flex-1 overflow-auto rounded border bg-gray-50 p-3 font-mono text-xs">
{JSON.stringify(data.elements, null, 2)}
</pre>
</div>
</div>
)}
</div>
<div className="bg-background h-full max-h-[calc(100vh-56px)] overflow-y-auto rounded border shadow">
<ReportEditor value={usedValue} readOnly={false} />
</div>
<div className="bg-background flex gap-x-2 overflow-x-auto rounded border p-2">
{themesList.map((theme) => {
const firstThreeColors = Object.values(theme.light).slice(0, 3);
const isActive = activeTheme.id === theme.id;
return (
<div
key={theme.id}
className={cn(
'min-h-7 min-w-7 cursor-pointer rounded-full border transition-all duration-200 hover:scale-110',
isActive && 'border-primary shadow-2xl'
)}
style={{
background: `linear-gradient(0deg, hsl(${firstThreeColors[0]}) 0%, hsl(${firstThreeColors[0]}) 33%, hsl(${firstThreeColors[1]}) 33%, hsl(${firstThreeColors[1]}) 66%, hsl(${firstThreeColors[2]}) 66%, hsl(${firstThreeColors[2]}) 100%)`
}}
onClick={() => setActiveTheme(theme)}
title={theme.id}
/>
);
})}
</div>
);
};
});
ThemePicker.displayName = 'ThemePicker';
const value: ReportElements = [
{

View File

@ -59,7 +59,7 @@ export const ReportEditor = React.memo(
variant={variant}
readonly={readOnly}
disabled={disabled}
className={cn('pb-[20vh]', className)}>
className={cn('pb-[15vh]', className)}>
<Editor style={style} placeholder={placeholder} disabled={disabled} />
</EditorContainer>
</Plate>

View File

@ -13,5 +13,5 @@ export function useThemesConfig() {
const activeTheme = useThemesConfigStore((state) => state.activeTheme);
const setActiveTheme = useThemesConfigStore((state) => state.setActiveTheme);
return { activeTheme, setActiveTheme };
return { activeTheme, setActiveTheme, allThemes: THEMES };
}

View File

@ -275,7 +275,7 @@ function withTooltip<T extends React.ElementType>(Component: T) {
tooltip,
tooltipContentProps,
tooltipProps,
tooltipTriggerProps,
ref,
...props
}: TooltipProps<T>) {
const [mounted, setMounted] = React.useState(false);
@ -287,15 +287,7 @@ function withTooltip<T extends React.ElementType>(Component: T) {
const component = <Component {...(props as React.ComponentProps<T>)} />;
if (tooltip && mounted) {
return (
<Tooltip {...tooltipProps}>
<TooltipTrigger asChild {...tooltipTriggerProps}>
{component}
</TooltipTrigger>
<TooltipContent {...tooltipContentProps}>{tooltip}</TooltipContent>
</Tooltip>
);
return <Tooltip title={tooltip}>{component}</Tooltip>;
}
return component;

View File

@ -2,6 +2,7 @@
@import './tailwindAnimations.css';
@plugin 'tailwind-scrollbar';
@plugin "tailwindcss-animate";
@plugin 'tailwind-scrollbar-hide';
@custom-variant dark (&:where(.dark, .dark *));