UNPKG

@mindmakr/gs-websdk

Version:

Web SDK for Guru SaaS System - Complete JavaScript/TypeScript SDK for building applications with dynamic schema management

467 lines 15.5 kB
"use strict"; /** * Form Helper Utilities * Provides utility functions for form generation, rendering, and data handling */ Object.defineProperty(exports, "__esModule", { value: true }); exports.exportFormData = exports.calculateFormCompletion = exports.generateFieldOptions = exports.shouldDisplayField = exports.getFieldWidth = exports.getFieldDisplayConfig = exports.transformFormDataForSubmission = exports.generateFormLayout = exports.extractValidationRules = exports.generateInitialFormData = exports.createFormConfig = exports.determineFieldType = exports.generateFormFields = void 0; /** * Generate form fields configuration from schema template */ function generateFormFields(template) { const fields = []; const schema = template.schema_definition; if (!schema.properties) { return fields; } const fieldOrder = schema['x-field-order'] || Object.keys(schema.properties); fieldOrder.forEach((key, index) => { const property = schema.properties[key]; if (!property) return; const field = { id: `field_${key}_${index}`, key, type: determineFieldType(property), properties: property, required: schema.required?.includes(key) || false, order: property['x-order'] || index, layout: property.layout || property['ui:layout'] || { width: 'full' } }; fields.push(field); }); return fields.sort((a, b) => a.order - b.order); } exports.generateFormFields = generateFormFields; /** * Determine field type from schema property */ function determineFieldType(property) { // Layout fields if (property.type === 'null' && property['ui:widget'] === 'section') { return 'section'; } // Selection fields if (property.enum) { return property['ui:widget'] === 'radio' ? 'radio' : 'select'; } if (property.type === 'array' && property.items?.enum) { return property['ui:widget'] === 'checkboxes' ? 'checkbox' : 'multiselect'; } // Format-based fields if (property.format) { const formatMap = { 'textarea': 'textarea', 'email': 'email', 'password': 'password', 'url': 'url', 'date': 'date', 'datetime': 'datetime', 'time': 'time', 'color': 'color', 'richtext': 'richtext', 'image-url-or-upload': 'image_input', 'video-url-or-upload': 'video_input', 'audio-url-or-upload': 'audio_input', 'reference': 'reference', 'phone': 'phone', 'currency': 'currency', 'percentage': 'percentage', 'rating': 'rating', 'slider': 'slider', 'formatted-number': 'formatted_number' }; if (property.format && formatMap[property.format]) { return formatMap[property.format]; } } // Type-based fields if (property.type === 'boolean') return 'boolean'; if (property.type === 'number' || property.type === 'integer') return 'number'; if (property.type === 'array') return 'array'; if (property.type === 'object') return 'object'; // Default to text return 'text'; } exports.determineFieldType = determineFieldType; /** * Create form configuration for rendering */ function createFormConfig(template, instance) { const fields = generateFormFields(template); const initialData = instance?.instance_data || generateInitialFormData(template); const validationRules = extractValidationRules(template); const layout = generateFormLayout(fields); return { fields, initialData, validationRules, layout }; } exports.createFormConfig = createFormConfig; /** * Generate initial form data from template */ function generateInitialFormData(template) { const data = {}; const schema = template.schema_definition; if (!schema.properties) { return data; } Object.entries(schema.properties).forEach(([key, property]) => { if (property.default !== undefined) { data[key] = property.default; } else { // Set appropriate default values based on type switch (property.type) { case 'boolean': data[key] = false; break; case 'array': data[key] = []; break; case 'object': data[key] = {}; break; case 'number': case 'integer': if (property.minimum !== undefined) { data[key] = property.minimum; } break; case 'string': if (property.enum && property.enum.length > 0) { data[key] = property.enum[0]; } break; } } }); return data; } exports.generateInitialFormData = generateInitialFormData; /** * Extract validation rules from schema */ function extractValidationRules(template) { const rules = {}; const schema = template.schema_definition; if (!schema.properties) { return rules; } Object.entries(schema.properties).forEach(([key, property]) => { const fieldRules = {}; // Required validation if (schema.required?.includes(key)) { fieldRules.required = true; } // String validations if (property.type === 'string') { if (property.minLength !== undefined) { fieldRules.minLength = property.minLength; } if (property.maxLength !== undefined) { fieldRules.maxLength = property.maxLength; } if (property.pattern) { fieldRules.pattern = property.pattern; } } // Number validations if (property.type === 'number' || property.type === 'integer') { if (property.minimum !== undefined) { fieldRules.min = property.minimum; } if (property.maximum !== undefined) { fieldRules.max = property.maximum; } } // Array validations if (property.type === 'array') { if (property.minItems !== undefined) { fieldRules.minItems = property.minItems; } if (property.maxItems !== undefined) { fieldRules.maxItems = property.maxItems; } } // Format validations if (property.format) { fieldRules.format = property.format; } // Custom validation rules if (property.validation) { fieldRules.custom = property.validation; } if (Object.keys(fieldRules).length > 0) { rules[key] = fieldRules; } }); return rules; } exports.extractValidationRules = extractValidationRules; /** * Generate form layout configuration */ function generateFormLayout(fields) { const sections = []; let currentSection = { fields: [] }; fields.forEach(field => { // Check if this is a section divider if (field.type === 'section') { // Save current section if it has fields if (currentSection.fields.length > 0) { sections.push(currentSection); } // Start new section currentSection = { title: field.properties.title, fields: [] }; } else { currentSection.fields.push(field.key); } }); // Add the last section if (currentSection.fields.length > 0) { sections.push(currentSection); } // If no sections were created, create a default one if (sections.length === 0) { sections.push({ fields: fields.filter(f => f.type !== 'section').map(f => f.key) }); } return { sections }; } exports.generateFormLayout = generateFormLayout; /** * Transform form data for submission */ function transformFormDataForSubmission(formData, template) { const transformed = {}; const schema = template.schema_definition; if (!schema.properties) { return formData; } Object.entries(formData).forEach(([key, value]) => { const property = schema.properties[key]; if (!property) { transformed[key] = value; return; } // Transform based on field type and format if (value === null || value === undefined || value === '') { // Skip empty values unless they're required if (!schema.required?.includes(key)) { return; } transformed[key] = null; return; } switch (property.type) { case 'number': case 'integer': transformed[key] = typeof value === 'string' ? parseFloat(value) : value; break; case 'boolean': transformed[key] = Boolean(value); break; case 'array': transformed[key] = Array.isArray(value) ? value : [value]; break; case 'object': transformed[key] = typeof value === 'object' ? value : {}; break; case 'string': if (property.format === 'date' && value instanceof Date) { transformed[key] = value.toISOString().split('T')[0]; } else if (property.format === 'datetime' && value instanceof Date) { transformed[key] = value.toISOString(); } else { transformed[key] = String(value); } break; default: transformed[key] = value; } }); return transformed; } exports.transformFormDataForSubmission = transformFormDataForSubmission; /** * Get field display configuration */ function getFieldDisplayConfig(field) { const config = { label: field.properties.title || field.key, required: field.required, width: getFieldWidth(field.layout?.width || 'full') }; // Add optional properties const optionalConfig = {}; if (field.properties.description) { optionalConfig.placeholder = field.properties.description; } if (field.properties.helpText) { optionalConfig.helpText = field.properties.helpText; } if (field.properties['ui:disabled']) { optionalConfig.disabled = true; } if (field.properties['ui:hidden']) { optionalConfig.hidden = true; } if (field.properties['ui:className'] || field.layout?.className) { optionalConfig.className = field.properties['ui:className'] || field.layout?.className; } return { ...config, ...optionalConfig }; } exports.getFieldDisplayConfig = getFieldDisplayConfig; /** * Convert layout width to CSS class or percentage */ function getFieldWidth(width) { const widthMap = { 'quarter': '25%', '25': '25%', 'half': '50%', '50': '50%', 'three-quarter': '75%', '75': '75%', 'full': '100%', '100': '100%' }; return widthMap[width] || '100%'; } exports.getFieldWidth = getFieldWidth; /** * Check if field should be conditionally displayed */ function shouldDisplayField(field, formData) { if (!field.layout?.conditional) { return true; } const { field: conditionField, operator, value } = field.layout.conditional; const fieldValue = formData[conditionField]; switch (operator) { case 'equals': return fieldValue === value; case 'not_equals': return fieldValue !== value; case 'contains': return Array.isArray(fieldValue) && fieldValue.includes(value); default: return true; } } exports.shouldDisplayField = shouldDisplayField; /** * Generate field options for select, radio, and checkbox fields */ function generateFieldOptions(field) { const options = []; if (field.properties.enum && field.properties.enumNames) { // Use enumNames for labels if available field.properties.enum.forEach((value, index) => { options.push({ value, label: field.properties.enumNames[index] || String(value) }); }); } else if (field.properties.enum) { // Use enum values as both value and label field.properties.enum.forEach((value) => { options.push({ value, label: String(value) }); }); } else if (field.type === 'multiselect' && field.properties.items?.enum) { // For multiselect fields field.properties.items.enum.forEach((value) => { options.push({ value, label: String(value) }); }); } return options; } exports.generateFieldOptions = generateFieldOptions; /** * Calculate form completion percentage */ function calculateFormCompletion(formData, template) { const schema = template.schema_definition; const requiredFields = schema.required || []; const allFields = Object.keys(schema.properties || {}); let completed = 0; const missingFields = []; allFields.forEach(field => { const value = formData[field]; const hasValue = value !== undefined && value !== null && value !== ''; if (hasValue) { completed++; } else if (requiredFields.includes(field)) { missingFields.push(field); } }); return { percentage: Math.round((completed / allFields.length) * 100), completed, total: allFields.length, missingFields }; } exports.calculateFormCompletion = calculateFormCompletion; /** * Export form data in various formats */ function exportFormData(formData, template, format = 'json') { switch (format) { case 'json': return JSON.stringify(formData, null, 2); case 'csv': const headers = Object.keys(formData); const values = headers.map(header => { const value = formData[header]; if (typeof value === 'object') { return JSON.stringify(value); } return String(value || ''); }); return [ headers.join(','), values.map(v => `"${v.replace(/"/g, '""')}"`).join(',') ].join('\n'); case 'xml': const xmlData = Object.entries(formData) .map(([key, value]) => { const xmlValue = typeof value === 'object' ? JSON.stringify(value) : String(value || ''); return ` <${key}>${xmlValue}</${key}>`; }) .join('\n'); return `<?xml version="1.0" encoding="UTF-8"?>\n<formData>\n${xmlData}\n</formData>`; default: return JSON.stringify(formData, null, 2); } } exports.exportFormData = exportFormData; //# sourceMappingURL=form-helpers.js.map