UNPKG

@digital-blueprint/formalize-app

Version:

[GitHub Repository](https://github.com/digital-blueprint/formalize-app) | [npmjs package](https://www.npmjs.com/package/@digital-blueprint/formalize-app) | [Unpkg CDN](https://unpkg.com/browse/@digital-blueprint/formalize-app/) | [Formalize Bundle](https:

433 lines (393 loc) • 14.6 kB
// @ts-nocheck /** * Table configuration helpers for the Manage Forms activity. * * Contains everything related to tabulator table column setup, * visibility, ordering, and submission form options. */ import {dateToTimestamp} from './manage-forms-api.js'; // --------------------------------------------------------------------------- // Submission form options // --------------------------------------------------------------------------- /** * Build the `options_submissions[state]` object used to initialise the * tabulator table for a given submission state. * * @param {object} host - The ManageForms element. * @param {string} state - 'draft' or 'submitted' */ export function setSubmissionFormOptions(host, state) { const noSubmissionDataPlaceholder = host._i18n ? host._i18n.t('manage-forms.no-submission-data-available') : 'No submission data available'; let lang_submissions = { en: {columns: {}}, de: {columns: {}}, }; const options_submissions = { langs: lang_submissions, autoColumns: 'full', rowHeight: 64, layout: 'fitDataStretch', // layoutColumnsOnNewData: true, selectableRows: 'highlight', rowHeader: { formatter: 'rowSelection', titleFormatter: 'rowSelection', titleFormatterParams: { rowRange: 'visible', }, headerSort: false, resizable: false, frozen: true, headerHozAlign: 'center', hozAlign: 'center', }, columnDefaults: { vertAlign: 'middle', hozAlign: 'left', resizable: false, }, initialSort: [{column: 'dateCreated', dir: 'desc'}], placeholder: noSubmissionDataPlaceholder, }; options_submissions.autoColumnsDefinitions = (definitions) => { definitions.forEach((columnDefinition) => { if (columnDefinition.field === 'submissionId') { columnDefinition.visible = false; columnDefinition.title = host._i18n.t('manage-forms.submission-id'); } if (columnDefinition.field === 'dateCreated') { columnDefinition.visible = true; columnDefinition.title = host.lang === 'de' ? 'Erstellt am' : 'Date created'; } if (columnDefinition.field === 'htmlButtons') { columnDefinition.title = ''; columnDefinition.formatter = 'html'; columnDefinition.hozAlign = 'right'; columnDefinition.vertAlign = 'middle'; columnDefinition.headerSort = false; columnDefinition.minWidth = 64; columnDefinition.frozen = true; columnDefinition.headerHozAlign = 'right'; columnDefinition.download = false; } else { columnDefinition.sorter = 'string'; } if (columnDefinition.field.includes('date')) { columnDefinition.sorter = (a, b) => { return dateToTimestamp(a) - dateToTimestamp(b); }; } }); return [ { title: 'ID', field: 'rowIndex', formatter: function (cell) { const row = cell.getRow(); const table = row.getTable(); const page = table.getPage(); const pageSize = table.getPageSize(); const position = row.getPosition(true); return (page - 1) * pageSize + position; }, hozAlign: 'center', headerHozAlign: 'center', headerSort: false, frozen: true, width: 30, download: false, }, ...definitions, ]; }; host.options_submissions[state] = {...options_submissions}; } // --------------------------------------------------------------------------- // Checkbox / pagination helpers // --------------------------------------------------------------------------- /** * @param {object} host * @param {string} state */ export function enableCheckboxSelection(host, state) { host.options_submissions[state].rowHeader = { formatter: 'rowSelection', titleFormatter: 'rowSelection', titleFormatterParams: {rowRange: 'visible'}, headerSort: false, resizable: false, frozen: true, headerHozAlign: 'center', hozAlign: 'center', }; host.options_submissions[state].headerVisible = true; } /** * @param {object} host * @param {string} state */ export function disableCheckboxSelection(host, state) { host.options_submissions[state].rowHeader = false; host.options_submissions[state].headerVisible = false; } /** * @param {object} host * @param {string} state */ export function disablePagination(host, state) { host.submissionTables[state].paginationEnabled = false; } /** * @param {object} host * @param {string} state */ export function enablePagination(host, state) { if (host.submissionTables[state].paginationEnabled === false) { host.submissionTables[state].paginationEnabled = true; } } // --------------------------------------------------------------------------- // Column definitions helpers (schema-driven) // --------------------------------------------------------------------------- /** * Get the parsed form schema from the active form. * @param {object} activeForm * @returns {object|null} */ function getFormSchema(activeForm) { if (!activeForm.dataFeedSchema) return null; try { const schema = JSON.parse(activeForm.dataFeedSchema); const fallbackSchemaValue = activeForm.moduleInstance?.getDataFeedSchema?.(); if (!fallbackSchemaValue) { return schema; } const fallbackSchema = typeof fallbackSchemaValue === 'string' ? JSON.parse(fallbackSchemaValue) : fallbackSchemaValue; for (const [field, definition] of Object.entries(schema.properties ?? {})) { definition.localizedName ??= fallbackSchema.properties?.[field]?.localizedName ?? fallbackSchema.files?.[field]?.localizedName; } for (const [field, definition] of Object.entries(schema.files ?? {})) { definition.localizedName ??= fallbackSchema.files?.[field]?.localizedName; } return schema; } catch (e) { console.log('Failed parsing json data', e); return null; } } /** * Get the initial column definitions from the submissions table. * @param {object} submissionsTable * @returns {Array} */ function getInitialColumnDefinitions(submissionsTable) { const columnComponents = submissionsTable.getColumns(); if (!columnComponents || columnComponents.length === 0) return []; return columnComponents.map((column) => column.getDefinition()); } /** * Check if the form schema is a catch-all (no explicit properties). * @param {object} formSchemaFields * @returns {boolean} */ function isCatchAllSchema(formSchemaFields) { return !formSchemaFields?.properties || Object.keys(formSchemaFields?.properties).length === 0; } /** * Get schema fields, handling visibility and localization. * @param {object} host * @param {object} formSchemaFields * @param {boolean} isCatchAll * @param {Array} initialColumnDefinitions * @param {string} state * @param {boolean} hasTagsColumn * @returns {Array} */ function getSchemaFields( host, formSchemaFields, isCatchAll, initialColumnDefinitions, state, hasTagsColumn, ) { const schemaFields = []; // Always include dateCreated at the beginning const dateCreatedDef = initialColumnDefinitions.find((def) => def.field === 'dateCreated'); if (dateCreatedDef) { schemaFields.push(dateCreatedDef); } // Add tags column right after dateCreated if it exists if (hasTagsColumn) { const tagsField = initialColumnDefinitions.find((def) => def.field === 'tags'); if (tagsField) { const tagsFieldClone = {...tagsField}; tagsFieldClone.frozen = false; tagsFieldClone.formatter = 'html'; if (tagsFieldClone.visible === undefined) { tagsFieldClone.visible = true; } schemaFields.push(tagsFieldClone); } } if (isCatchAll) { initialColumnDefinitions.forEach((def) => { if ( def.field && def.field !== 'rowIndex' && def.field !== 'dateCreated' && def.field !== 'tags' && def.field !== 'identifier' && def.field !== 'submissionId' && def.field !== 'htmlButtons' ) { def.visible = true; schemaFields.push(def); } }); } else { Object.keys(formSchemaFields.properties).forEach((field) => { const schemaField = formSchemaFields.properties[field]; const definition = { field: field, visible: schemaField?.tableViewVisibleDefault ?? true, title: schemaField?.localizedName?.[host.lang] || field, }; schemaFields.push(definition); }); } return schemaFields; } /** * Get attachment fields from the form schema. * @param {object} formSchemaFields * @returns {Array} */ function getAttachmentFields(host, formSchemaFields) { const attachmentFields = []; if (formSchemaFields?.files && typeof formSchemaFields.files === 'object') { Object.keys(formSchemaFields.files).forEach((attachmentType) => { attachmentFields.push({ field: `form_files-${attachmentType}`, title: formSchemaFields.files[attachmentType]?.localizedName?.[host.lang] || attachmentType, visible: true, }); }); } return attachmentFields; } /** * Get system fields that should always be present. * When the form schema explicitly defines a field (e.g. "identifier"), skip it * here so it is only rendered once – via the schema-driven column list. * @param {Array} initialColumnDefinitions * @param {object|null} formSchemaFields * @returns {Array} */ function getSystemFields(initialColumnDefinitions, formSchemaFields) { const schemaProperties = formSchemaFields?.properties || {}; return initialColumnDefinitions.filter( (def) => (def.field === 'identifier' && !Object.hasOwn(schemaProperties, 'identifier')) || def.field === 'submissionId' || def.field === 'htmlButtons', ); } /** * Set the initial visibility/order of submission table columns from the form schema. * @param {object} host * @param {string} state */ export function setDefaultSubmissionTableOrder(host, state) { const activeForm = host.forms.get(host.activeFormId); if (!activeForm) return; const formSchemaFields = getFormSchema(activeForm); // Set download folder name pattern from schema host.downloadFolderNamePattern = formSchemaFields?.submissionExport?.downloadFolderPattern || ''; host.useSubFoldersForExports = formSchemaFields?.submissionExport?.subfolders ?? true; const submissionsTable = host.submissionTables[state]; if (!submissionsTable) return; const initialColumnDefinitions = getInitialColumnDefinitions(submissionsTable); if (initialColumnDefinitions.length === 0) return; const hasTagsColumn = Array.isArray(host.availableTags) && host.availableTags.length > 0; const catchAll = isCatchAllSchema(formSchemaFields); const schemaFields = getSchemaFields( host, formSchemaFields, catchAll, initialColumnDefinitions, state, hasTagsColumn, ); const attachmentFields = getAttachmentFields(host, formSchemaFields); const systemFields = getSystemFields(initialColumnDefinitions, formSchemaFields); // Ensure rowIndex is included const rowIndexDef = initialColumnDefinitions.find((def) => def.field === 'rowIndex'); const defaultRowIndexDef = { title: 'ID', field: 'rowIndex', formatter: function (cell) { const row = cell.getRow(); const table = row.getTable(); const page = table.getPage(); const pageSize = table.getPageSize(); const position = row.getPosition(true); return (page - 1) * pageSize + position; }, hozAlign: 'center', headerHozAlign: 'center', headerSort: false, frozen: true, width: 30, download: false, }; // Separate htmlButtons to place it last const htmlButtonsDef = systemFields.find((def) => def.field === 'htmlButtons'); const otherSystemFields = systemFields.filter((def) => def.field !== 'htmlButtons'); // Build columns: rowIndex first, schema/attachments/system, htmlButtons last const schemaColumnDefinitions = [ rowIndexDef || defaultRowIndexDef, ...schemaFields, ...attachmentFields, ...otherSystemFields, ]; if (htmlButtonsDef) { schemaColumnDefinitions.push(htmlButtonsDef); } host.submissionsColumnsInitial[state] = cloneColumnDefinitions(schemaColumnDefinitions); } // --------------------------------------------------------------------------- // Column definitions cloning // --------------------------------------------------------------------------- /** * Clone an array of column definitions so mutations don't affect the source. * @param {object[]} definitions * @returns {object[]} */ export function cloneColumnDefinitions(definitions) { if (!Array.isArray(definitions)) return []; return definitions.map((definition) => cloneColumnDefinition(definition)); } /** * Clone a single column definition, including nested column groups. * @param {object} definition * @returns {object} */ function cloneColumnDefinition(definition) { if (!definition || typeof definition !== 'object') return definition; const clone = {...definition}; if (Array.isArray(definition.columns)) { clone.columns = cloneColumnDefinitions(definition.columns); } return clone; }