258 lines
8.7 KiB
JavaScript
258 lines
8.7 KiB
JavaScript
import {
|
|
FIELD_TYPES,
|
|
LAYOUT_TYPES,
|
|
OPTION_SORTABLE_TYPES,
|
|
EXCLUDED_GROUP_TYPES
|
|
} from './constants.js';
|
|
|
|
/**
|
|
* Sorts field options by index for fields that support options
|
|
* @param {Object} field - Field object
|
|
* @returns {Array} Sorted option labels
|
|
*/
|
|
const sortFieldOptions = (field) => {
|
|
if (!field.options) return [];
|
|
return Object.values(field.options)
|
|
.sort((a, b) => Number(a.index) - Number(b.index))
|
|
.map(option => option.label);
|
|
};
|
|
|
|
/**
|
|
* Checks if a field should have its options sorted
|
|
* @param {string} fieldType - Type of the field
|
|
* @returns {boolean} True if options should be sorted
|
|
*/
|
|
const shouldSortOptions = (fieldType) => OPTION_SORTABLE_TYPES.includes(fieldType);
|
|
|
|
/**
|
|
* Adds system status fields to the fields array if they are enabled
|
|
* @param {Array} properties - All property objects
|
|
* @param {Array} fields - Array to add fields to
|
|
*/
|
|
const addSystemStatusFields = (properties, fields) => {
|
|
const propertyList = Object.values(properties);
|
|
|
|
[FIELD_TYPES.STATUS, FIELD_TYPES.STATUS_ASSIGNEE, FIELD_TYPES.CATEGORY].forEach(type => {
|
|
const field = propertyList.find(f => f.type === type);
|
|
if (field && field.enabled) {
|
|
fields.push(field);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Processes fields in a regular row layout
|
|
* @param {Array} rowFields - Fields in the row
|
|
* @param {Object} properties - Property mapping
|
|
* @param {Array} fields - Array to add processed fields to
|
|
* @param {Array} spacers - Array to add spacer elements to
|
|
*/
|
|
const processRowFields = (rowFields, properties, fields, spacers) => {
|
|
for (const field of rowFields) {
|
|
if ([LAYOUT_TYPES.LABEL, LAYOUT_TYPES.HR].includes(field.type)) continue;
|
|
|
|
if (field.type === LAYOUT_TYPES.SPACER) {
|
|
spacers.push(field);
|
|
continue;
|
|
}
|
|
|
|
const fieldProperty = properties[field.code];
|
|
if ([FIELD_TYPES.SUBTABLE, FIELD_TYPES.GROUP].includes(fieldProperty.type)) continue;
|
|
|
|
const fieldToAdd = shouldSortOptions(fieldProperty.type)
|
|
? { ...fieldProperty, sortedOptions: sortFieldOptions(fieldProperty) }
|
|
: fieldProperty;
|
|
|
|
fields.push(fieldToAdd);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Processes subtable fields
|
|
* @param {Object} layout - Subtable layout object
|
|
* @param {Object} properties - Property mapping
|
|
* @param {Array} fields - Array to add processed fields to
|
|
*/
|
|
const processSubtableFields = (layout, properties, fields) => {
|
|
const tableCode = layout.code;
|
|
const tableField = properties[tableCode];
|
|
|
|
if (tableField.type !== FIELD_TYPES.SUBTABLE) return;
|
|
|
|
const tableFields = {};
|
|
|
|
layout.fields.forEach(({ code: fieldCode }) => {
|
|
const subField = tableField.fields[fieldCode];
|
|
const processedField = shouldSortOptions(subField.type)
|
|
? { ...subField, table: tableCode, sortedOptions: sortFieldOptions(subField) }
|
|
: { ...subField, table: tableCode };
|
|
|
|
tableFields[fieldCode] = processedField;
|
|
fields.push(processedField);
|
|
});
|
|
|
|
fields.push({ ...tableField, fields: tableFields });
|
|
};
|
|
|
|
/**
|
|
* Processes group fields
|
|
* @param {Object} layout - Group layout object
|
|
* @param {Object} properties - Property mapping
|
|
* @param {Array} fields - Array to add processed fields to
|
|
* @param {Array} spacers - Array to add spacer elements to
|
|
*/
|
|
const processGroupFields = (layout, properties, fields, spacers) => {
|
|
const groupCode = layout.code;
|
|
const groupField = properties[groupCode];
|
|
|
|
if (groupField.type !== FIELD_TYPES.GROUP) return;
|
|
|
|
const groupFields = {};
|
|
|
|
layout.layout.forEach(row => {
|
|
row.fields.forEach(field => {
|
|
if ([LAYOUT_TYPES.LABEL, LAYOUT_TYPES.HR].includes(field.type)) return;
|
|
|
|
if (field.type === LAYOUT_TYPES.SPACER) {
|
|
spacers.push({ ...field, group: groupCode });
|
|
return;
|
|
}
|
|
|
|
const groupSubField = properties[field.code];
|
|
|
|
if (EXCLUDED_GROUP_TYPES.includes(groupSubField.type)) return;
|
|
|
|
const processedField = shouldSortOptions(groupSubField.type)
|
|
? { ...groupSubField, group: groupCode, sortedOptions: sortFieldOptions(groupSubField) }
|
|
: { ...groupSubField, group: groupCode };
|
|
|
|
groupFields[field.code] = processedField;
|
|
fields.push(processedField);
|
|
});
|
|
});
|
|
|
|
fields.push({ ...groupField, fields: groupFields });
|
|
};
|
|
|
|
/**
|
|
* Marks lookup copy fields in the fields array
|
|
* @param {Array} fields - Array of fields to process
|
|
*/
|
|
const markLookupCopies = (fields) => {
|
|
const lookupCopyTypes = [
|
|
FIELD_TYPES.SINGLE_LINE_TEXT, FIELD_TYPES.NUMBER, FIELD_TYPES.MULTI_LINE_TEXT,
|
|
FIELD_TYPES.RICH_TEXT, FIELD_TYPES.LINK, FIELD_TYPES.CHECK_BOX,
|
|
FIELD_TYPES.RADIO_BUTTON, FIELD_TYPES.DROP_DOWN, FIELD_TYPES.MULTI_SELECT,
|
|
FIELD_TYPES.DATE, FIELD_TYPES.TIME, FIELD_TYPES.DATETIME,
|
|
FIELD_TYPES.USER_SELECT, FIELD_TYPES.ORGANIZATION_SELECT,
|
|
FIELD_TYPES.GROUP_SELECT, FIELD_TYPES.CALC, FIELD_TYPES.RECORD_NUMBER
|
|
];
|
|
|
|
fields.forEach(field => {
|
|
if (field.lookup && Array.isArray(field.lookup.fieldMappings)) {
|
|
field.lookup.fieldMappings.forEach(mapping => {
|
|
const targetField = fields.find(f =>
|
|
lookupCopyTypes.includes(f.type) && f.code === mapping.field
|
|
);
|
|
if (targetField) {
|
|
targetField.isLookupCopy = true;
|
|
}
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Adds system fields that aren't already in the fields array
|
|
* @param {Array} properties - Property objects
|
|
* @param {Array} fields - Array to add fields to
|
|
*/
|
|
const addSystemFields = (properties, fields) => {
|
|
const propertyList = Object.values(properties);
|
|
const systemFieldTypes = [
|
|
FIELD_TYPES.RECORD_NUMBER,
|
|
FIELD_TYPES.CREATOR,
|
|
FIELD_TYPES.CREATED_TIME,
|
|
FIELD_TYPES.MODIFIER,
|
|
FIELD_TYPES.UPDATED_TIME
|
|
];
|
|
|
|
systemFieldTypes.forEach(type => {
|
|
const field = propertyList.find(f => f.type === type);
|
|
if (field && !fields.some(f => f.type === type)) {
|
|
fields.push(field);
|
|
}
|
|
});
|
|
};
|
|
|
|
export const kintonePrettyFields = {
|
|
/**
|
|
* Generates processed field and spacer arrays from form properties and layout
|
|
* @param {Object} properties - Field properties object
|
|
* @param {Array} layouts - Layout definitions
|
|
* @returns {Object} Object containing fields and spacers arrays
|
|
*/
|
|
generateFields: (properties, layouts) => {
|
|
const fields = [];
|
|
const spacers = [];
|
|
|
|
// Add system status fields first
|
|
addSystemStatusFields(properties, fields);
|
|
|
|
// Process each layout item
|
|
layouts.forEach(layout => {
|
|
switch (layout.type) {
|
|
case LAYOUT_TYPES.ROW:
|
|
processRowFields(layout.fields, properties, fields, spacers);
|
|
break;
|
|
case LAYOUT_TYPES.SUBTABLE:
|
|
processSubtableFields(layout, properties, fields);
|
|
break;
|
|
case LAYOUT_TYPES.GROUP:
|
|
processGroupFields(layout, properties, fields, spacers);
|
|
break;
|
|
}
|
|
});
|
|
|
|
// Mark lookup copy fields
|
|
markLookupCopies(fields);
|
|
|
|
// Add any missing system fields
|
|
addSystemFields(properties, fields);
|
|
|
|
return { fields, spacers };
|
|
},
|
|
isCategory: field => field.type === "CATEGORY",
|
|
isCheckBox: field => field.type === "CHECK_BOX",
|
|
isCreatedTime: field => field.type === "CREATED_TIME",
|
|
isCreator: field => field.type === "CREATOR",
|
|
isDate: field => field.type === "DATE",
|
|
isDatetime: field => field.type === "DATETIME",
|
|
isDropDown: field => field.type === "DROP_DOWN",
|
|
isFile: field => field.type === "FILE",
|
|
isGroup: field => field.type === "GROUP",
|
|
isGroupSelect: field => field.type === "GROUP_SELECT",
|
|
isInGroup: field => 'group' in field,
|
|
isInSubtable: field => 'table' in field,
|
|
isLink: field => field.type === "LINK",
|
|
isLookup: field => 'lookup' in field,
|
|
isLookupCopy: field => field.isLookupCopy === true,
|
|
isModifier: field => field.type === "MODIFIER",
|
|
isMultiLineText: field => field.type === "MULTI_LINE_TEXT",
|
|
isMultiSelect: field => field.type === "MULTI_SELECT",
|
|
isNotInSubtable: field => !('table' in field),
|
|
isNumber: field => field.type === "NUMBER",
|
|
isOrganizationSelect: field => field.type === "ORGANIZATION_SELECT",
|
|
isRadioButton: field => field.type === "RADIO_BUTTON",
|
|
isRecordNumber: field => field.type === "RECORD_NUMBER",
|
|
isReferenceTable: field => field.type === "REFERENCE_TABLE",
|
|
isRichText: field => field.type === "RICH_TEXT",
|
|
isSingleLineText: field => field.type === "SINGLE_LINE_TEXT",
|
|
isStatus: field => field.type === "STATUS",
|
|
isStatusAssignee: field => field.type === "STATUS_ASSIGNEE",
|
|
isSubtable: field => field.type === "SUBTABLE",
|
|
isTime: field => field.type === "TIME",
|
|
isUpdatedTime: field => field.type === "UPDATED_TIME",
|
|
isUserSelect: field => field.type === "USER_SELECT",
|
|
};
|