update fetching sql

This commit is contained in:
Nate Kelley 2025-04-04 12:25:06 -06:00
parent 16c6ea1d26
commit 49ec28142f
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
8 changed files with 492 additions and 428 deletions

708
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -38,11 +38,11 @@
"@radix-ui/react-tooltip": "^1.1.8", "@radix-ui/react-tooltip": "^1.1.8",
"@supabase/ssr": "^0.6.1", "@supabase/ssr": "^0.6.1",
"@supabase/supabase-js": "^2.49.4", "@supabase/supabase-js": "^2.49.4",
"@tanstack/query-sync-storage-persister": "^5.71.5", "@tanstack/query-sync-storage-persister": "^5.71.10",
"@tanstack/react-form": "^1.1.4", "@tanstack/react-form": "^1.1.4",
"@tanstack/react-query": "^5.71.5", "@tanstack/react-query": "^5.71.10",
"@tanstack/react-query-devtools": "^5.71.5", "@tanstack/react-query-devtools": "^5.71.10",
"@tanstack/react-query-persist-client": "^5.71.5", "@tanstack/react-query-persist-client": "^5.71.10",
"@tanstack/react-table": "^8.21.2", "@tanstack/react-table": "^8.21.2",
"@types/jest": "^29.5.14", "@types/jest": "^29.5.14",
"@types/prettier": "^2.7.3", "@types/prettier": "^2.7.3",
@ -74,7 +74,7 @@
"next-themes": "^0.4.6", "next-themes": "^0.4.6",
"papaparse": "^5.5.2", "papaparse": "^5.5.2",
"pluralize": "^8.0.0", "pluralize": "^8.0.0",
"posthog-js": "^1.234.1", "posthog-js": "^1.234.8",
"prettier": "^3.5.3", "prettier": "^3.5.3",
"prettier-plugin-tailwindcss": "^0.6.11", "prettier-plugin-tailwindcss": "^0.6.11",
"react": "^18", "react": "^18",
@ -88,7 +88,7 @@
"rehype-raw": "^7.0.0", "rehype-raw": "^7.0.0",
"remark-gfm": "^4.0.1", "remark-gfm": "^4.0.1",
"sonner": "^2.0.3", "sonner": "^2.0.3",
"tailwind-merge": "^3.0.2", "tailwind-merge": "^3.1.0",
"ts-jest": "^29.3.1", "ts-jest": "^29.3.1",
"use-context-selector": "^2.0.0", "use-context-selector": "^2.0.0",
"utility-types": "^3.11.0", "utility-types": "^3.11.0",
@ -99,15 +99,15 @@
"devDependencies": { "devDependencies": {
"@chromatic-com/storybook": "^3.2.6", "@chromatic-com/storybook": "^3.2.6",
"@eslint/eslintrc": "^3", "@eslint/eslintrc": "^3",
"@storybook/addon-controls": "^8.6.10", "@storybook/addon-controls": "^8.6.12",
"@storybook/addon-essentials": "^8.6.10", "@storybook/addon-essentials": "^8.6.12",
"@storybook/addon-interactions": "^8.6.10", "@storybook/addon-interactions": "^8.6.12",
"@storybook/blocks": "^8.6.10", "@storybook/blocks": "^8.6.12",
"@storybook/nextjs": "^8.6.10", "@storybook/nextjs": "^8.6.12",
"@storybook/react": "^8.6.10", "@storybook/react": "^8.6.12",
"@storybook/test": "^8.6.10", "@storybook/test": "^8.6.12",
"@tailwindcss/postcss": "4.0.17", "@tailwindcss/postcss": "4.1.2",
"@testing-library/react": "^16.2.0", "@testing-library/react": "^16.3.0",
"@types/canvas-confetti": "^1.9.0", "@types/canvas-confetti": "^1.9.0",
"@types/js-cookie": "^3.0.6", "@types/js-cookie": "^3.0.6",
"@types/js-yaml": "^4.0.9", "@types/js-yaml": "^4.0.9",
@ -123,9 +123,9 @@
"eslint-config-prettier": "^10.1.1", "eslint-config-prettier": "^10.1.1",
"eslint-plugin-storybook": "^0.12.0", "eslint-plugin-storybook": "^0.12.0",
"msw-storybook-addon": "^2.0.4", "msw-storybook-addon": "^2.0.4",
"sass": "^1.86.0", "sass": "^1.86.3",
"tailwind-scrollbar": "^4.0.1", "tailwind-scrollbar": "^4.0.2",
"tailwindcss": "4.0.17", "tailwindcss": "4.1.2",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"typescript": "^5" "typescript": "^5"
}, },

