@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
JavaScript
;
/**
* 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