import React, { useState, useEffect } from 'react'; import { Paper, Stack, Text, Divider, Box, ScrollArea, } from '@mantine/core'; import { FormSidebar, FormField } from '@skybridge/web-components'; import Editor from '@monaco-editor/react'; import { functionApi, runtimeApi } from '../services/apiService'; import { FunctionDefinition, CreateFunctionRequest, UpdateFunctionRequest, RuntimeType } from '../types'; interface FunctionSidebarProps { opened: boolean; onClose: () => void; onSuccess: () => void; editFunction?: FunctionDefinition; } export const FunctionSidebar: React.FC = ({ opened, onClose, onSuccess, editFunction, }) => { const [runtimeOptions, setRuntimeOptions] = useState>([]); const [codeContent, setCodeContent] = useState(''); // Default images for each runtime const DEFAULT_IMAGES: Record = { 'nodejs18': 'node:18-alpine', 'python3.9': 'python:3.9-alpine', 'go1.20': 'golang:1.20-alpine', }; // Map runtime to Monaco editor language const getEditorLanguage = (runtime: string): string => { const languageMap: Record = { 'nodejs18': 'javascript', 'python3.9': 'python', 'go1.20': 'go', }; return languageMap[runtime] || 'javascript'; }; // Get default code template based on runtime const getDefaultCode = (runtime: string): string => { const templates: Record = { 'nodejs18': `exports.handler = async (event, context) => { console.log('Event:', JSON.stringify(event, null, 2)); return { statusCode: 200, body: JSON.stringify({ message: 'Hello from Node.js!', timestamp: new Date().toISOString() }) }; };`, 'python3.9': `import json from datetime import datetime def handler(event, context): print('Event:', json.dumps(event, indent=2)) return { 'statusCode': 200, 'body': json.dumps({ 'message': 'Hello from Python!', 'timestamp': datetime.now().isoformat() }) }`, 'go1.20': `package main import ( "context" "encoding/json" "fmt" "time" ) type Event map[string]interface{} func Handler(ctx context.Context, event Event) (map[string]interface{}, error) { fmt.Printf("Event: %+v\\n", event) return map[string]interface{}{ "statusCode": 200, "body": map[string]interface{}{ "message": "Hello from Go!", "timestamp": time.Now().Format(time.RFC3339), }, }, nil }` }; return templates[runtime] || templates['nodejs18']; }; useEffect(() => { loadRuntimeOptions(); if (editFunction) { setCodeContent(editFunction.code || ''); } }, [editFunction]); const loadRuntimeOptions = async () => { try { const runtimes = await runtimeApi.listRuntimes(); const options = runtimes.map((runtime: RuntimeType) => ({ value: runtime.name, label: `${runtime.name} (${runtime.version})` })); setRuntimeOptions(options); } catch (error) { console.error('Failed to load runtimes:', error); // Fallback options setRuntimeOptions([ { value: 'nodejs18', label: 'Node.js 18' }, { value: 'python3.9', label: 'Python 3.9' }, { value: 'go1.20', label: 'Go 1.20' } ]); } }; const fields: FormField[] = [ { name: 'name', label: 'Function Name', type: 'text', required: true, placeholder: 'my-function', validation: { pattern: /^[a-z0-9-]+$/ }, }, { name: 'description', label: 'Description', type: 'textarea', required: false, placeholder: 'Function description...', }, { name: 'runtime', label: 'Runtime', type: 'select', required: true, options: runtimeOptions, defaultValue: 'nodejs18', }, { name: 'timeout', label: 'Timeout (seconds)', type: 'number', required: true, defaultValue: 30, }, { name: 'memory', label: 'Memory (MB)', type: 'number', required: true, defaultValue: 128, }, { name: 'environment_variables', label: 'Environment Variables', type: 'json', required: false, defaultValue: '{}', }, ]; const handleSubmit = async (values: any) => { const submitData = { ...values, code: codeContent, docker_image: DEFAULT_IMAGES[values.runtime] || DEFAULT_IMAGES['nodejs18'], }; if (editFunction) { const updateRequest: UpdateFunctionRequest = { description: submitData.description, code: submitData.code, timeout: submitData.timeout, memory: submitData.memory, environment_variables: submitData.environment_variables, docker_image: submitData.docker_image, }; await functionApi.updateFunction(editFunction.id, updateRequest); } else { const createRequest: CreateFunctionRequest = submitData; await functionApi.createFunction(createRequest); } }; // Create a custom sidebar that includes the Monaco editor return ( {}} // Handled by parent onSuccess={onSuccess} title="Function" editMode={!!editFunction} editItem={editFunction} fields={fields} onSubmit={handleSubmit} width={600} style={{ position: 'relative', right: 'auto', top: 'auto', bottom: 'auto' }} /> Code Editor setCodeContent(value || '')} theme="vs-dark" options={{ minimap: { enabled: false }, scrollBeyondLastLine: false, fontSize: 14, }} /> ); };