import React, { useState, useEffect, useCallback } from 'react' import { useParams, useNavigate } from 'react-router-dom' import { useComponents } from '../../contexts/ComponentContext' import { FaRegSave, FaArrowLeft, FaExclamationCircle, FaSync, FaCode, FaEye, FaCog, } from 'react-icons/fa' import { parseReactCode } from '../../utils/codeParser' import ComponentPreview from '../../components/componentEditor/ComponentPreview' import { EditorState } from '../../proxy/developerKit/componentInfo' import { ROUTES_ENUM } from '@/routes/route.constant' import { useLocalization } from '@/utils/hooks/useLocalization' import { Formik, Form, Field, FieldProps } from 'formik' import * as Yup from 'yup' import { Checkbox, FormContainer, FormItem, Input } from '@/components/ui' // Error tipini tanımla interface ValidationError { message: string severity: number startLineNumber?: number } // Validation schema const validationSchema = Yup.object({ name: Yup.string().required('Component name is required'), description: Yup.string(), dependencies: Yup.array().of(Yup.string()), code: Yup.string(), isActive: Yup.boolean(), }) const ComponentEditor: React.FC = () => { const { id } = useParams() const navigate = useNavigate() const { translate } = useLocalization() const { getComponent, addComponent, updateComponent } = useComponents() const [validationErrors, setValidationErrors] = useState([]) const [isLoaded, setIsLoaded] = useState(false) const isEditing = !!id //Eksikler Dahil edit modunda const [editorState, setEditorState] = useState({ code: '', components: [], selectedComponentId: null, }) // Initial values for Formik const [initialValues, setInitialValues] = useState({ name: '', description: '', dependencies: [] as string[], code: '', isActive: true, }) const parseAndUpdateComponents = useCallback((code: string) => { try { 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) setEditorState((prev) => ({ ...prev, code, // Only update the code, keep existing components })) } }, []) // Load existing component data - sadece edit modunda useEffect(() => { if (isEditing && id && !isLoaded) { const component = getComponent(id) if (component) { // Parse dependencies from JSON string let deps: string[] = [] try { deps = component.dependencies ? JSON.parse(component.dependencies) : [] deps = Array.isArray(deps) ? deps : [] } catch { deps = [] } const values = { name: component.name, description: component.description || '', dependencies: deps, code: component.code, isActive: component.isActive, } setInitialValues(values) parseAndUpdateComponents(component.code) setIsLoaded(true) } } else if (!isEditing && !isLoaded) { // Yeni komponent için boş başla - TEMPLATE YOK setIsLoaded(true) } }, [id, isEditing, getComponent, isLoaded, parseAndUpdateComponents]) // Generate component template based on name const generateComponentTemplate = (componentName: string): string => { if (!componentName.trim()) return '' // Convert component name to PascalCase if not already const pascalCaseName = componentName.charAt(0).toUpperCase() + componentName.slice(1) return `const ${pascalCaseName}Component = ({ title = "${pascalCaseName}" }) => { return ( {title} ); }; export default ${pascalCaseName}Component;` } const handleSubmit = async (values: typeof initialValues, { setSubmitting }: any) => { if (!values.name.trim()) { alert('Please enter a component name') setSubmitting(false) return } const criticalErrors = validationErrors.filter((error) => { const message = error.message.toLowerCase() return ( !message.includes('can only be used in typescript files') && !message.includes('type annotations can only be used') && !message.includes('jsx-runtime') && !message.includes('corresponding type declarations') && error.severity === 8 ) }) if (criticalErrors.length > 0) { const proceed = window.confirm( `There are ${criticalErrors.length} critical error(s) in your code. Do you want to save anyway?`, ) if (!proceed) { setSubmitting(false) return } } try { const componentData = { name: values.name.trim(), description: values.description.trim(), dependencies: JSON.stringify(values.dependencies), // Serialize dependencies to JSON string code: values.code.trim(), isActive: values.isActive, } if (isEditing && id) { updateComponent(id, componentData) } else { addComponent(componentData) } navigate(ROUTES_ENUM.protected.saas.developerKit.components) } catch (error) { console.error('Error saving component:', error) alert('Failed to save component. Please try again.') } finally { setSubmitting(false) } } // Loading state - komponent yüklenene kadar bekle if (isEditing && !isLoaded) { return (

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

) } return ( {({ values, touched, errors, isSubmitting, setFieldValue, submitForm, isValid }) => ( <> {/* Enhanced Header */}

{isEditing ? `${translate('::App.DeveloperKit.ComponentEditor.Title.Edit')} - ${values.name || initialValues.name || 'Component'}` : translate('::App.DeveloperKit.ComponentEditor.Title.Create')}

{isEditing ? 'Modify your React component' : 'Create a new React component'}

{/* Save Button in Header */}
{/* Left Side - Component Settings */}

Component Settings

) => { const newName = e.target.value setFieldValue('name', newName) // Auto-generate code template if this is a new component (not editing) // and only if the code field is empty or contains the default template if (!isEditing && newName.trim()) { const currentCode = values.code.trim() const isCodeEmpty = !currentCode const isCodeDefaultTemplate = currentCode.includes('Component = ({') && currentCode.includes('export default') && currentCode.includes('{title}') if (isCodeEmpty || isCodeDefaultTemplate) { const template = generateComponentTemplate(newName) setFieldValue('code', template) parseAndUpdateComponents(template) } } }} /> ) => { setFieldValue('code', e.target.value) }} /> {({ field }: FieldProps) => ( setFieldValue( 'dependencies', e.target.value .split(',') .map((s) => s.trim()) .filter(Boolean), ) } placeholder="MyComponent, AnotherComponent, etc." /> )}
{/* Right Side - Preview and Validation */}
{/* Validation Errors */} {validationErrors.length > 0 && (

Validation Issues

{validationErrors.length} issue {validationErrors.length !== 1 ? 's' : ''} found in your code

{validationErrors.slice(0, 5).map((error, index) => (
Line {error.startLineNumber} {error.message}
))} {validationErrors.length > 5 && (
... and {validationErrors.length - 5} more issue {validationErrors.length - 5 !== 1 ? 's' : ''}
)}
)} {/* Component Preview */}

Preview

)}
) } export default ComponentEditor