Initial commit

This commit is contained in:
Jordan
2026-04-01 23:16:45 +01:00
commit 91cfdaee72
200 changed files with 25589 additions and 0 deletions

View File

@@ -0,0 +1,195 @@
/**
* 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;
};