new file: .claude/settings.local.json
new file: .gitignore new file: backend/.env.example new file: backend/.gitignore new file: backend/ecosystem.config.js new file: backend/nodemon.json new file: backend/package-lock.json new file: backend/package.json new file: backend/src/config/database.ts new file: backend/src/config/dynamicSwagger.ts new file: backend/src/config/environment.ts new file: backend/src/config/swagger.ts new file: backend/src/controllers/apiKeyController.ts new file: backend/src/controllers/authController.ts new file: backend/src/controllers/databaseController.ts new file: backend/src/controllers/databaseManagementController.ts new file: backend/src/controllers/dynamicApiController.ts new file: backend/src/controllers/endpointController.ts new file: backend/src/controllers/folderController.ts new file: backend/src/controllers/logsController.ts new file: backend/src/controllers/userController.ts new file: backend/src/middleware/apiKey.ts new file: backend/src/middleware/auth.ts new file: backend/src/middleware/logging.ts new file: backend/src/migrations/001_initial_schema.sql new file: backend/src/migrations/002_add_logging.sql new file: backend/src/migrations/003_add_scripting.sql new file: backend/src/migrations/004_add_superadmin.sql new file: backend/src/migrations/run.ts new file: backend/src/migrations/seed.ts new file: backend/src/routes/apiKeys.ts new file: backend/src/routes/auth.ts new file: backend/src/routes/databaseManagement.ts new file: backend/src/routes/databases.ts new file: backend/src/routes/dynamic.ts new file: backend/src/routes/endpoints.ts new file: backend/src/routes/folders.ts new file: backend/src/routes/logs.ts new file: backend/src/routes/users.ts new file: backend/src/server.ts new file: backend/src/services/DatabasePoolManager.ts new file: backend/src/services/ScriptExecutor.ts new file: backend/src/services/SqlExecutor.ts new file: backend/src/types/index.ts new file: backend/tsconfig.json new file: frontend/.gitignore new file: frontend/index.html new file: frontend/nginx.conf new file: frontend/package-lock.json new file: frontend/package.json new file: frontend/postcss.config.js new file: frontend/src/App.tsx new file: frontend/src/components/CodeEditor.tsx
This commit is contained in:
108
frontend/src/components/SqlEditor.tsx
Normal file
108
frontend/src/components/SqlEditor.tsx
Normal file
@@ -0,0 +1,108 @@
|
||||
import { useRef } from 'react';
|
||||
import Editor, { Monaco, loader } from '@monaco-editor/react';
|
||||
import { databasesApi } from '@/services/api';
|
||||
import * as monacoEditor from 'monaco-editor';
|
||||
|
||||
// Configure loader to use local Monaco
|
||||
loader.config({ monaco: monacoEditor });
|
||||
|
||||
interface SqlEditorProps {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
databaseId?: string;
|
||||
height?: string;
|
||||
}
|
||||
|
||||
export default function SqlEditor({ value, onChange, databaseId, height = '400px' }: SqlEditorProps) {
|
||||
const editorRef = useRef<any>(null);
|
||||
const monacoRef = useRef<Monaco | null>(null);
|
||||
|
||||
const handleEditorDidMount = (editor: any, monaco: Monaco) => {
|
||||
editorRef.current = editor;
|
||||
monacoRef.current = monaco;
|
||||
|
||||
// Configure SQL language features
|
||||
monaco.languages.registerCompletionItemProvider('sql', {
|
||||
provideCompletionItems: async (model, position) => {
|
||||
const word = model.getWordUntilPosition(position);
|
||||
const range = {
|
||||
startLineNumber: position.lineNumber,
|
||||
endLineNumber: position.lineNumber,
|
||||
startColumn: word.startColumn,
|
||||
endColumn: word.endColumn,
|
||||
};
|
||||
|
||||
let suggestions: any[] = [
|
||||
// SQL Keywords
|
||||
{ label: 'SELECT', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'SELECT ', range },
|
||||
{ label: 'FROM', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'FROM ', range },
|
||||
{ label: 'WHERE', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'WHERE ', range },
|
||||
{ label: 'JOIN', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'JOIN ', range },
|
||||
{ label: 'LEFT JOIN', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'LEFT JOIN ', range },
|
||||
{ label: 'INNER JOIN', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'INNER JOIN ', range },
|
||||
{ label: 'GROUP BY', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'GROUP BY ', range },
|
||||
{ label: 'ORDER BY', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'ORDER BY ', range },
|
||||
{ label: 'HAVING', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'HAVING ', range },
|
||||
{ label: 'LIMIT', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'LIMIT ', range },
|
||||
{ label: 'OFFSET', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'OFFSET ', range },
|
||||
{ label: 'AND', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'AND ', range },
|
||||
{ label: 'OR', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'OR ', range },
|
||||
{ label: 'NOT', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'NOT ', range },
|
||||
{ label: 'IN', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'IN ', range },
|
||||
{ label: 'LIKE', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'LIKE ', range },
|
||||
{ label: 'COUNT', kind: monaco.languages.CompletionItemKind.Function, insertText: 'COUNT()', range },
|
||||
{ label: 'SUM', kind: monaco.languages.CompletionItemKind.Function, insertText: 'SUM()', range },
|
||||
{ label: 'AVG', kind: monaco.languages.CompletionItemKind.Function, insertText: 'AVG()', range },
|
||||
{ label: 'MAX', kind: monaco.languages.CompletionItemKind.Function, insertText: 'MAX()', range },
|
||||
{ label: 'MIN', kind: monaco.languages.CompletionItemKind.Function, insertText: 'MIN()', range },
|
||||
];
|
||||
|
||||
// Fetch table names from database if databaseId is provided
|
||||
if (databaseId) {
|
||||
try {
|
||||
const { data } = await databasesApi.getTables(databaseId);
|
||||
const tableSuggestions = data.tables.map(table => ({
|
||||
label: table,
|
||||
kind: monaco.languages.CompletionItemKind.Class,
|
||||
insertText: table,
|
||||
detail: 'Table',
|
||||
range,
|
||||
}));
|
||||
suggestions = [...suggestions, ...tableSuggestions];
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch table names:', error);
|
||||
}
|
||||
}
|
||||
|
||||
return { suggestions };
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="border border-gray-300 rounded-lg overflow-hidden">
|
||||
<Editor
|
||||
height={height}
|
||||
defaultLanguage="sql"
|
||||
value={value}
|
||||
onChange={(value) => onChange(value || '')}
|
||||
onMount={handleEditorDidMount}
|
||||
theme="vs-light"
|
||||
options={{
|
||||
minimap: { enabled: false },
|
||||
fontSize: 14,
|
||||
lineNumbers: 'on',
|
||||
scrollBeyondLastLine: false,
|
||||
automaticLayout: true,
|
||||
tabSize: 2,
|
||||
wordWrap: 'on',
|
||||
formatOnPaste: true,
|
||||
formatOnType: true,
|
||||
suggestOnTriggerCharacters: true,
|
||||
quickSuggestions: true,
|
||||
acceptSuggestionOnEnter: 'on',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user