View File

@ -15,8 +15,7 @@ export const DataContainer: React.FC<{
return ( return (
<div <div
className={cn( className={cn(
'bg-background rounded border shadow', 'bg-background relative h-full w-full overflow-hidden rounded border shadow',
'relative h-full w-full overflow-hidden',
className className
)}> )}>
<IndeterminateLinearLoader <IndeterminateLinearLoader

View File

@ -5,10 +5,11 @@ import { AppCodeEditor } from '@/components/ui/inputs/AppCodeEditor';
import { useBusterNotifications } from '@/context/BusterNotifications'; import { useBusterNotifications } from '@/context/BusterNotifications';
import { useMemoizedFn } from '@/hooks'; import { useMemoizedFn } from '@/hooks';
import { Button } from '@/components/ui/buttons/Button'; import { Button } from '@/components/ui/buttons/Button';
import React, { useState } from 'react'; import React, { useMemo, useState } from 'react';
import type { AppVerticalCodeSplitterProps } from './AppVerticalCodeSplitter'; import type { AppVerticalCodeSplitterProps } from './AppVerticalCodeSplitter';
import { cn } from '@/lib/classMerge'; import { cn } from '@/lib/classMerge';
import { ErrorClosableContainer } from '@/components/ui/error/ErrorClosableContainer'; import { ErrorClosableContainer } from '@/components/ui/error/ErrorClosableContainer';
import { FileCard } from '@/components/ui/card/FileCard';
export const SQLContainer: React.FC<{ export const SQLContainer: React.FC<{
className?: string; className?: string;
@ -34,22 +35,9 @@ export const SQLContainer: React.FC<{
setIsRunning(false); setIsRunning(false);
}); });
return ( const memoizedFooter = useMemo(() => {
<div return (
className={cn( <>
'flex h-full w-full flex-col overflow-hidden',
'bg-background rounded border',
className
)}>
<AppCodeEditor
className="overflow-hidden border-x-0 border-t-0"
value={sql}
onChange={setDatasetSQL}
onMetaEnter={onRunQueryPreflight}
variant={null}
/>
<div className="bg-border-color my-0! h-[0.5px] w-full" />
<div className="relative flex items-center justify-between px-4 py-2.5">
<Button onClick={onCopySQL}>Copy SQL</Button> <Button onClick={onCopySQL}>Copy SQL</Button>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@ -77,10 +65,25 @@ export const SQLContainer: React.FC<{
Run Run
</Button> </Button>
</div> </div>
</>
);
}, [disabledSave, isRunning, onCopySQL, onRunQueryPreflight, onSaveSQL, sql]);
{error && <ErrorClosableContainer error={error} />} return (
</div> <FileCard
</div> className={className}
footerClassName="flex justify-between space-x-4"
footer={memoizedFooter}>
<AppCodeEditor
className="overflow-hidden border-x-0 border-t-0"
value={sql}
onChange={setDatasetSQL}
onMetaEnter={onRunQueryPreflight}
variant={null}
/>
{error && <ErrorClosableContainer error={error} />}
</FileCard>
); );
} }
); );

View File

