mirror of https://github.com/buster-so/buster.git
create additional refs and playground
This commit is contained in:
parent
14e1680f43
commit
8a5ee5b417
|
@ -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 = [
|
||||
{
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 };
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
@import './tailwindAnimations.css';
|
||||
@plugin 'tailwind-scrollbar';
|
||||
@plugin "tailwindcss-animate";
|
||||
@plugin 'tailwind-scrollbar-hide';
|
||||
|
||||
@custom-variant dark (&:where(.dark, .dark *));
|
||||
|
||||
|
|
Loading…
Reference in New Issue