From 8c74740eb35afe237981d309861a33dd30f848b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sedat=20=C3=96ZT=C3=9CRK?= <76204082+iamsedatozturk@users.noreply.github.com> Date: Mon, 11 Aug 2025 12:20:26 +0300 Subject: [PATCH] =?UTF-8?q?CustomComponents=20hatalar=C4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui/src/components/developerKit/CodeLayout.tsx | 703 +++++++----------- .../developerKit/ComponentEditor.tsx | 29 +- .../developerKit/ComponentManager.tsx | 151 ++-- 3 files changed, 363 insertions(+), 520 deletions(-) diff --git a/ui/src/components/developerKit/CodeLayout.tsx b/ui/src/components/developerKit/CodeLayout.tsx index 9f06587b..c2ba35a0 100644 --- a/ui/src/components/developerKit/CodeLayout.tsx +++ b/ui/src/components/developerKit/CodeLayout.tsx @@ -1,5 +1,5 @@ -import { useState, useEffect, useCallback } from "react"; -import { PanelTop as Panels } from "lucide-react"; +import { useState, useEffect, useCallback } from 'react' +import { PanelTop as Panels } from 'lucide-react' import { parseReactCode, updateComponentProp, @@ -11,23 +11,19 @@ import { removeComponentAndHooksFromCode, generateComponentJSX, insertJSXAtPosition, -} from "../../utils/codeParser"; -import { ComponentLibrary } from "../../components/codeLayout/ComponentLibrary"; -import { Splitter } from "../../components/codeLayout/Splitter"; -import { PanelManager } from "../../components/codeLayout/PanelManager"; -import { CodeEditor } from "../../components/codeLayout/CodeEditor"; -import { - ComponentDefinition, - ComponentInfo, - EditorState, -} from "../../@types/componentInfo"; -import PropertyPanel from "../../components/codeLayout/PropertyPanel"; -import ComponentSelector from "../../components/codeLayout/ComponentSelector"; -import { useParams } from "react-router-dom"; -import { useComponents } from "../../contexts/ComponentContext"; -import { toast } from "../../components/ui"; -import Notification from "../../components/ui/Notification/Notification"; -import { PanelState } from "../../components/codeLayout/data/componentDefinitions"; +} from '../../utils/codeParser' +import { ComponentLibrary } from '../../components/codeLayout/ComponentLibrary' +import { Splitter } from '../../components/codeLayout/Splitter' +import { PanelManager } from '../../components/codeLayout/PanelManager' +import { CodeEditor } from '../../components/codeLayout/CodeEditor' +import { ComponentDefinition, ComponentInfo, EditorState } from '../../@types/componentInfo' +import PropertyPanel from '../../components/codeLayout/PropertyPanel' +import ComponentSelector from '../../components/codeLayout/ComponentSelector' +import { useParams } from 'react-router-dom' +import { useComponents } from '../../contexts/ComponentContext' +import { toast } from '../../components/ui' +import Notification from '../../components/ui/Notification/Notification' +import { PanelState } from '../../components/codeLayout/data/componentDefinitions' const INITIAL_CODE = `const Component = () => { return ( @@ -37,29 +33,29 @@ const INITIAL_CODE = `const Component = () => { }; export default Component -`; +` function CodeLayout() { - const { id } = useParams(); - const { getComponent, updateComponent } = useComponents(); - const [showPanelManager, setShowPanelManager] = useState(false); + const { id } = useParams() + const { getComponent, updateComponent } = useComponents() + const [showPanelManager, setShowPanelManager] = useState(false) const [panelState, setPanelState] = useState({ toolbox: true, properties: true, - }); + }) const [editorState, setEditorState] = useState({ code: INITIAL_CODE, components: [], selectedComponentId: null, - }); + }) - const isEditing = !!id; - const [code, setCode] = useState(INITIAL_CODE); - const [hasCodeChanges, setHasCodeChanges] = useState(false); - const [isLoaded, setIsLoaded] = useState(false); - const [name, setName] = useState(""); - const [dependencies, setDependencies] = useState([]); - const [isActive, setIsActive] = useState(true); + const isEditing = !!id + const [code, setCode] = useState(INITIAL_CODE) + const [hasCodeChanges, setHasCodeChanges] = useState(false) + const [isLoaded, setIsLoaded] = useState(false) + const [name, setName] = useState('') + const [dependencies, setDependencies] = useState([]) + const [isActive, setIsActive] = useState(true) const handleSave = async () => { try { @@ -68,77 +64,75 @@ function CodeLayout() { dependencies: JSON.stringify(dependencies), // Serialize dependencies to JSON string code: code.trim(), isActive, - }; + } if (isEditing && id) { - updateComponent(id, componentData); - parseAndUpdateComponents(componentData.code); + updateComponent(id, componentData) + parseAndUpdateComponents(componentData.code) } } catch (error) { - console.error("Error saving component:", error); - alert("Failed to save component. Please try again."); + console.error('Error saving component:', error) + alert('Failed to save component. Please try again.') } finally { - setHasCodeChanges(false); - setIsLoaded(true); + setHasCodeChanges(false) + setIsLoaded(true) toast.push( "Bileşen başarıyla kaydedildi." , { - placement: "top-center", - } - ); + placement: 'top-center', + }, + ) } - }; + } // Load existing component data - sadece edit modunda useEffect(() => { if (isEditing && id && !isLoaded) { - const component = getComponent(id); + const component = getComponent(id) if (component) { - setName(component.name); + setName(component.name) // setDescription(component.description || ""); // Parse dependencies from JSON string try { - const deps = component.dependencies - ? JSON.parse(component.dependencies) - : []; - setDependencies(Array.isArray(deps) ? deps : []); + const deps = component.dependencies ? JSON.parse(component.dependencies) : [] + setDependencies(Array.isArray(deps) ? deps : []) } catch { - setDependencies([]); + setDependencies([]) } - setCode(component.code); // Mevcut kodu yükle + setCode(component.code) // Mevcut kodu yükle // Parse components from the loaded code - parseAndUpdateComponents(component.code); - setIsActive(component.isActive); - setIsLoaded(true); + parseAndUpdateComponents(component.code) + setIsActive(component.isActive) + setIsLoaded(true) } } else if (!isEditing && !isLoaded) { // Yeni komponent için boş başla - TEMPLATE YOK - setIsLoaded(true); + setIsLoaded(true) } - }, [id, isEditing, getComponent, isLoaded]); + }, [id, isEditing, getComponent, isLoaded]) // NEW: Handle drop to Code Editor instead of Canvas const handleDropToCodeEditor = ( componentDef: ComponentDefinition, - position: { line: number; column: number } + position: { line: number; column: number }, ): void => { - if (!componentDef || typeof componentDef !== "object") { - console.error("Component definition is null or undefined"); - return; + if (!componentDef || typeof componentDef !== 'object') { + console.error('Component definition is null or undefined') + return } if (!componentDef.name) { - console.error("Invalid component definition in handleDropToCodeEditor"); - return; + console.error('Invalid component definition in handleDropToCodeEditor') + return } - const propertiesObj: Record = {}; + const propertiesObj: Record = {} if (Array.isArray(componentDef.properties)) { componentDef.properties.forEach((prop) => { - propertiesObj[prop.name] = prop; - }); + propertiesObj[prop.name] = prop + }) } const newComponent: ComponentInfo = { id: generateUniqueId(), @@ -149,141 +143,123 @@ function CodeLayout() { endLine: 0, startColumn: 0, endColumn: 0, - }; + } // Generate JSX string for this component - const componentJSX = generateComponentJSX(newComponent); + const componentJSX = generateComponentJSX(newComponent) // Insert the JSX at the cursor position in the code editor - setCode(insertJSXAtPosition(code, componentJSX, position)); - }; + setCode(insertJSXAtPosition(code, componentJSX, position)) + } const handleAppDragOver = (e: React.DragEvent) => { - e.preventDefault(); - }; + e.preventDefault() + } const parseAndUpdateComponents = useCallback((code: string) => { try { - const parsed = parseReactCode(code); + const parsed = parseReactCode(code) setEditorState((prev) => ({ ...prev, code, components: parsed.components, - })); + })) } catch (error) { - const msg = - error instanceof Error - ? error.message - : "Koddan komponentler parse edilemedi."; - console.log("Parse error:", msg); + const msg = error instanceof Error ? error.message : 'Koddan komponentler parse edilemedi.' + console.log('Parse error:', msg) setEditorState((prev) => ({ ...prev, code, // Only update the code, keep existing components - })); + })) } - }, []); + }, []) // Handle code changes from Monaco Editor const handleCodeChange = useCallback( (value: string | undefined) => { if (value !== undefined) { - setCode(value); - setHasCodeChanges(value !== editorState.code); + setCode(value) + setHasCodeChanges(value !== editorState.code) } }, - [editorState.code] - ); + [editorState.code], + ) // Apply code changes const handleApplyCodeChanges = useCallback(() => { - parseAndUpdateComponents(code); - setHasCodeChanges(false); - }, [parseAndUpdateComponents, code]); + parseAndUpdateComponents(code) + setHasCodeChanges(false) + }, [parseAndUpdateComponents, code]) // Reset code changes const handleResetCodeChanges = useCallback(() => { - setCode(editorState.code); - setHasCodeChanges(false); - }, [editorState.code]); + setCode(editorState.code) + setHasCodeChanges(false) + }, [editorState.code]) // Handle property changes from Property Panel // Handle hook toggle const handleHookToggle = useCallback( (componentId: string, hookType: string, enabled: boolean) => { - console.log("🪝 App: handleHookToggle called:", { + console.log('🪝 App: handleHookToggle called:', { componentId, hookType, enabled, - }); - const selectedComponent = editorState.components.find( - (c) => c.id === componentId - ); - if (!selectedComponent) return; + }) + const selectedComponent = editorState.components?.find((c) => c.id === componentId) + if (!selectedComponent) return // Use the most up-to-date code (pendingCode if available, otherwise editorState.code) - let updatedCode = code || editorState.code; - console.log( - "🔍 App: Using code source:", - code === editorState.code ? "same" : "pendingCode" - ); + let updatedCode = code || editorState.code + console.log('🔍 App: Using code source:', code === editorState.code ? 'same' : 'pendingCode') if (enabled) { // Check if hook is already present - more specific check - const varName = generateHookVariableName(hookType, componentId); - console.log("🔍 App: Checking for existing hook variable:", varName); + const varName = generateHookVariableName(hookType, componentId) + console.log('🔍 App: Checking for existing hook variable:', varName) // Create a more specific regex to check for actual hook declarations const hookDeclarationRegex = new RegExp( - `const\\s+(?:\\[.*?${varName}.*?\\]|${varName})\\s*=\\s*${hookType}\\s*\\(` - ); + `const\\s+(?:\\[.*?${varName}.*?\\]|${varName})\\s*=\\s*${hookType}\\s*\\(`, + ) if (hookDeclarationRegex.test(updatedCode)) { - console.log("⚠️ App: Hook already exists, skipping"); - return; // Hook already exists + console.log('⚠️ App: Hook already exists, skipping') + return // Hook already exists } // Handle React imports - improved approach - const reactImportRegex = - /^import\s+\{([^}]*)\}\s+from\s+['"]react['"]\s*;?/m; - const reactMatch = reactImportRegex.exec(updatedCode); + const reactImportRegex = /^import\s+\{([^}]*)\}\s+from\s+['"]react['"]\s*;?/m + const reactMatch = reactImportRegex.exec(updatedCode) if (reactMatch) { // reactMatch[1] --> süslü parantez içi örn: "useState, useRef" let existingHooks = reactMatch[1] - .split(",") + .split(',') .map((h) => h.trim()) - .filter((h) => h); + .filter((h) => h) // Eğer hookType yoksa ekle if (!existingHooks.includes(hookType)) { - existingHooks.push(hookType); + existingHooks.push(hookType) } // Tekrarları kaldır (güvenlik için) - existingHooks = [...new Set(existingHooks)]; + existingHooks = [...new Set(existingHooks)] - const newImport = `import { ${existingHooks.join( - ", " - )} } from 'react';\n`; - updatedCode = updatedCode.replace(reactImportRegex, newImport); + const newImport = `import { ${existingHooks.join(', ')} } from 'react';\n` + updatedCode = updatedCode.replace(reactImportRegex, newImport) - console.log( - "🔄 App: Updated existing React import:", - newImport.trim() - ); + console.log('🔄 App: Updated existing React import:', newImport.trim()) } else { // React import satırı yoksa ekle - const importLine = `import { ${hookType} } from 'react';\n\n`; - updatedCode = importLine + updatedCode; - console.log("🔄 App: Added new React import:", importLine.trim()); + const importLine = `import { ${hookType} } from 'react';\n\n` + updatedCode = importLine + updatedCode + console.log('🔄 App: Added new React import:', importLine.trim()) } // Add hook declaration - const hookCode = generateHookCode( - hookType, - componentId, - selectedComponent.type - ); - console.log("🔍 App: Generated hook code:", hookCode); + const hookCode = generateHookCode(hookType, componentId, selectedComponent.type) + console.log('🔍 App: Generated hook code:', hookCode) // Try multiple patterns for function declaration const functionPatterns = [ @@ -291,445 +267,323 @@ function CodeLayout() { /function\s+\w+\s*\([^)]*\)\s*\{/, // function AnyName() { /const\s+\w+\s*=\s*\([^)]*\)\s*=>\s*\{/, // const Component = () => { /export\s+default\s+function\s*\([^)]*\)\s*\{/, // export default function() { - ]; + ] - let match2 = null; + let match2 = null for (const pattern of functionPatterns) { - match2 = updatedCode.match(pattern); + match2 = updatedCode.match(pattern) if (match2) { - console.log("🔍 App: Found function with pattern:", pattern); - break; + console.log('🔍 App: Found function with pattern:', pattern) + break } } - console.log("🔍 App: Function match result:", match2); - console.log( - "🔍 App: Updated code preview:", - updatedCode.substring(0, 500) - ); + console.log('🔍 App: Function match result:', match2) + console.log('🔍 App: Updated code preview:', updatedCode.substring(0, 500)) - if (match2 && typeof match2.index === "number") { - const insertPosition = match2.index + match2[0].length; + if (match2 && typeof match2.index === 'number') { + const insertPosition = match2.index + match2[0].length updatedCode = updatedCode.slice(0, insertPosition) + - "\n " + + '\n ' + hookCode + - "\n" + - updatedCode.slice(insertPosition); - console.log("✅ App: Hook code inserted successfully"); + '\n' + + updatedCode.slice(insertPosition) + console.log('✅ App: Hook code inserted successfully') } else { - console.log("⚠️ App: Could not find function body to insert hook"); + console.log('⚠️ App: Could not find function body to insert hook') // Fallback: insert after the first opening brace - const firstBrace = updatedCode.indexOf("{"); + const firstBrace = updatedCode.indexOf('{') if (firstBrace !== -1) { updatedCode = updatedCode.slice(0, firstBrace + 1) + - "\n " + + '\n ' + hookCode + - "\n" + - updatedCode.slice(firstBrace + 1); - console.log("✅ App: Hook code inserted using fallback method"); + '\n' + + updatedCode.slice(firstBrace + 1) + console.log('✅ App: Hook code inserted using fallback method') } } // Update component properties if needed - if (hookType === "useState") { - const setterName = `set${ - varName.charAt(0).toUpperCase() + varName.slice(1) - }`; + if (hookType === 'useState') { + const setterName = `set${varName.charAt(0).toUpperCase() + varName.slice(1)}` // Update component props based on type - if (selectedComponent.type === "input") { + if (selectedComponent.type === 'input') { + updatedCode = updateComponentProp(updatedCode, componentId, 'value', `{${varName}}`) updatedCode = updateComponentProp( updatedCode, componentId, - "value", - `{${varName}}` - ); + 'onChange', + `{(e) => ${setterName}(e.target.value)}`, + ) + } else if (selectedComponent.type === 'button') { updatedCode = updateComponentProp( updatedCode, componentId, - "onChange", - `{(e) => ${setterName}(e.target.value)}` - ); - } else if (selectedComponent.type === "button") { + 'onClick', + `{() => ${setterName}(!${varName})}`, + ) + } else if (selectedComponent.type === 'checkbox') { + updatedCode = updateComponentProp(updatedCode, componentId, 'checked', `{${varName}}`) updatedCode = updateComponentProp( updatedCode, componentId, - "onClick", - `{() => ${setterName}(!${varName})}` - ); - } else if (selectedComponent.type === "checkbox") { - updatedCode = updateComponentProp( - updatedCode, - componentId, - "checked", - `{${varName}}` - ); - updatedCode = updateComponentProp( - updatedCode, - componentId, - "onChange", - `{(val) => ${setterName}(val)}` - ); + 'onChange', + `{(val) => ${setterName}(val)}`, + ) } - } else if (hookType === "useRef") { - updatedCode = updateComponentProp( - updatedCode, - componentId, - "ref", - `{${varName}}` - ); + } else if (hookType === 'useRef') { + updatedCode = updateComponentProp(updatedCode, componentId, 'ref', `{${varName}}`) } } else { // Remove hook - updatedCode = removeHookFromCode(updatedCode, hookType, componentId); + updatedCode = removeHookFromCode(updatedCode, hookType, componentId) // Remove related props - if (hookType === "useState") { - if (selectedComponent.type === "input") { - updatedCode = updateComponentProp( - updatedCode, - componentId, - "value", - "" - ); - updatedCode = updateComponentProp( - updatedCode, - componentId, - "onChange", - null - ); - } else if (selectedComponent.type === "button") { - updatedCode = updateComponentProp( - updatedCode, - componentId, - "onClick", - null - ); - } else if (selectedComponent.type === "checkbox") { - updatedCode = updateComponentProp( - updatedCode, - componentId, - "checked", - false - ); - updatedCode = updateComponentProp( - updatedCode, - componentId, - "onChange", - null - ); + if (hookType === 'useState') { + if (selectedComponent.type === 'input') { + updatedCode = updateComponentProp(updatedCode, componentId, 'value', '') + updatedCode = updateComponentProp(updatedCode, componentId, 'onChange', null) + } else if (selectedComponent.type === 'button') { + updatedCode = updateComponentProp(updatedCode, componentId, 'onClick', null) + } else if (selectedComponent.type === 'checkbox') { + updatedCode = updateComponentProp(updatedCode, componentId, 'checked', false) + updatedCode = updateComponentProp(updatedCode, componentId, 'onChange', null) } - } else if (hookType === "useRef") { - updatedCode = updateComponentProp( - updatedCode, - componentId, - "ref", - null - ); + } else if (hookType === 'useRef') { + updatedCode = updateComponentProp(updatedCode, componentId, 'ref', null) } } setEditorState((prev) => { - console.log("🔄 App: Final updatedCode before parsing:", updatedCode); - const parsed = parseReactCode(updatedCode); - console.log("🔄 App: Parsed components:", parsed.components.length); + console.log('🔄 App: Final updatedCode before parsing:', updatedCode) + const parsed = parseReactCode(updatedCode) + console.log('🔄 App: Parsed components:', parsed.components?.length) const newState = { code: updatedCode, components: parsed.components, selectedComponentId: prev.selectedComponentId, // Preserve selection - }; - console.log( - "🔄 App: New editor state code preview:", - newState.code.substring(0, 300) - ); - return newState; - }); + } + console.log('🔄 App: New editor state code preview:', newState.code.substring(0, 300)) + return newState + }) // Also update pending code to match - setCode(updatedCode); + setCode(updatedCode) }, - [editorState.code, editorState.components, code] - ); + [editorState.code, editorState.components, code], + ) // Zincirleme hook güncelleme fonksiyonu const applyMultipleHookToggles = ( - toggles: { componentId: string; hookType: string; enabled: boolean }[] + toggles: { componentId: string; hookType: string; enabled: boolean }[], ) => { - let updatedCode = code || editorState.code; + let updatedCode = code || editorState.code // 1. Sadece kaldırılması gereken hook'ları kaldır toggles .filter((t) => t.enabled === false) .forEach(({ hookType, componentId }) => { - const selectedComponent = editorState.components.find( - (c) => c.id === componentId - ); - if (!selectedComponent) return; - updatedCode = removeHookFromCode(updatedCode, hookType, componentId); + const selectedComponent = editorState.components?.find((c) => c.id === componentId) + if (!selectedComponent) return + updatedCode = removeHookFromCode(updatedCode, hookType, componentId) // Prop temizliği - if (hookType === "useState") { - if (selectedComponent.type === "input") { - updatedCode = updateComponentProp( - updatedCode, - componentId, - "value", - "" - ); - updatedCode = updateComponentProp( - updatedCode, - componentId, - "onChange", - null - ); - } else if (selectedComponent.type === "button") { - updatedCode = updateComponentProp( - updatedCode, - componentId, - "onClick", - null - ); - } else if (selectedComponent.type === "checkbox") { - updatedCode = updateComponentProp( - updatedCode, - componentId, - "checked", - false - ); - updatedCode = updateComponentProp( - updatedCode, - componentId, - "onChange", - null - ); + if (hookType === 'useState') { + if (selectedComponent.type === 'input') { + updatedCode = updateComponentProp(updatedCode, componentId, 'value', '') + updatedCode = updateComponentProp(updatedCode, componentId, 'onChange', null) + } else if (selectedComponent.type === 'button') { + updatedCode = updateComponentProp(updatedCode, componentId, 'onClick', null) + } else if (selectedComponent.type === 'checkbox') { + updatedCode = updateComponentProp(updatedCode, componentId, 'checked', false) + updatedCode = updateComponentProp(updatedCode, componentId, 'onChange', null) } - } else if (hookType === "useRef") { - updatedCode = updateComponentProp( - updatedCode, - componentId, - "ref", - null - ); + } else if (hookType === 'useRef') { + updatedCode = updateComponentProp(updatedCode, componentId, 'ref', null) } - }); + }) // 2. Eklenmesi gereken hook'ları ekle (veya zaten varsa dokunma) toggles .filter((t) => t.enabled === true) .forEach(({ hookType, componentId }) => { - const selectedComponent = editorState.components.find( - (c) => c.id === componentId - ); - if (!selectedComponent) return; - const varName = generateHookVariableName(hookType, componentId); + const selectedComponent = editorState.components?.find((c) => c.id === componentId) + if (!selectedComponent) return + const varName = generateHookVariableName(hookType, componentId) // Hook kodu fonksiyon gövdesinde yoksa ekle const hookDeclarationRegex = new RegExp( - `const\\s+(?:\\[.*?${varName}.*?\\]|${varName})\\s*=\\s*${hookType}\\s*\\(` - ); + `const\\s+(?:\\[.*?${varName}.*?\\]|${varName})\\s*=\\s*${hookType}\\s*\\(`, + ) if (!hookDeclarationRegex.test(updatedCode)) { const reactImportRegex = // eslint-disable-next-line no-useless-escape - /import\\s+React(?:\\s*,\\s*\\{([^}]*)\\})?\\s+from\\s+['\"]react['\"];?/; - const importMatch = updatedCode.match(reactImportRegex); - const allHooks = new Set(); + /import\\s+React(?:\\s*,\\s*\\{([^}]*)\\})?\\s+from\\s+['\"]react['\"];?/ + const importMatch = updatedCode.match(reactImportRegex) + const allHooks = new Set() if (importMatch && importMatch[1]) { importMatch[1] - .split(",") + .split(',') .map((h) => h.trim()) .filter((h) => h) - .forEach((h) => allHooks.add(h)); + .forEach((h) => allHooks.add(h)) } - allHooks.add(hookType); - let newImport = ""; + allHooks.add(hookType) + let newImport = '' if (allHooks.size > 0) { - newImport = `import { ${Array.from(allHooks).join( - ", " - )} } from 'react';\n`; + newImport = `import { ${Array.from(allHooks).join(', ')} } from 'react';\n` } if (importMatch) { - updatedCode = updatedCode.replace(reactImportRegex, newImport); + updatedCode = updatedCode.replace(reactImportRegex, newImport) } else { - updatedCode = newImport + updatedCode; + updatedCode = newImport + updatedCode } // Hook kodunu fonksiyon gövdesine ekle - const hookCode = generateHookCode( - hookType, - componentId, - selectedComponent.type - ); + const hookCode = generateHookCode(hookType, componentId, selectedComponent.type) const functionPatterns = [ /function\s+Component\s*\([^)]*\)\s*\{/, // function Component() { /function\s+\w+\s*\([^)]*\)\s*\{/, // function AnyName() { /const\s+\w+\s*=\s*\([^)]*\)\s*=>\s*\{/, // const Component = () => { /export\s+default\s+function\s*\([^)]*\)\s*\{/, // export default function() { - ]; - let match2 = null; + ] + let match2 = null for (const pattern of functionPatterns) { - match2 = updatedCode.match(pattern); - if (match2) break; + match2 = updatedCode.match(pattern) + if (match2) break } - if (match2 && typeof match2.index === "number") { - const insertPosition = match2.index + match2[0].length; + if (match2 && typeof match2.index === 'number') { + const insertPosition = match2.index + match2[0].length updatedCode = updatedCode.slice(0, insertPosition) + - "\n " + + '\n ' + hookCode + - "\n" + - updatedCode.slice(insertPosition); + '\n' + + updatedCode.slice(insertPosition) } else { - const firstBrace = updatedCode.indexOf("{"); + const firstBrace = updatedCode.indexOf('{') if (firstBrace !== -1) { updatedCode = updatedCode.slice(0, firstBrace + 1) + - "\n " + + '\n ' + hookCode + - "\n" + - updatedCode.slice(firstBrace + 1); + '\n' + + updatedCode.slice(firstBrace + 1) } } } // Prop güncellemeleri - if (hookType === "useState") { - const setterName = `set${ - varName.charAt(0).toUpperCase() + varName.slice(1) - }`; - if (selectedComponent.type === "input") { + if (hookType === 'useState') { + const setterName = `set${varName.charAt(0).toUpperCase() + varName.slice(1)}` + if (selectedComponent.type === 'input') { + updatedCode = updateComponentProp(updatedCode, componentId, 'value', `{${varName}}`) updatedCode = updateComponentProp( updatedCode, componentId, - "value", - `{${varName}}` - ); + 'onChange', + `{(e) => ${setterName}(e.target.value)}`, + ) + } else if (selectedComponent.type === 'button') { updatedCode = updateComponentProp( updatedCode, componentId, - "onChange", - `{(e) => ${setterName}(e.target.value)}` - ); - } else if (selectedComponent.type === "button") { + 'onClick', + `{() => ${setterName}(!${varName})}`, + ) + } else if (selectedComponent.type === 'checkbox') { + updatedCode = updateComponentProp(updatedCode, componentId, 'checked', `{${varName}}`) updatedCode = updateComponentProp( updatedCode, componentId, - "onClick", - `{() => ${setterName}(!${varName})}` - ); - } else if (selectedComponent.type === "checkbox") { - updatedCode = updateComponentProp( - updatedCode, - componentId, - "checked", - `{${varName}}` - ); - updatedCode = updateComponentProp( - updatedCode, - componentId, - "onChange", - `{(e) => ${setterName}(e.target.checked)}` - ); + 'onChange', + `{(e) => ${setterName}(e.target.checked)}`, + ) } - } else if (hookType === "useRef") { - updatedCode = updateComponentProp( - updatedCode, - componentId, - "ref", - `{${varName}}` - ); + } else if (hookType === 'useRef') { + updatedCode = updateComponentProp(updatedCode, componentId, 'ref', `{${varName}}`) } - }); + }) setEditorState((prev) => { - const parsed = parseReactCode(updatedCode); + const parsed = parseReactCode(updatedCode) return { code: updatedCode, components: parsed.components, selectedComponentId: prev.selectedComponentId, - }; - }); - setCode(updatedCode); - }; + } + }) + setCode(updatedCode) + } // Handle multiple property changes at once const handlePropertiesChange = useCallback( (componentId: string, updates: Record) => { - console.log("🔄 App: handlePropertiesChange called:", { + console.log('🔄 App: handlePropertiesChange called:', { componentId, updates, - }); + }) - const updatedCode = updateComponentProps( - editorState.code, - componentId, - updates - ); - console.log( - "📝 App: Properties updated, code changed:", - editorState.code !== updatedCode - ); + const updatedCode = updateComponentProps(editorState.code, componentId, updates) + console.log('📝 App: Properties updated, code changed:', editorState.code !== updatedCode) setEditorState((prev) => { - const parsed = parseReactCode(updatedCode); + const parsed = parseReactCode(updatedCode) return { code: updatedCode, components: parsed.components, selectedComponentId: prev.selectedComponentId, // Preserve selection - }; - }); + } + }) // Update pending code to reflect changes - setCode(updatedCode); + setCode(updatedCode) }, - [editorState.code] - ); + [editorState.code], + ) // Handle component list refresh const handleRefreshComponents = useCallback(() => { - parseAndUpdateComponents(editorState.code); - }, [parseAndUpdateComponents, editorState.code]); + parseAndUpdateComponents(editorState.code) + }, [parseAndUpdateComponents, editorState.code]) // Handle component selection const handleSelectComponent = useCallback((componentId: string | null) => { setEditorState((prev) => ({ ...prev, selectedComponentId: componentId, - })); - }, []); + })) + }, []) // Initialize parsing on mount useEffect(() => { - parseAndUpdateComponents(INITIAL_CODE); - }, [parseAndUpdateComponents]); + parseAndUpdateComponents(INITIAL_CODE) + }, [parseAndUpdateComponents]) - const handleDragStart = ( - _componentDef: ComponentDefinition, - e: React.DragEvent - ) => { - e.stopPropagation(); - }; + const handleDragStart = (_componentDef: ComponentDefinition, e: React.DragEvent) => { + e.stopPropagation() + } const selectedComponent = - editorState.components.find( - (c) => c.id === editorState.selectedComponentId - ) || null; + editorState.components?.find((c) => c.id === editorState.selectedComponentId) || null const renderLeftPanel = () => { - if (!panelState.toolbox) return null; - return ; - }; + if (!panelState.toolbox) return null + return + } // Komponent ve ilgili hook'ları koddan silen fonksiyon const handleDeleteComponent = (componentId: string) => { // Koddan JSX ve hook'ları sil - const updatedCode = removeComponentAndHooksFromCode(code, componentId); + const updatedCode = removeComponentAndHooksFromCode(code, componentId) // Koddan parse edip state'i güncelle - parseAndUpdateComponents(updatedCode); - setCode(updatedCode); + parseAndUpdateComponents(updatedCode) + setCode(updatedCode) // Seçili komponenti kaldır setEditorState((prev) => ({ ...prev, selectedComponentId: null, - })); - }; + })) + } const renderRightPanel = () => { - if (!panelState.properties) return null; + if (!panelState.properties) return null return (
@@ -749,8 +603,8 @@ function CodeLayout() { onDeleteComponent={handleDeleteComponent} />
- ); - }; + ) + } const mainContent = (
@@ -759,7 +613,7 @@ function CodeLayout() {

{name}

-

{dependencies.join(", ")}

+

{dependencies.join(', ')}

@@ -788,15 +642,15 @@ function CodeLayout() {
- ); + ) return (
{ - e.preventDefault(); - e.stopPropagation(); + e.preventDefault() + e.stopPropagation() }} > {/* Panel Yöneticisi Modal */} @@ -805,19 +659,12 @@ function CodeLayout() { isOpen={showPanelManager} onClose={() => setShowPanelManager(false)} panelState={panelState} - onPanelToggle={(panel) => - setPanelState((prev) => ({ ...prev, [panel]: !prev[panel] })) - } + onPanelToggle={(panel) => setPanelState((prev) => ({ ...prev, [panel]: !prev[panel] }))} /> )} {/* Sol Sidebar ve ana içerik */} {panelState.toolbox ? ( - + {renderLeftPanel()}
{panelState.properties ? ( @@ -855,7 +702,7 @@ function CodeLayout() {
)}
- ); + ) } -export default CodeLayout; +export default CodeLayout diff --git a/ui/src/components/developerKit/ComponentEditor.tsx b/ui/src/components/developerKit/ComponentEditor.tsx index f14bc2c6..c9ac563d 100644 --- a/ui/src/components/developerKit/ComponentEditor.tsx +++ b/ui/src/components/developerKit/ComponentEditor.tsx @@ -19,7 +19,7 @@ const ComponentEditor: React.FC = () => { const { id } = useParams() const navigate = useNavigate() const { translate } = useLocalization() - + const { getComponent, addComponent, updateComponent } = useComponents() const [name, setName] = useState('') @@ -59,10 +59,10 @@ const ComponentEditor: React.FC = () => { }, []) useEffect(() => { - if (code && editorState.components.length === 0) { + if (code && editorState.components?.length === 0) { parseAndUpdateComponents(code) } - }, [code, editorState.components.length]) + }, [code, editorState.components?.length]) // Load existing component data - sadece edit modunda useEffect(() => { @@ -144,7 +144,9 @@ const ComponentEditor: React.FC = () => {
-

