// @ts-check /** * Map a CiviCRM CustomField row (as returned by APIv4 CustomField.get with * `custom_group_id.name` and `custom_group_id.title` joined in) to a * StaffFieldDescriptor for the staff report. * * Unknown data_type/html_type combinations fall back to "text" and the * caller should log a warning naming the field so we notice schema * additions we haven't modeled yet. * * Written as JS+JSDoc rather than TS so Node's built-in --test runner * can import this file directly without any tooling. TypeScript callers * still get full types via the JSDoc annotations. * * @typedef {import("../types/form").StaffFieldDescriptor} StaffFieldDescriptor * @typedef {import("../types/form").StaffRenderKind} StaffRenderKind * * The CustomFieldRow shape isn't fully expressible in JSDoc because two of * its property names contain dots (the APIv4 joined-field syntax). TypeScript * callers declare their own typed interface for the row; here we use a * permissive shape so the @ts-check pass doesn't complain about the dotted * accesses below. * * @typedef {Record & { * name: string; * label: string; * data_type: string; * html_type: string; * option_group_id: number | null | undefined; * weight: number; * }} CustomFieldRow */ const ORG_GROUP_NAMES = new Set(["Food_Co_op_Organizing"]); /** * @param {CustomFieldRow} row * @returns {StaffFieldDescriptor} */ export function mapCustomFieldRow(row) { const groupName = /** @type {string} */ (row["custom_group_id.name"]); const groupTitle = /** @type {string} */ (row["custom_group_id.title"]); /** @type {"activity" | "org"} */ const groupKind = ORG_GROUP_NAMES.has(groupName) ? "org" : "activity"; const optionGroupId = typeof row.option_group_id === "number" && row.option_group_id > 0 ? row.option_group_id : undefined; const render = inferRender(row.data_type, row.html_type, optionGroupId); return { groupName, groupTitle, groupKind, civiField: `${groupName}.${row.name}`, name: row.name, label: row.label, render, optionGroupId, }; } /** * @param {string} dataType * @param {string} htmlType * @param {number | undefined} optionGroupId * @returns {StaffRenderKind} */ function inferRender(dataType, htmlType, optionGroupId) { if (dataType === "Money") return "currency"; if (dataType === "Date") return "date"; if (dataType === "Timestamp") return "datetime"; if (dataType === "Boolean") return "boolean"; if (dataType === "File") return "file"; if (dataType === "Memo") return "longtext"; if (optionGroupId !== undefined) { if (htmlType === "CheckBox" || htmlType === "Multi-Select") return "multiselect"; if (htmlType === "Select" || htmlType === "Radio" || htmlType === "Autocomplete-Select") { return "select"; } } if (dataType === "Int" || dataType === "Float") return "number"; return "text"; }