diff --git a/apps/web/src/api/createAxiosInstance.ts b/apps/web/src/api/createAxiosInstance.ts
index c88547f0a..d1fb67456 100644
--- a/apps/web/src/api/createAxiosInstance.ts
+++ b/apps/web/src/api/createAxiosInstance.ts
@@ -77,11 +77,11 @@ export const defaultAxiosRequestHandler = async (
token = await getSupabaseTokenFromCookies();
} else {
// Always check token validity before making requests
- const tokenResult = await options?.checkTokenValidity();
+ const tokenResult = await options?.checkTokenValidity?.();
token = tokenResult?.access_token || '';
}
- if (!token) {
+ if (!token && options?.checkTokenValidity) {
throw new Error('User authentication error - no token found');
}
diff --git a/apps/web/src/app/test/report-playground/ReportPlayground.tsx b/apps/web/src/app/test/report-playground/ReportPlayground.tsx
index aa93cbf79..1a263f751 100644
--- a/apps/web/src/app/test/report-playground/ReportPlayground.tsx
+++ b/apps/web/src/app/test/report-playground/ReportPlayground.tsx
@@ -4,23 +4,125 @@ import React, { useState } from 'react';
import dynamic from 'next/dynamic';
import { InputTextArea } from '@/components/ui/inputs/InputTextArea';
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';
const ReportEditor = dynamic(
() => import('@/components/ui/report/ReportEditor').then((mod) => mod.ReportEditor),
{ ssr: false, loading: () =>
Loading...
}
);
+// Status indicator component with dynamic backgrounds
+interface ValidationStatusProps {
+ isLoading: boolean;
+ error: unknown;
+ isFetched: boolean;
+ data: unknown;
+}
+
+const ValidationStatus: React.FC = ({
+ isLoading,
+ error,
+ isFetched,
+ data
+}) => {
+ // Determine status and styling
+ const getStatusConfig = () => {
+ if (isLoading) {
+ return {
+ bgClass: 'bg-blue-50 border-blue-200',
+ textClass: 'text-blue-700',
+ iconBg: 'bg-blue-600',
+ icon: (
+
+ ),
+ message: 'Validating markdown...'
+ };
+ }
+
+ if (error && !isLoading) {
+ return {
+ bgClass: 'bg-red-50 border-red-200',
+ textClass: 'text-red-700',
+ iconBg: 'bg-red-600',
+ icon: (
+
+ ✕
+
+ ),
+ message: `Validation failed: ${error instanceof Error ? error.message : error || 'Unknown error'}`
+ };
+ }
+
+ if (isFetched && !error && !isLoading && data) {
+ return {
+ bgClass: 'bg-green-50 border-green-400',
+ textClass: 'text-green-700',
+ iconBg: 'bg-green-600',
+ icon: (
+
+ ✓
+
+ ),
+ message: 'Markdown validated successfully'
+ };
+ }
+
+ // Default/ready state
+ return {
+ bgClass: 'bg-gray-50 border-gray-200',
+ textClass: 'text-gray-600',
+ iconBg: 'bg-gray-400',
+ icon: ,
+ message: 'Ready to validate'
+ };
+ };
+
+ const config = getStatusConfig();
+
+ return (
+
+
+ {config.icon}
+ {config.message}
+
+
+ );
+};
+
export const ReportPlayground: React.FC = () => {
const [markdown, setMarkdown] = useState('');
+ const { data, refetch, isLoading, isFetched, error } = useQuery({
+ queryKey: ['report-playground', markdown],
+ queryFn: () => {
+ return mainApiV2.post('/temp/validate-markdown', { markdown }).then((res) => res.data);
+ },
+ enabled: false
+ });
+
+ useDebounceEffect(
+ () => {
+ refetch();
+ },
+ [markdown, refetch],
+ { wait: 250 }
+ );
+
return (
-
-
setMarkdown(e.target.value)}
- />
-
+
+
+ setMarkdown(e.target.value)}
+ />
+
+
+
diff --git a/apps/web/src/app/test/report-playground/layout.tsx b/apps/web/src/app/test/report-playground/layout.tsx
new file mode 100644
index 000000000..f2ce9737c
--- /dev/null
+++ b/apps/web/src/app/test/report-playground/layout.tsx
@@ -0,0 +1,6 @@
+import { BusterReactQueryProvider } from '@/context/BusterReactQuery/BusterReactQueryAndApi';
+import { QueryClient } from '@tanstack/react-query';
+
+export default function Layout({ children }: { children: React.ReactNode }) {
+ return
{children};
+}