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}; +}