196 lines
5.8 KiB
JavaScript
196 lines
5.8 KiB
JavaScript
/**
|
|
* Field and schema manipulation helpers
|
|
*/
|
|
|
|
export const LIST_OPTION_ALIAS_KEYS = ["options", "optionsList", "optionList", "choices", "values", "items"];
|
|
|
|
export const optionEntryToLine = (entry) => {
|
|
if (entry == null) {
|
|
return null;
|
|
}
|
|
|
|
if (typeof entry === "string") {
|
|
const trimmed = entry.trim();
|
|
if (!trimmed) return null;
|
|
if (trimmed.includes("|")) return trimmed.replace(/\r/g, "");
|
|
return `${trimmed}|${trimmed}`;
|
|
}
|
|
|
|
if (Array.isArray(entry)) {
|
|
const [valueRaw, labelRaw] = entry;
|
|
const value = valueRaw ?? labelRaw;
|
|
const label = labelRaw ?? valueRaw;
|
|
if (value == null && label == null) return null;
|
|
const valueStr = `${value ?? ""}`.trim();
|
|
const labelStr = `${label ?? valueStr}`.trim();
|
|
if (!valueStr) return null;
|
|
return `${valueStr}|${labelStr || valueStr}`;
|
|
}
|
|
|
|
if (typeof entry === "object") {
|
|
const value =
|
|
entry.value ??
|
|
entry.id ??
|
|
entry.key ??
|
|
entry.slug ??
|
|
entry.code ??
|
|
entry.name ??
|
|
entry.label ??
|
|
entry.text;
|
|
const label = entry.label ?? entry.text ?? entry.name ?? entry.title ?? value;
|
|
if (value == null && label == null) return null;
|
|
const valueStr = `${value ?? ""}`.trim();
|
|
const labelStr = `${label ?? valueStr}`.trim();
|
|
if (!valueStr) return null;
|
|
return `${valueStr}|${labelStr || valueStr}`;
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
export const buildOptionsTextFromInput = (input) => {
|
|
if (input == null) {
|
|
return "";
|
|
}
|
|
|
|
if (Array.isArray(input)) {
|
|
return input.map(optionEntryToLine).filter(Boolean).join("\n");
|
|
}
|
|
|
|
if (typeof input === "object") {
|
|
return Object.entries(input)
|
|
.map(([value, label]) => optionEntryToLine({ value, label }))
|
|
.filter(Boolean)
|
|
.join("\n");
|
|
}
|
|
|
|
if (typeof input === "string") {
|
|
const trimmed = input.trim();
|
|
if (!trimmed) {
|
|
return "";
|
|
}
|
|
|
|
if (trimmed.includes("|")) {
|
|
return trimmed
|
|
.replace(/\r/g, "")
|
|
.split("\n")
|
|
.map((line) => line.trim())
|
|
.filter(Boolean)
|
|
.join("\n");
|
|
}
|
|
|
|
const hasNewLines = /\r|\n/.test(trimmed);
|
|
const separator = hasNewLines ? /\r?\n/ : /,/;
|
|
return trimmed
|
|
.split(separator)
|
|
.map((token) => token.trim())
|
|
.filter(Boolean)
|
|
.map((token) => `${token}|${token}`)
|
|
.join("\n");
|
|
}
|
|
|
|
return "";
|
|
};
|
|
|
|
export const normalizeListFieldDefinition = (field = {}) => {
|
|
if (!field || field.type !== "list") {
|
|
return field;
|
|
}
|
|
|
|
if (!field.listType) {
|
|
field.listType = "pulldown";
|
|
}
|
|
|
|
if (!field.optionsType) {
|
|
field.optionsType = "text";
|
|
}
|
|
|
|
if (field.optionsType === "text") {
|
|
let source = field.optionsText;
|
|
|
|
if (Array.isArray(source) || (source && typeof source === "object" && !Array.isArray(source))) {
|
|
field.optionsText = buildOptionsTextFromInput(source);
|
|
} else {
|
|
let aliasValue;
|
|
for (const aliasKey of LIST_OPTION_ALIAS_KEYS) {
|
|
if (field[aliasKey] != null) {
|
|
aliasValue = field[aliasKey];
|
|
delete field[aliasKey];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (aliasValue != null) {
|
|
field.optionsText = buildOptionsTextFromInput(aliasValue);
|
|
} else if (typeof field.optionsText === "string") {
|
|
field.optionsText = buildOptionsTextFromInput(field.optionsText);
|
|
} else {
|
|
field.optionsText = field.optionsText ?? "";
|
|
}
|
|
}
|
|
} else {
|
|
// Ensure plain text payload for non-text option sources.
|
|
if (field.optionsText && typeof field.optionsText !== "string") {
|
|
field.optionsText = "";
|
|
}
|
|
for (const aliasKey of LIST_OPTION_ALIAS_KEYS) {
|
|
if (field[aliasKey] != null) {
|
|
delete field[aliasKey];
|
|
}
|
|
}
|
|
}
|
|
|
|
return field;
|
|
};
|
|
|
|
export const normalizeSchemaForSave = (schema = {}) => {
|
|
if (!schema || typeof schema !== "object") {
|
|
return schema;
|
|
}
|
|
|
|
if (schema.schema && typeof schema.schema === "object") {
|
|
const normalized = {};
|
|
for (const [fieldName, fieldDefinition] of Object.entries(schema.schema)) {
|
|
if (!fieldDefinition || typeof fieldDefinition !== "object") {
|
|
normalized[fieldName] = fieldDefinition;
|
|
continue;
|
|
}
|
|
const clonedDefinition = { ...fieldDefinition };
|
|
if (clonedDefinition.type === "list") {
|
|
normalized[fieldName] = normalizeListFieldDefinition(clonedDefinition);
|
|
} else {
|
|
normalized[fieldName] = clonedDefinition;
|
|
}
|
|
}
|
|
schema.schema = normalized;
|
|
}
|
|
|
|
return schema;
|
|
};
|
|
|
|
export const mergeTableSchemas = (currentTable, incomingSchema = {}) => {
|
|
const merged = {
|
|
...currentTable,
|
|
schema: { ...(currentTable?.schema || {}) },
|
|
schemaInfo: { ...(currentTable?.schemaInfo || {}) },
|
|
};
|
|
|
|
if (!incomingSchema || typeof incomingSchema !== "object") {
|
|
return merged;
|
|
}
|
|
|
|
for (const [key, value] of Object.entries(incomingSchema)) {
|
|
if (key === "schema" && value && typeof value === "object") {
|
|
merged.schema = { ...(currentTable?.schema || {}), ...value };
|
|
} else if (key === "schemaInfo" && value && typeof value === "object") {
|
|
merged.schemaInfo = { ...(currentTable?.schemaInfo || {}), ...value };
|
|
} else {
|
|
merged[key] = value;
|
|
}
|
|
}
|
|
|
|
return merged;
|
|
};
|
|
|
|
|