{translate('::App.DeveloperKit.ComponentEditor.Loading')}

+

+ {translate('::App.DeveloperKit.ComponentEditor.Loading')} +

) @@ -220,7 +222,9 @@ const ComponentEditor: React.FC = () => { className="flex items-center gap-2 bg-yellow-600 text-white px-4 py-2 rounded-lg hover:bg-yellow-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed shadow-sm" > - {isSaving ? translate('::App.DeveloperKit.ComponentEditor.Saving') : translate('::App.DeveloperKit.ComponentEditor.Save')} + {isSaving + ? translate('::App.DeveloperKit.ComponentEditor.Saving') + : translate('::App.DeveloperKit.ComponentEditor.Save')} @@ -237,19 +241,26 @@ const ComponentEditor: React.FC = () => {

- {validationErrors.length} {translate('::App.DeveloperKit.ComponentEditor.ValidationError.Title')} - {validationErrors.length !== 1 ? 's' : ''} {translate('::App.DeveloperKit.ComponentEditor.ValidationError.Found')} + {validationErrors.length}{' '} + {translate('::App.DeveloperKit.ComponentEditor.ValidationError.Title')} + {validationErrors.length !== 1 ? 's' : ''}{' '} + {translate('::App.DeveloperKit.ComponentEditor.ValidationError.Found')}

{validationErrors.slice(0, 5).map((error, index) => (
- {translate('::App.DeveloperKit.ComponentEditor.ValidationError.Line')} {error.startLineNumber}:{' '} + + {translate('::App.DeveloperKit.ComponentEditor.ValidationError.Line')}{' '} + {error.startLineNumber}: + {' '} {error.message}
))} {validationErrors.length > 5 && (
- ... {translate('::App.DeveloperKit.ComponentEditor.ValidationError.And')} {validationErrors.length - 5} {translate('::App.DeveloperKit.ComponentEditor.ValidationError.More')} + ... {translate('::App.DeveloperKit.ComponentEditor.ValidationError.And')}{' '} + {validationErrors.length - 5}{' '} + {translate('::App.DeveloperKit.ComponentEditor.ValidationError.More')} {validationErrors.length - 5 !== 1 ? 's' : ''}
)} diff --git a/ui/src/components/developerKit/ComponentManager.tsx b/ui/src/components/developerKit/ComponentManager.tsx index cbd12878..f9508b13 100644 --- a/ui/src/components/developerKit/ComponentManager.tsx +++ b/ui/src/components/developerKit/ComponentManager.tsx @@ -1,6 +1,6 @@ -import React, { useState } from "react"; -import { Link } from "react-router-dom"; -import { useComponents } from "../../contexts/ComponentContext"; +import React, { useState } from 'react' +import { Link } from 'react-router-dom' +import { useComponents } from '../../contexts/ComponentContext' import { Plus, Search, @@ -14,21 +14,19 @@ import { CheckCircle, XCircle, View, -} from "lucide-react"; -import { ROUTES_ENUM } from "@/routes/route.constant"; -import { useLocalization } from "@/utils/hooks/useLocalization"; +} from 'lucide-react' +import { ROUTES_ENUM } from '@/routes/route.constant' +import { useLocalization } from '@/utils/hooks/useLocalization' const ComponentManager: React.FC = () => { - const { components, updateComponent, deleteComponent } = useComponents(); - const [searchTerm, setSearchTerm] = useState(""); - const [filterActive, setFilterActive] = useState< - "all" | "active" | "inactive" - >("all"); + const { components, updateComponent, deleteComponent } = useComponents() + const [searchTerm, setSearchTerm] = useState('') + const [filterActive, setFilterActive] = useState<'all' | 'active' | 'inactive'>('all') // Calculate statistics - const totalComponents = components?.length; - const activeComponents = components?.filter((c) => c.isActive).length; - const inactiveComponents = totalComponents - activeComponents; + const totalComponents = components?.length + const activeComponents = components?.filter((c) => c.isActive).length + const inactiveComponents = totalComponents - activeComponents const { translate } = useLocalization() const stats = [ @@ -36,62 +34,58 @@ const ComponentManager: React.FC = () => { name: translate('::App.DeveloperKit.Component.Total'), value: totalComponents, icon: Puzzle, - color: "text-purple-600", - bgColor: "bg-purple-100", + color: 'text-purple-600', + bgColor: 'bg-purple-100', }, { name: translate('::App.DeveloperKit.Component.Active'), value: activeComponents, icon: CheckCircle, - color: "text-emerald-600", - bgColor: "bg-emerald-100", + color: 'text-emerald-600', + bgColor: 'bg-emerald-100', }, { name: translate('::App.DeveloperKit.Component.Inactive'), value: inactiveComponents, icon: XCircle, - color: "text-slate-600", - bgColor: "bg-slate-100", + color: 'text-slate-600', + bgColor: 'bg-slate-100', }, - ]; + ] const filteredComponents = components?.filter((component) => { const matchesSearch = component.name.toLowerCase().includes(searchTerm.toLowerCase()) || - (component.description || "") - .toLowerCase() - .includes(searchTerm.toLowerCase()); + (component.description || '').toLowerCase().includes(searchTerm.toLowerCase()) const matchesFilter = - filterActive === "all" || - (filterActive === "active" && component.isActive) || - (filterActive === "inactive" && !component.isActive); + filterActive === 'all' || + (filterActive === 'active' && component.isActive) || + (filterActive === 'inactive' && !component.isActive) - return matchesSearch && matchesFilter; - }); + return matchesSearch && matchesFilter + }) const handleToggleActive = async (id: string, isActive: boolean) => { try { - const component = components.find((c) => c.id === id); + const component = components?.find((c) => c.id === id) if (component) { - await updateComponent(id, { ...component, isActive }); + await updateComponent(id, { ...component, isActive }) } } catch (err) { - console.error("Failed to toggle component status:", err); + console.error('Failed to toggle component status:', err) } - }; + } const handleDelete = async (id: string, name: string) => { - if ( - window.confirm(translate('::App.DeveloperKit.Component.ConfirmDelete')) - ) { + if (window.confirm(translate('::App.DeveloperKit.Component.ConfirmDelete'))) { try { - await deleteComponent(id); + await deleteComponent(id) } catch (err) { - console.error("Failed to delete component:", err); + console.error('Failed to delete component:', err) } } - }; + } return (
@@ -114,18 +108,11 @@ const ComponentManager: React.FC = () => { {/* Statistics Cards */}
{stats.map((stat, index) => ( -
+
-

- {stat.name} -

-

- {stat.value} -

+

{stat.name}

+

{stat.value}

@@ -152,14 +139,16 @@ const ComponentManager: React.FC = () => {
@@ -177,43 +166,35 @@ const ComponentManager: React.FC = () => {
-

- {component.name} -

+

{component.name}

{(() => { try { - const parsed = JSON.parse( - component.dependencies ?? "[]" - ); + const parsed = JSON.parse(component.dependencies ?? '[]') return Array.isArray(parsed) && parsed.length > 0 - ? `${parsed.join(", ")}` - : translate('::App.DeveloperKit.Component.NoDependencies'); + ? `${parsed.join(', ')}` + : translate('::App.DeveloperKit.Component.NoDependencies') } catch { - return translate('::App.DeveloperKit.Component.NoDependencies'); + return translate('::App.DeveloperKit.Component.NoDependencies') } })()}

{component.description && ( -

- {component.description} -

+

{component.description}

)}
{component.lastModificationTime - ? new Date( - component.lastModificationTime - ).toLocaleDateString() + ? new Date(component.lastModificationTime).toLocaleDateString() : translate('::App.DeveloperKit.Component.DateNotAvailable')}
@@ -237,13 +218,11 @@ const ComponentManager: React.FC = () => {
{ @@ -295,16 +280,16 @@ const ComponentManager: React.FC = () => {

- {searchTerm || filterActive !== "all" + {searchTerm || filterActive !== 'all' ? translate('::App.DeveloperKit.Component.Empty.Filtered.Title') : translate('::App.DeveloperKit.Component.Empty.Initial.Title')}

- {searchTerm || filterActive !== "all" + {searchTerm || filterActive !== 'all' ? translate('::App.DeveloperKit.Component.Empty.Filtered.Description') : translate('::App.DeveloperKit.Component.Empty.Initial.Description')}

- {!searchTerm && filterActive === "all" && ( + {!searchTerm && filterActive === 'all' && ( {
)}
- ); -}; + ) +} -export default ComponentManager; +export default ComponentManager