UNPKG

@dbp-topics/formalize

Version:

[GitLab Repository](https://gitlab.tugraz.at/dbp/formalize/formalize) | [npmjs package](https://www.npmjs.com/package/@dbp-topics/formalize) | [Unpkg CDN](https://unpkg.com/browse/@dbp-topics/formalize/) | [Formalize Bundle](https://gitlab.tugraz.at/dbp/f

1,485 lines (1,300 loc) 89.3 kB
import {createInstance} from './i18n.js'; import {css, unsafeCSS, html} from 'lit'; import {ScopedElementsMixin} from '@open-wc/scoped-elements'; import DBPLitElement from '@dbp-toolkit/common/dbp-lit-element'; import {Icon, MiniSpinner, LoadingButton, getIconSVGURL} from '@dbp-toolkit/common'; import * as commonUtils from '@dbp-toolkit/common/utils'; import * as commonStyles from '@dbp-toolkit/common/styles'; import {classMap} from 'lit/directives/class-map.js'; import {Activity} from './activity.js'; import {TabulatorFull as Tabulator} from 'tabulator-tables'; import MicroModal from './micromodal.es'; import {name as pkgName} from './../package.json'; import * as fileHandlingStyles from './styles'; import * as tabulatorStyles from './tabulator-table-styles'; import metadata from './dbp-formalize-show-registrations.metadata.json'; import xss from 'xss'; import {send} from '@dbp-toolkit/common/notification'; import {getStackTrace} from '@dbp-toolkit/common/error'; /** * Imports xlsx plugin * * @returns {object} xlsx */ async function importXLSX() { return await import('xlsx'); } /** * Imports jsPDF and include jspdf-autotable plugin * * @returns {object} jspdf */ async function importJsPDF() { let jspdf = await import('jspdf'); let autotable = await import('jspdf-autotable'); autotable.applyPlugin(jspdf.jsPDF); return jspdf; } class ShowRegistrations extends ScopedElementsMixin(DBPLitElement) { constructor() { super(); this._i18n = createInstance(); this.lang = this._i18n.language; this.auth = {}; this.entryPointUrl = ''; this.activity = new Activity(metadata); this.coursesTable = null; this.submissionsTable = null; this.showSubmissionsTable = false; this.submissionsColumns = []; this.submissionsColumnsUpdated = false; this.initateOpenAdditionalMenu = false; this.initateOpenAdditionalSearchMenu = false; this.boundCloseAdditionalMenuHandler = this.hideAdditionalMenu.bind(this); this.boundCloseAdditionalSearchMenuHandler = this.hideAdditionalSearchMenu.bind(this); this.boundPressEnterAndSubmitSearchHandler = this.pressEnterAndSubmitSearch.bind(this); this.activeCourse = ''; this.currentRow = null; this.currentBeautyId = 0; this.totalNumberOfItems = 0; this.isPrevEnabled = false; this.isNextEnabled = false; this.storeSession = true; this.loadingCourseTable = false; this.loadingSubmissionTable = false; this.dataLoaded = false; this.modalContentHeight = 0; this.loadCourses = true; this.hasPermissions = true; this.hiddenColumns = false; } static get scopedElements() { return { 'dbp-icon': Icon, 'dbp-mini-spinner': MiniSpinner, 'dbp-loading-button': LoadingButton }; } static get properties() { return { ...super.properties, lang: {type: String}, auth: {type: Object}, entryPointUrl: {type: String, attribute: 'entry-point-url'}, coursesTable: {type: Object, attribute: false}, submissionsTable: {type: Object, attribute: false}, showSubmissionsTable: {type: Boolean, attribute: false}, submissionsColumns: {type: Array, attribute: false}, submissionsColumnsUpdated: {type: Boolean, attribute: false}, isPrevEnabled: {type: Boolean, attribute: false}, isNextEnabled: {type: Boolean, attribute: false}, currentBeautyId: {type: Number, attribute: false}, loadingCourseTable: {type: Boolean, attribute: false}, loadingSubmissionTable: {type: Boolean, attribute: false}, modalContentHeight: {type: Number, attribute: false}, loadCourses: {type: Boolean, attribute: false}, hasPermissions: {type: Boolean, attribute: false}, hiddenColumns: {type: Boolean, attribute: false} }; } disconnectedCallback() { super.disconnectedCallback(); this.submissionsTable.off('dataProcesseds'); this.submissionsTable.off('pageLoaded'); document.removeEventListener('keyup', this.boundPressEnterAndSubmitSearchHandler); } /** * Converts a timestamp to a readable date * * @param value * @returns {string} xlsx year-month-date hours:minutes */ humanReadableDate(value) { const d = Date.parse(value); const timestamp = new Date(d); const year = timestamp.getFullYear(); const month = ('0' + (timestamp.getMonth() + 1)).slice(-2); const date = ('0' + timestamp.getDate()).slice(-2); const hours = ('0' + timestamp.getHours()).slice(-2); const minutes = ('0' + timestamp.getMinutes()).slice(-2); return year + '-' + month + '-' + date + ' ' + hours + ':' + minutes; } connectedCallback() { super.connectedCallback(); const i18n = this._i18n; this._loginStatus = ''; this._loginState = []; this.updateComplete.then(() => { const that = this; // see: http://tabulator.info/docs/5.1 this.coursesTable = new Tabulator(this._('#courses-table'), { layout: 'fitColumns', selectable: false, placeholder: i18n.t('show-registrations.no-data'), pagination: true, paginationMode: 'local', paginationSize: 10, paginationSizeSelector: true, locale: true, columnDefaults: { vertAlign: 'middle', resizable: false }, columns: [ { title: 'ID', field: 'id', widthGrow: 1, maxWidth: 50, }, { title: 'Name', field: 'name', widthGrow: 2, }, { title: i18n.t('show-registrations.date'), field: 'date', widthGrow: 2, formatter: function(cell, formatterParams, onRendered) { return that.humanReadableDate(cell.getValue()); }, visible: false, }, { title: '', maxWidth: 45, field: 'actionButton', formatter: 'html', headerSort: false, } ], langs: { 'en': { 'pagination': { 'page_size': 'Page size', 'page_size_title': 'Page size', 'first': '<span class="mobile-hidden">First</span>', 'first_title': 'First Page', 'last': '<span class="mobile-hidden">Last</span>', 'last_title': 'Last Page', 'prev': '<span class="mobile-hidden">Prev</span>', 'prev_title': 'Prev Page', 'next': '<span class="mobile-hidden">Next</span>', 'next_title': 'Next Page' } }, 'de': { 'pagination': { 'page_size': 'Einträge pro Seite', 'page_size_title': 'Einträge pro Seite', 'first': '<span class="mobile-hidden">Erste</span>', 'first_title': 'Erste Seite', 'last': '<span class="mobile-hidden">Letzte</span>', 'last_title': 'Letzte Seite', 'prev': '<span class="mobile-hidden">Vorherige</span>', 'prev_title': 'Vorherige Seite', 'next': '<span class="mobile-hidden">Nächste</span>', 'next_title': 'Nächste Seite' } } } }); const actionsButtons = (cell, formatterParams) => { let id = cell.getData()['id']; let btn = this.createScopedElement('dbp-icon'); btn.setAttribute('name', 'keyword-research'); btn.setAttribute('id', id); btn.classList.add('open-modal-icon'); btn.addEventListener('click', event => { this.requestDetailedSubmission(cell.getRow(), cell.getRow().getData()); event.stopPropagation(); }); let div = this.createScopedElement('div'); div.appendChild(btn); div.classList.add('actions-buttons'); return div; }; let customAccessor = (value, data, type, params, column, row) => { return this.humanReadableDate(value); }; let paginationElement = this._('.tabulator-paginator'); this.submissionsTable = new Tabulator(this._('#submissions-table'), { layout: 'fitDataFill', selectable: true, selectablePersistence: false, placeholder: i18n.t('show-registrations.no-data'), columnDefaults: { vertAlign: 'middle', resizable: false }, pagination: true, paginationMode: 'local', paginationSize: 10, paginationSizeSelector: true, paginationElement: paginationElement, autoColumns: true, downloadRowRange: 'selected', locale: true, langs: { 'en': { 'pagination': { 'page_size': 'Page size', 'page_size_title': 'Page size', 'first': '<span class="mobile-hidden">First</span>', 'first_title': 'First Page', 'last': '<span class="mobile-hidden">Last</span>', 'last_title': 'Last Page', 'prev': '<span class="mobile-hidden">Prev</span>', 'prev_title': 'Prev Page', 'next': '<span class="mobile-hidden">Next</span>', 'next_title': 'Next Page' } }, 'de': { 'pagination': { 'page_size': 'Einträge pro Seite', 'page_size_title': 'Einträge pro Seite', 'first': '<span class="mobile-hidden">Erste</span>', 'first_title': 'Erste Seite', 'last': '<span class="mobile-hidden">Letzte</span>', 'last_title': 'Letzte Seite', 'prev': '<span class="mobile-hidden">Vorherige</span>', 'prev_title': 'Vorherige Seite', 'next': '<span class="mobile-hidden">Nächste</span>', 'next_title': 'Nächste Seite' } } }, autoColumnsDefinitions: [ { title: '', hozAlign: 'center', field: 'no_display_1', download: false, headerSort: false, visible: true, formatter: actionsButtons, frozen: true }, { minWidth: 150, field: 'dateCreated', title: i18n.t('show-registrations.creation-date'), hozAlign: 'left', sorter: (a, b, aRow, bRow, column, dir, sorterParams) => { const a_timestamp = Date.parse(a); const b_timestamp = Date.parse(b); return a_timestamp - b_timestamp; }, formatter: (cell, formatterParams, onRendered) => { return this.humanReadableDate(cell.getValue()); }, accessorParams: {}, accessor: customAccessor }, { field: 'id', title: 'ID', download: false, visible: false }, { field: 'id_', title: 'ID', hozAlign: 'center', visible: false, download: false } ] }); this.submissionsTable.on('dataProcessed', this.dataProcessedSubmissionTableFunction.bind(this)); this.submissionsTable.on("pageLoaded", function(pageno){ if (that._('#searchbar')) { setTimeout(function () { that._('#searchbar').scrollIntoView({behavior: 'smooth', block: 'start'}); }, 0); } }); document.addEventListener('keyup', this.boundPressEnterAndSubmitSearchHandler); }); } /** * An event function, * if we cant load table settings, then update the header list * */ dataProcessedSubmissionTableFunction() { if (this.submissionsTable !== null) { if (!this.getSubmissionTableSettings()) { this.updateTableHeaderList(); } } console.log("DATALOADED\n"); } update(changedProperties) { changedProperties.forEach((oldValue, propName) => { switch (propName) { case 'lang': this._i18n.changeLanguage(this.lang); if (this.coursesTable) { this.coursesTable.setLocale(this.lang); } if (this.submissionsTable) { this.submissionsTable.setLocale(this.lang); } break; case 'auth': this._updateAuth(); break; } }); super.update(changedProperties); } /** * Sends an analytics error event * * @param category * @param action * @param information * @param responseData */ async sendErrorAnalyticsEvent(category, action, information, responseData = {}) { let responseBody = {}; // Use a clone of responseData to prevent "Failed to execute 'json' on 'Response': body stream already read" // after this function, but still a TypeError will occur if .json() was already called before this function try { responseBody = await responseData.clone().json(); } catch (e) { responseBody = responseData; // got already decoded data } const data = { status: responseData.status || '', url: responseData.url || '', description: responseBody['hydra:description'] || '', errorDetails: responseBody['relay:errorDetails'] || '', information: information, // get 5 items from the stack trace stack: getStackTrace().slice(1, 6) }; this.sendSetPropertyEvent('analytics-event', { category: category, action: action, name: JSON.stringify(data) }); } /** * Request a re-render every time isLoggedIn()/isLoading() changes */ _updateAuth() { this._loginStatus = this.auth['login-status']; let newLoginState = [this.isLoggedIn(), this.isLoading()]; if (this._loginState.toString() !== newLoginState.toString()) { this.requestUpdate(); } this._loginState = newLoginState; } /** * Returns if a person is set in or not * * @returns {boolean} true or false */ isLoggedIn() { return (this.auth.person !== undefined && this.auth.person !== null); } /** * Returns true if a person has successfully logged in * * @returns {boolean} true or false */ isLoading() { if (this._loginStatus === 'logged-out') return false; return (!this.isLoggedIn() && this.auth.token !== undefined); } /** * Send a fetch to given url with given options * * @param url * @param options * @returns {object} response (error or result) */ async httpGetAsync(url, options) { let response = await fetch(url, options).then(result => { if (!result.ok) throw result; return result; }).catch(error => { return error; }); return response; } throwSomethingWentWrongNotification() { const i18n = this._i18n; send({ summary: i18n.t('show-registrations.something-went-wrong-title'), body: i18n.t('show-registrations.something-went-wrong-body'), type: 'danger', timeout: 5 }); } /** * Gets the list of courses * * @returns {object} response */ async getListOfAllCourses() { const i18n = this._i18n; //TODO cache this data let dataList = []; let response = await this.getAllSubmissions(); if (!response) { this.sendErrorAnalyticsEvent('LoadListOfAllCourses', 'NoResponse', ''); this.throwSomethingWentWrongNotification(); return; } if (response.status !== 200) { if (response.status === 403) { this.hasPermissions = false; this.sendErrorAnalyticsEvent('LoadListOfAllCourses', 'NoPermission', '', response); send({ summary: i18n.t('show-registrations.load-courses-no-permission-title'), body: i18n.t('show-registrations.load-courses-no-permission-body'), type: 'danger', timeout: 5 }); return; } this.sendErrorAnalyticsEvent('LoadListOfAllCourses', 'SomeWentWrong', '', response); this.throwSomethingWentWrongNotification(); return; } let data = []; try { data = await response.json(); } catch (e) { this.sendErrorAnalyticsEvent('LoadListOfAllCourses', 'WrongResponse', e); this.throwSomethingWentWrongNotification(); return; } if (!data || !data['hydra:member']) { this.sendErrorAnalyticsEvent('LoadListOfAllCourses', 'WrongData', ''); this.throwSomethingWentWrongNotification(); return; } let id = 1; let courses = []; for (let x = 0; x <= data["hydra:member"].length; x++) { if (x === data['hydra:member'].length) { this.coursesTable.setData(dataList); this.coursesTable.setLocale(this.lang); this.dataLoaded = true; return; } let entry = data['hydra:member'][x]; try { let name = entry['form']; // Load form only one time if (!name || courses.length > 0 && courses.includes(name)) { continue; } let date = entry['dateCreated']; // create 'show form' button let icon = this.createScopedElement('dbp-icon'); icon.setAttribute('name', 'chevron-right'); icon.setAttribute('title', i18n.t('show-registrations.open-forms')); let btn = this.createScopedElement('dbp-button'); btn.classList.add('button', 'courses-btn'); btn.addEventListener('click', async event => { this.loadingSubmissionTable = true; await this.requestAllCourseSubmissions(name); this.loadingSubmissionTable = false; event.stopPropagation(); }); btn.appendChild(icon); let div = this.createScopedElement('div'); div.classList.add('button-wrapper'); div.appendChild(btn); let course = {id: id, name: name, date: date, actionButton: div}; id++; courses.push(name); dataList.push(course); } catch (e) { this.sendErrorAnalyticsEvent('LoadListOfAllCourses', 'ErrorInDataCreation', e); } } } /** * Gets the list of submissions * * @returns {object} response */ async getAllSubmissions() { let response; const options = { method: 'GET', headers: { 'Content-Type': 'application/ld+json', Authorization: 'Bearer ' + this.auth.token } }; response = await this.httpGetAsync(this.entryPointUrl + '/formalize/submissions', options); return response; } /** * Gets a submission for a given identifier * * @param identifier * @returns {object} response */ async getSubmissionForId(identifier) { const options = { method: 'GET', headers: { 'Content-Type': 'application/ld+json', Authorization: 'Bearer ' + this.auth.token } }; return await this.httpGetAsync(this.entryPointUrl + '/formalize/submissions/' + identifier, options); } /** * Initiate getListOfAllCourses and set Loading */ async requestCourses() { if (!this.dataLoaded) { this.loadingCourseTable = true; await this.getListOfAllCourses(); this.loadingCourseTable = false; } } /** * Gets the list of submissions for a specific course * * @param {string} name */ async requestAllCourseSubmissions(name) { const i18n = this._i18n; let dataList2 = []; let response = await this.getAllSubmissions(); this.submissionsColumns = []; if (!response) { this.sendErrorAnalyticsEvent('requestAllCourseSubmissions', 'NoResponse', ''); this.throwSomethingWentWrongNotification(); return; } if (response.status !== 200) { if (response.status === 403) { this.hasPermissions = false; this.sendErrorAnalyticsEvent('requestAllCourseSubmissions', 'NoPermission', '', response); send({ summary: i18n.t('show-registrations.load-courses-no-permission-title'), body: i18n.t('show-registrations.load-courses-no-permission-body'), type: 'danger', timeout: 5 }); return; } this.sendErrorAnalyticsEvent('requestAllCourseSubmissions', 'NoResponse', '', response); this.throwSomethingWentWrongNotification(); return; } let data = []; try { data = await response.json(); } catch (e) { this.sendErrorAnalyticsEvent('requestAllCourseSubmissions', 'WrongResponse', e); this.throwSomethingWentWrongNotification(); return; } if (!data || !data['hydra:member']) { this.showSubmissionsTable = true; this.sendErrorAnalyticsEvent('requestAllCourseSubmissions', 'WrongData', ''); this.throwSomethingWentWrongNotification(); return; } let itemsCount = 0; for (let x = 0; x <= data["hydra:member"].length; x++) { if (x === data['hydra:member'].length) { this.activeCourse = name; this.submissionsTable.setData(dataList2); this.submissionsTable.setLocale(this.lang); this.updateSubmissionTable(); this.showSubmissionsTable = true; const that = this; setTimeout(function() { if (that._('.subheadline')) { // that._('.subheadline').scrollIntoView({behavior: 'smooth', block: 'start'}); } }, 10); return; } let entry = data['hydra:member'][x]; let id = entry['@id'].split('/')[3]; let date = entry['dateCreated']; try { if (entry && entry['form'] !== name) continue; let json = JSON.parse(entry['dataFeedElement']); let jsonFirst = {}; jsonFirst['id'] = id; jsonFirst['no_display_1'] = ''; jsonFirst['id_'] = itemsCount + 1; jsonFirst['dateCreated'] = date; json = Object.assign(jsonFirst, json); dataList2.push(json); itemsCount++; } catch (e) { this.sendErrorAnalyticsEvent('LoadListOfAllCourses', 'ErrorInDataCreation', e); } this.totalNumberOfItems = itemsCount; } } /** * Gets the detaildata of a specific row * * @param row * @param data */ requestDetailedSubmission(row, data) { if (!this._('.detailed-submission-modal-content-wrapper')) return; this._('.detailed-submission-modal-content-wrapper').innerHTML = ''; if (!this._('#apply-col-settings')) return; let colSettings = this._('#apply-col-settings').checked; let identifier = data['id_']; if (!colSettings) { let cells = data; for (let i = 0; i < Object.keys(cells).length; i++) { let key = Object.keys(cells)[i]; if (key.includes('no_display') || key.includes('id')) { continue; } else if (key.includes('dateCreated') && (cells[key] !== '')) { let title = this.submissionsTable.getColumn('dateCreated').getDefinition().title; title = title === '' ? key : title; this._('.detailed-submission-modal-content-wrapper').innerHTML += `<div class='element-left'>` + title + `:</div>`; this._('.detailed-submission-modal-content-wrapper').innerHTML += `<div class='element-right'>` + this.humanReadableDate(cells[key]); +`</div>`; continue; } this._('.detailed-submission-modal-content-wrapper').innerHTML += `<div class='element-left'>` + xss(key) + `:</div>`; if (cells[key] !== '') { this._('.detailed-submission-modal-content-wrapper').innerHTML += `<div class='element-right'>` + xss(cells[key]) + `</div>`; } else { this._('.detailed-submission-modal-content-wrapper').innerHTML += `<div class='element-right'></div>`; } } } else { // If checkbox checked let cells = data; for (let i = 0; i < Object.keys(cells).length; i++) { let key = Object.keys(cells)[i]; let isVisible = true; if (this.submissionsTable.getColumn(key)) { isVisible = window.getComputedStyle(this.submissionsTable.getColumn(key).getElement()).display === 'none' ? false : true; } if (key.includes('no_display') || key.includes('id') || !isVisible) { continue; } else if (key.includes('dateCreated') && (cells[key] !== '')) { let title = this.submissionsTable.getColumn('dateCreated').getDefinition().title; title = title === '' ? key : title; this._('.detailed-submission-modal-content-wrapper').innerHTML += `<div class='element-left'>` + title + `:</div>`; this._('.detailed-submission-modal-content-wrapper').innerHTML += `<div class='element-right'>` + this.humanReadableDate(cells[key]); +`</div>`; continue; } this._('.detailed-submission-modal-content-wrapper').innerHTML += `<div class='element-left'>` + xss(key) + `:</div>`; if (cells[key] !== '') { this._('.detailed-submission-modal-content-wrapper').innerHTML += `<div class='element-right'>` + xss(cells[key]) + `</div>`; } else { this._('.detailed-submission-modal-content-wrapper').innerHTML += `<div class='element-right'></div>`; } } } if (this._('.detailed-submission-modal-content-wrapper > div:first-child')) this._('.detailed-submission-modal-content-wrapper > div:first-child').classList.add('first'); if (this._('.detailed-submission-modal-content-wrapper > div:nth-child(2)')) this._('.detailed-submission-modal-content-wrapper > div:nth-child(2)').classList.add('first'); this.currentRow = row; this.currentBeautyId = identifier; this.isPrevEnabled = identifier !== 1; this.isNextEnabled = (identifier + 1) <= this.submissionsTable.getDataCount(); this.showDetailedModal(); this.modalContentHeight = this._('#detailed-submission-modal-box > .modal-header').offsetHeight + this._('#detailed-submission-modal-box > .modal-footer').offsetHeight; this._('.detailed-submission-modal-content-wrapper').setAttribute('style', 'max-height: calc(100vH - ' + this.modalContentHeight + 'px);'); } /** * Export the specific table * * @param e */ async exportSubmissionTable(e) { let exportInput = this._('#export-select'); if (!exportInput) return; let exportValue = exportInput.value; if (!exportValue || exportValue === '') return; if (e) e.stopPropagation(); switch (exportValue) { case 'csv': this.exportCSV(); break; case 'pdf': this.exportPdf(); break; case 'excel': this.exportXLSX(); break; default: break; } exportInput.value = '-'; } /** * Export submissionTable data as CSV * */ async exportCSV() { console.log('export csv'); let selected = this.submissionsTable.getSelectedRows().length; let all = 'selected'; if (selected === 0) { all = 'active'; } this.submissionsTable.download('csv', this.activeCourse + '.csv', {}, all); } /** * Get color of a css var * * @param {string} cssVar * @returns {string} color */ getColorFromCssVar(cssVar) { const docStyle = getComputedStyle(this); let color = docStyle.getPropertyValue(cssVar); if (color.includes('white')) { return '#ffffff'; } if (color.includes('black')) { return '#000000'; } return color.trim(); } /** * Imports JsPdf, * Exports submissionTable data as PDF * Delets JsPdf Plugin * */ async exportPdf() { let selected = this.submissionsTable.getSelectedRows().length; let all = 'selected'; if (selected === 0) { all = 'active'; } let headerBackground = this.getColorFromCssVar('--dbp-primary-surface'); let headerContent = this.getColorFromCssVar('--dbp-on-primary-surface'); if (!headerBackground || !headerContent) { headerBackground = '#000000'; headerContent = '#ffffff'; } window.jspdf = await importJsPDF(); this.submissionsTable.download('pdf', this.activeCourse + '.pdf', { title: this.activeCourse, autoTable: { //advanced table styling theme: 'grid', styles: { fontSize: 8 }, headStyles: { Color: headerContent, fillColor: headerBackground }, margin: {top: 60}, pageBreak: 'auto' } }, all); delete window.jspdf; } /** * import xlsx plgin * Export submissionTable data as Excel * delete xlsx plugin * */ async exportXLSX() { console.log('export xlsx'); window.XLSX = await importXLSX(); let selected = this.submissionsTable.getSelectedRows().length; let all = 'selected'; if (selected === 0) { all = 'active'; } this.submissionsTable.download('xlsx', this.activeCourse + '.xlsx', {sheetName: this.activeCourse}, all); delete window.XLSX; } /** * Function for filtering table * */ filterTable() { let filter = this._('#searchbar'); let search = this._('#search-select'); let operator = this._('#search-operator'); if (!filter || !search || !operator || !this.submissionsTable) return; filter = filter.value; search = search.value; operator = operator.value; if (search !== 'all') { this.submissionsTable.setFilter(search, operator, filter); return; } let filterArray = []; this.submissionsColumns.forEach(col => { filterArray.push({field: col.field, type: operator, value: filter}); }); this.submissionsTable.setFilter([filterArray]); } /* * Clear Filer */ clearFilter() { let filter = this._('#searchbar'); let search = this._('#search-select'); if (!filter || !search || !this.submissionsTable) return; filter.value = ''; search.value = 'all'; this.submissionsTable.clearFilter(); } /** * Updates the this.submissionColumns Array based on the actual columns of the this.submissionTable * */ updateTableHeaderList() { if (!this.submissionsTable) return; let columns = this.submissionsTable.getColumns(); this.submissionsColumns = []; columns.forEach((col) => { let name = col.getDefinition().title; let field = col.getDefinition().field; let visibility = col.isVisible(); if (field && !field.includes('no_display') && field !== 'id' && field !== 'id_') { this.submissionsColumns.push({name: name, field: field, visibility: visibility}); } }); } /** * Creates options for a select box of the t * his.submissionColumns Array (all possible cols of active table) * * @returns {Array<html>} options */ getTableHeaderOptions() { if (!this.submissionsTable) return; const i18n = this._i18n; let options = []; options[0] = html` <option value='all'>${i18n.t('show-registrations.all-columns')}</option>`; this.submissionsColumns.forEach((col, counter) => { if (!col.visibility) { options[counter + 1] = html` <option disabled value='${col.field}'>${col.name}</option>`; } else { options[counter + 1] = html` <option value='${col.field}'>${col.name}</option>`; } }); return options; } /** * Opens submission Columns Modal * */ openModal() { let modal = this._('#submission-modal'); if (modal) { MicroModal.show(modal, { disableScroll: true, disableFocus: false, }); } // Scroll list to topdisableScroll: true let scrollWrapper = this._('#submission-modal-content'); if (scrollWrapper) { scrollWrapper.scrollTo(0, 0); } } /** * Close submission Columns Modal * */ closeModal() { let modal = this._('#submission-modal'); if (modal) { MicroModal.close(modal); } } /** * Opens submission detail Modal * */ showDetailedModal() { let modal = this._('#detailed-submission-modal'); if (modal) { MicroModal.show(modal, { disableScroll: true, disableFocus: false, }); } } /** * Toggle additional functionalities menu on mobile * */ toggleMoreMenu() { const menu = this.shadowRoot.querySelector('ul.extended-menu'); const menuStart = this.shadowRoot.querySelector('a.extended-menu-link'); if (menu === null || menuStart === null) { return; } menu.classList.toggle('hidden'); if (!menu.classList.contains('hidden')) { // add event listener for clicking outside of menu document.addEventListener('click', this.boundCloseAdditionalMenuHandler); this.initateOpenAdditionalMenu = true; } else { document.removeEventListener('click', this.boundCloseAdditionalMenuHandler); } } /** * Keydown Event function if enter pressed, then start filtering the table * * @param event */ pressEnterAndSubmitSearch(event) { if (event.keyCode === 13) { const activeElement = this.shadowRoot.activeElement; if (activeElement && activeElement.id === 'searchbar') { event.preventDefault(); this.filterTable(); this.hideAdditionalSearchMenu(event); } } } /** * Hide additional functionalities menu * This function is used as bounded event function, * if clicked outside then we can close the menu * */ hideAdditionalMenu() { if (this.initateOpenAdditionalMenu) { this.initateOpenAdditionalMenu = false; return; } const menu = this.shadowRoot.querySelector('ul.extended-menu'); if (menu && !menu.classList.contains('hidden')) this.toggleMoreMenu(); } /** * Toggle search menu * */ toggleSearchMenu() { const menu = this._('#extendable-searchbar .extended-menu'); if (menu === null) { return; } menu.classList.remove('hidden'); if (!menu.classList.contains('hidden')) { // add event listener for clicking outside of menu document.addEventListener('click', this.boundCloseAdditionalSearchMenuHandler); this.initateOpenAdditionalSearchMenu = true; } } /** * hide search menu * * @param e */ hideAdditionalSearchMenu(e) { if (this.initateOpenAdditionalSearchMenu) { this.initateOpenAdditionalSearchMenu = false; return; } if (e.type !== 'keyup' && e.keyCode !== 13 && (e.originalTarget && e.originalTarget.parentElement && (e.originalTarget.parentElement.classList.contains('extended-menu') || e.originalTarget.parentElement.id === 'search-operator' || e.originalTarget.parentElement.id === 'search-operator' || e.originalTarget.parentElement.id === 'search-select') || e.originalTarget && e.originalTarget.id === 'searchbar-menu' || e.originalTarget && e.originalTarget.id === 'searchbar')) { return; } const menu = this._('#extendable-searchbar .extended-menu'); if (menu && !menu.classList.contains('hidden')) { menu.classList.add('hidden'); document.removeEventListener('click', this.boundCloseAdditionalSearchMenuHandler); } } /** * Toggle visibility of an item * * @param {object} item */ changeVisibility(item) { item.visibility = !item.visibility; if (item.visibility) { this._('.' + item.field + ' .header-visibility-icon-hide').classList.remove('hidden'); this._('.' + item.field + ' .header-visibility-icon-show').classList.add('hidden'); } else { this._('.' + item.field + ' .header-visibility-icon-hide').classList.add('hidden'); this._('.' + item.field + ' .header-visibility-icon-show').classList.remove('hidden'); } } /** * Update Submission Table (order and visibility) * */ updateSubmissionTable() { // Add all known colums in the right order let newDefs = []; let addedFields = []; for (let spec of this.submissionsColumns) { let col = this.submissionsTable.getColumn(spec.field); if (!col) { continue; } addedFields.push(spec.field); newDefs.push(col.getDefinition()); } // Append everything we didn't know about for (let col of this.submissionsTable.getColumns()) { let def = col.getDefinition(); if (addedFields.indexOf(def.field) === -1) { newDefs.push(def); addedFields.push(def.field); } } // Replace all columns this.submissionsTable.setColumns(newDefs); // Set the visibility status this.hiddenColumns = false; for (let spec of this.submissionsColumns) { let col = this.submissionsTable.getColumn(spec.field); if (!col) { continue; } if (spec.visibility) { col.show(); } else { col.hide(); this.hiddenColumns = true; } } } /** * Gets stored submission table settings from localStorage * * @returns {boolean} success */ getSubmissionTableSettings() { if ( this.storeSession && this.isLoggedIn() ) { let optionsString = localStorage.getItem('dbp-formalize-tableoptions-' + this.activeCourse + '-' + this.auth['person-id']); if (!optionsString) { this.submissionsColumns = []; return false; } try { let options = JSON.parse(optionsString); if (options) { this.submissionsColumns = [...options]; } } catch (e) { this.submissionsColumns = []; console.log(e); return false; } return true; } return false; } /** * Stores submission Table settings in localStorage * */ setSubmissionTableSettings() { if ( this.storeSession && this.isLoggedIn() ) { const publicId = this.auth['person-id']; localStorage.setItem('dbp-formalize-tableoptions-' + this.activeCourse + '-' + publicId, JSON.stringify(this.submissionsColumns)); } } /** * Moves a header in this.submissionColumns Array and in DOM up * * @param {object} i */ moveHeaderUp(i) { let elem = this._('.' + i.field); let elemIndex = elem.getAttribute('data-index'); if (parseInt(elemIndex) === 0) return; let swapElem = this.submissionsColumns.find((col, index) => { return index + 1 <= this.submissionsColumns.length && this.submissionsColumns[index + 1].field === i.field; }); this.swapHeader(swapElem, elemIndex, i); } /** * Moves a header in this.submissionColumns Array and in DOM up * * @param {object} i */ moveHeaderDown(i) { let elem = this._('.' + i.field); let elemIndex = elem.getAttribute('data-index'); if (parseInt(elemIndex) === this.submissionsColumns.length - 1) return; let swapElem = this.submissionsColumns.find((col, index) => { return index - 1 >= 0 && this.submissionsColumns[index - 1].field === i.field; }); this.swapHeader(swapElem, elemIndex, i); } /** * Swaps two elements in this.submissionColumns Array and in DOM * * @param {object} swapElem_ * @param {number} elemIndex * @param {object} i */ swapHeader(swapElem_, elemIndex, i) { let swapElem = this._('.' + swapElem_.field); let swapElemIndex = swapElem.getAttribute('data-index'); let tmp = this.submissionsColumns[elemIndex]; this.submissionsColumns[elemIndex] = this.submissionsColumns[swapElemIndex]; this.submissionsColumns[swapElemIndex] = tmp; this.submissionsColumnsUpdated = !this.submissionsColumnsUpdated; let swapElem2 = this._('.' + swapElem_.field); function removeClass() { swapElem2.classList.remove('move-up'); } function addClass() { swapElem2.classList.add('move-up'); } setTimeout(addClass.bind(swapElem2), 0); setTimeout(removeClass.bind(swapElem2), 400); } /** * Shows last entry of this.submissionTable * */ showLastEntry() { if (this.currentRow !== null) { let currentRow = this.currentRow; let nextIndex = currentRow.getPosition() - 1; let nextRow; this.submissionsTable.getRows().forEach((row) => { if (row.getPosition() === nextIndex) { nextRow = row; } }); if (nextRow) { this.requestDetailedSubmission(nextRow, nextRow.getData()); } } } /** * Shows next entry of this.submissionTable * */ showNextEntry() { if (this.currentRow !== null) { let currentRow = this.currentRow; let nextIndex = currentRow.getPosition() + 1; let nextRow; this.submissionsTable.getRows().forEach((row) => { if (row.getPosition() === nextIndex) { nextRow = row; console.log('next row:', nextRow); } }); if (nextRow) { this.requestDetailedSubmission(nextRow, nextRow.getData()); } } } static get styles() { // language=css return css` ${commonStyles.getThemeCSS()} ${commonStyles.getModalDialogCSS()} ${commonStyles.getRadioAndCheckboxCss()} ${commonStyles.getGeneralCSS(false)} ${fileHandlingStyles.getFileHandlingCss()} ${commonStyles.getNotificationCSS()} ${commonStyles.getActivityCSS()} ${commonStyles.getButtonCSS()} ${tabulatorStyles.getTabulatorStyles()} .table-wrapper.submissions { padding-top: 0.5rem; } .table-header.submissions { margin-top: 0.5rem; } .btn-row-left { display: flex; justify-content: space-between; gap: 4px; } .btn-row-left > * { display: flex; align-items: center; } .next-btn dbp-icon, .back-btn dbp-icon { height: 15px; top: 0px; } .next-btn dbp-icon { margin-left: 0.2em; margin-right: -0.4em; } .back-btn dbp-icon { margin-left: -0.4em; margin-right: 0.2em; } .actions-buttons { width: 33px; position: absolute; margin: auto; left: 10px; } #detailed-submission-modal-title { margin-bottom: 10px; } #submission-modal-title { margin-top: unset; } #detailed-submission-modal-content { padding: 0 20px 0px 20px; } #detailed-submission-modal-box { height: auto; width: auto; overflow-y: hidden; min-height: 0; max-width: 768px; min-width: 768px; } .open-modal-icon { font-size: 1.3em;