import React, { useState, useEffect } from 'react' import { useParams, useNavigate } from 'react-router-dom' import { useEntities, EntityFieldType } from '../../contexts/EntityContext' import { FaSave, FaArrowLeft, FaPlus, FaTrashAlt, FaDatabase, FaCog, FaTable, FaColumns, } from 'react-icons/fa' import { CustomEntityField } from '@/proxy/developerKit/models' import { ROUTES_ENUM } from '@/routes/route.constant' import { useLocalization } from '@/utils/hooks/useLocalization' import { Formik, Form, Field, FieldProps, FieldArray } from 'formik' import * as Yup from 'yup' import { FormItem, Input, Select, Checkbox, FormContainer, Button } from '@/components/ui' import { SelectBoxOption } from '@/shared/types' // Validation schema const validationSchema = Yup.object({ name: Yup.string().required('Entity name is required'), displayName: Yup.string().required('Display name is required'), tableName: Yup.string().required('Table name is required'), description: Yup.string(), fields: Yup.array() .of( Yup.object({ name: Yup.string().required('Field name is required'), type: Yup.string().required('Field type is required'), isRequired: Yup.boolean(), maxLength: Yup.number().nullable(), isUnique: Yup.boolean(), defaultValue: Yup.string().notRequired(), description: Yup.string().notRequired(), displayOrder: Yup.number().required(), }), ) .min(1, 'At least one field is required'), isActive: Yup.boolean(), hasAuditFields: Yup.boolean(), hasSoftDelete: Yup.boolean(), }) const EntityEditor: React.FC = () => { const { id } = useParams() const navigate = useNavigate() const { translate } = useLocalization() const { getEntity, addEntity, updateEntity } = useEntities() const isEditing = !!id // Initial values for Formik const [initialValues, setInitialValues] = useState({ name: '', displayName: '', tableName: '', description: '', fields: [ { id: crypto.randomUUID(), entityId: id || '', name: 'Name', type: 'string' as EntityFieldType, isRequired: true, maxLength: 100, description: 'Entity name', displayOrder: 1, }, ] as CustomEntityField[], isActive: true, hasAuditFields: true, hasSoftDelete: true, }) // Check if migration is applied to disable certain fields const isMigrationApplied = Boolean( isEditing && id && getEntity(id)?.migrationStatus === 'applied', ) useEffect(() => { if (isEditing && id) { const entity = getEntity(id) if (entity) { // Ensure fields are sorted by displayOrder and normalized to sequential values const sortedFields = (entity.fields || []) .slice() .sort((a, b) => (a.displayOrder ?? 0) - (b.displayOrder ?? 0)) .map((f, idx) => ({ ...f, displayOrder: f.displayOrder ?? idx + 1 })) setInitialValues({ name: entity.name, displayName: entity.displayName, tableName: entity.tableName, description: entity.description || '', fields: sortedFields, isActive: entity.isActive, hasAuditFields: entity.hasAuditFields, hasSoftDelete: entity.hasSoftDelete, }) } } }, [id, isEditing, getEntity]) const handleSubmit = async (values: typeof initialValues, { setSubmitting }: any) => { try { const sanitizedFields = values.fields.map((f) => { // send both `displayOrder` (frontend proxy) and `order` (backend DTO) to be safe const sanitized: any = { ...(f.id && isEditing ? { id: f.id } : {}), name: f.name.trim(), type: f.type, isRequired: f.isRequired, maxLength: f.maxLength, isUnique: f.isUnique || false, defaultValue: f.defaultValue, description: f.description, displayOrder: f.displayOrder, order: f.displayOrder, } return sanitized }) const entityData = { name: values.name.trim(), displayName: values.displayName.trim(), tableName: values.tableName.trim(), description: values.description.trim(), fields: sanitizedFields, isActive: values.isActive, hasAuditFields: values.hasAuditFields, hasSoftDelete: values.hasSoftDelete, } if (isEditing && id) { await updateEntity(id, entityData) } else { await addEntity(entityData) } navigate(ROUTES_ENUM.protected.saas.developerKit.entities, { replace: true }) } catch (error) { console.error('Error saving entity:', error) alert('Failed to save entity. Please try again.') } finally { setSubmitting(false) } } const fieldTypes = [ { value: 'string', label: 'String' }, { value: 'number', label: 'Number' }, { value: 'decimal', label: 'Decimal' }, { value: 'boolean', label: 'Boolean' }, { value: 'date', label: 'Date' }, { value: 'guid', label: 'Guid' }, ] return ( {({ values, touched, errors, isSubmitting, setFieldValue, submitForm, isValid }) => ( <> {/* Enhanced Header */}

{isEditing ? `${translate('::App.DeveloperKit.EntityEditor.Title.Edit')} - ${values.name || initialValues.name || 'Entity'}` : translate('::App.DeveloperKit.Entity.CreateEntity')}

{isEditing ? 'Modify your entity' : 'Create a new entity'}

{/* Save Button in Header */}
{/* Basic Entity Information */}

Entity Settings

{({ field }: FieldProps) => ( { field.onBlur(e) if (!values.tableName) { setFieldValue('tableName', values.name + 's') } if (!values.displayName) { setFieldValue('displayName', values.name) } }} disabled={isMigrationApplied} placeholder="e.g., Product, User, Order" className="px-2 py-1.5 bg-slate-50 focus:bg-white transition-all duration-200 text-sm h-7" /> )}
{/* Fields Section */}
{({ push, remove }) => ( <>

{translate('::App.DeveloperKit.EntityEditor.Fields')}

Order *
{translate('::App.DeveloperKit.EntityEditor.FieldName')} *
{translate('::App.DeveloperKit.EntityEditor.Type')} *
{translate('::App.DeveloperKit.EntityEditor.DefaultValue')}
{translate('::App.DeveloperKit.EntityEditor.MaxLength')}
{translate('::App.DeveloperKit.EntityEditor.Description')}
{translate('::App.DeveloperKit.EntityEditor.Required')}
{translate('::App.DeveloperKit.EntityEditor.Unique')}
{values.fields.map((field, index) => (
{({ field, form }: FieldProps) => (