@ -4,7 +4,7 @@ import { cn } from '@/lib/classMerge';
import { Text } from '../typography'; import { Text } from '../typography';
interface FileCardProps { interface FileCardProps {
fileName: string | React.ReactNode; fileName?: string | React.ReactNode;
headerButtons?: React.ReactNode; headerButtons?: React.ReactNode;
className?: string; className?: string;
children?: React.ReactNode; children?: React.ReactNode;
@ -42,7 +42,9 @@ export const FileCard = React.memo(
</CardContent> </CardContent>
{footer && ( {footer && (
<CardFooter className={cn('bg-background', footerClassName)}>{footer}</CardFooter> <CardFooter className={cn('bg-background px-4 py-2.5', footerClassName)}>
{footer}
</CardFooter>
)} )}
</Card> </Card>
); );

View File

@ -52,16 +52,15 @@ export const MetricViewResults: React.FC<{ metricId: string }> = React.memo(({ m
metricId metricId
}); });
if (res) { if (res && res.data && res.data.length > 0) {
if (data && data.length > 0) { const data = res.data;
const headerHeight = 50; const headerHeight = 50;
const heightOfRow = 36; const heightOfRow = 36;
const heightOfDataContainer = headerHeight + heightOfRow * (data.length || 0); const heightOfDataContainer = headerHeight + heightOfRow * (data.length || 0);
const containerHeight = containerRef.current?.clientHeight || 0; const containerHeight = containerRef.current?.clientHeight || 0;
const maxHeight = Math.floor(containerHeight * 0.6); const maxHeight = Math.floor(containerHeight * 0.6);
const finalHeight = Math.min(heightOfDataContainer, maxHeight); const finalHeight = Math.min(heightOfDataContainer, maxHeight);
appSplitterRef.current?.setSplitSizes(['auto', `${finalHeight}px`]); appSplitterRef.current?.setSplitSizes(['auto', `${finalHeight}px`]);
}
} }
} catch (error) { } catch (error) {
// //

View File

@ -1,4 +1,4 @@
import React, { useRef } from 'react'; import React, { useRef, useEffect } from 'react';
import * as yaml from 'js-yaml'; import * as yaml from 'js-yaml';
import * as monaco from 'monaco-editor'; import * as monaco from 'monaco-editor';
import { type editor } from 'monaco-editor/esm/vs/editor/editor.api'; import { type editor } from 'monaco-editor/esm/vs/editor/editor.api';
@ -37,20 +37,29 @@ export const validateMetricYaml = (
try { try {
parsed = yaml.load(content); parsed = yaml.load(content);
} catch (error: any) { } catch (error: any) {
// For parse errors, try to extract line information from the error message // For parse errors, extract line and column information from the error
let lineNumber = 1; let lineNumber = 1;
const lineMatch = error.message.match(/line (\d+)/i); let columnNumber = 1;
if (lineMatch && lineMatch[1]) {
lineNumber = parseInt(lineMatch[1], 10); // js-yaml errors include mark object with position information
if (error.mark) {
lineNumber = error.mark.line + 1; // Convert to 1-based line numbering
columnNumber = error.mark.column + 1; // Convert to 1-based column numbering
} else {
// Fallback to regex for older versions or different error types
const lineMatch = error.message.match(/line (\d+)/i);
if (lineMatch && lineMatch[1]) {
lineNumber = parseInt(lineMatch[1], 10);
}
} }
markers.push({ markers.push({
severity: monaco.MarkerSeverity.Error, severity: monaco.MarkerSeverity.Error,
message: 'Invalid YAML 🤣: ' + error.message, message: 'Invalid YAML: ' + error.message,
startLineNumber: lineNumber, startLineNumber: lineNumber,
startColumn: 1, startColumn: columnNumber,
endLineNumber: lineNumber, endLineNumber: lineNumber,
endColumn: 1 endColumn: columnNumber + 1 // Highlight at least one character
}); });
return markers; return markers;
} }
@ -184,18 +193,70 @@ export const validateMetricYaml = (
return markers; return markers;
}; };
// Helper to configure Monaco YAML schema
const configureYamlSchema = (
monaco: typeof import('monaco-editor'),
editor: editor.IStandaloneCodeEditor
) => {
// This assumes you have monaco-yaml configured via yamlHelper.ts in the project
// The schema will help with validation and autocomplete
const model = editor.getModel();
if (model) {
// Check if the YAML language support exists
// This is dynamically added by monaco-yaml and may not be typed correctly
const yamlDefaults = (monaco.languages as any).yaml?.yamlDefaults;
if (yamlDefaults && typeof yamlDefaults.setDiagnosticsOptions === 'function') {
yamlDefaults.setDiagnosticsOptions({
validate: true,
schemas: [
{
uri: 'http://myserver/metric-yaml-schema.json',
fileMatch: ['*'],
schema: {
type: 'object',
required: ['Person', 'Place', 'Age', 'Siblings'],
properties: {
Person: { type: 'string' },
Place: { type: 'string' },
Age: { type: 'number' },
Siblings: {
type: 'object',
additionalProperties: { type: 'number' }
}
}
}
}
]
});
}
}
};
export const MyYamlEditor: React.FC = () => { export const MyYamlEditor: React.FC = () => {
const editorRef = useRef<any>(null); const editorRef = useRef<editor.IStandaloneCodeEditor | null>(null);
// Called once the Monaco editor is mounted // Called once the Monaco editor is mounted
const editorDidMount = (editor: any, monacoInstance: typeof import('monaco-editor')) => { const editorDidMount = (
editor: editor.IStandaloneCodeEditor,
monacoInstance: typeof import('monaco-editor')
) => {
editorRef.current = editor; editorRef.current = editor;
// Lint the document on every change // Try to configure YAML schema if monaco-yaml is properly loaded
try {
// Check if the YAML language support exists using type assertion
if ((monacoInstance.languages as any).yaml?.yamlDefaults) {
configureYamlSchema(monacoInstance, editor);
}
} catch (err) {
console.error('Failed to configure YAML schema:', err);
}
// Lint the document on every change using our custom validator
editor.onDidChangeModelContent(() => { editor.onDidChangeModelContent(() => {
const value = editor.getValue(); const value = editor.getValue();
const markers = validateMetricYaml(value, monacoInstance); const markers = validateMetricYaml(value, monacoInstance);
monacoInstance.editor.setModelMarkers(editor.getModel(), 'yaml', markers); monacoInstance.editor.setModelMarkers(editor.getModel()!, 'yaml', markers);
}); });
}; };

View File

@ -21,25 +21,25 @@
--text-3xs: 6px; --text-3xs: 6px;
--text-3xs--line-height: 1; --text-3xs--line-height: 1;
--text-2xs: 8px; --text-2xs: 8px;
--text-2xs--line-height: 1.3; --text-2xs--line-height: 1.25;
--text-xs: 11px; --text-xs: 11px;
--text-xs--line-height: 1.3; --text-xs--line-height: 1.25;
--text-sm: 12px; --text-sm: 12px;
--text-sm--line-height: 1.3; --text-sm--line-height: 1.25;
--text-base: 13px; --text-base: 13px;
--text-base--line-height: 1.3; --text-base--line-height: 1.25;
--text-md: 14px; --text-md: 14px;
--text-md--line-height: 1.3; --text-md--line-height: 1.25;
--text-lg: 16px; --text-lg: 16px;
--text-lg--line-height: 1.3; --text-lg--line-height: 1.25;
--text-xl: 18px; --text-xl: 18px;
--text-xl--line-height: 1.3; --text-xl--line-height: 1.25;
--text-2xl: 20px; --text-2xl: 20px;
--text-2xl--line-height: 1.3; --text-2xl--line-height: 1.25;
--text-3xl: 24px; --text-3xl: 24px;
--text-3xl--line-height: 1.3; --text-3xl--line-height: 1.25;
--text-4xl: 30px; --text-4xl: 30px;
--text-4xl--line-height: 1.3; --text-4xl--line-height: 1.25;
--text-size-inherit: inherit; --text-size-inherit: inherit;
--text-icon-size: 16px; --text-icon-size: 16px;