{"version":3,"file":"app-2472fb46.f584a1446b2fc034d810.bundle.js","mappings":"AAGA","sources":["webpack://lernplattform-admin-frontend/./src/resources/pages/administrative-staff-page/administrative-staff-page.html","webpack://lernplattform-admin-frontend/./src/resources/pages/announcements-page/announcements-page.html","webpack://lernplattform-admin-frontend/./src/resources/pages/assessment-code-details-page/assessment-code-details-page.html","webpack://lernplattform-admin-frontend/./src/resources/pages/assessment-code-print-page/assessment-code-print-page.html","webpack://lernplattform-admin-frontend/./src/resources/pages/assessment-code-protocol-print-page/assessment-code-protocol-print-page.html","webpack://lernplattform-admin-frontend/./src/resources/pages/assessment-details-page/assessment-details-page.html","webpack://lernplattform-admin-frontend/./src/resources/pages/assessments-page/assessments-page.html","webpack://lernplattform-admin-frontend/./src/resources/pages/avatar-component-set-page/avatar-component-set-page.html","webpack://lernplattform-admin-frontend/./src/resources/pages/avatar-component-sets-page/avatar-component-sets-page.html","webpack://lernplattform-admin-frontend/./src/resources/pages/billing-csv-export-page/billing-csv-export-page.html","webpack://lernplattform-admin-frontend/./src/resources/pages/block-overview-page/block-overview-page.html","webpack://lernplattform-admin-frontend/./src/resources/pages/blocklist-page/blocklist-page.html","webpack://lernplattform-admin-frontend/./src/resources/index.ts","webpack://lernplattform-admin-frontend/./src/resources/pages/administrative-staff-page/administrative-staff-page.ts","webpack://lernplattform-admin-frontend/./src/resources/pages/announcements-page/announcements-page.ts","webpack://lernplattform-admin-frontend/./src/resources/pages/assessment-code-details-page/assessment-code-details-page.ts","webpack://lernplattform-admin-frontend/./src/resources/pages/assessment-code-print-page/assessment-code-print-page.ts","webpack://lernplattform-admin-frontend/./src/resources/pages/assessment-code-protocol-print-page/assessment-code-protocol-print-page.ts","webpack://lernplattform-admin-frontend/./src/resources/pages/assessment-details-page/assessment-details-page.ts","webpack://lernplattform-admin-frontend/./src/resources/pages/assessments-page/assessments-page.ts","webpack://lernplattform-admin-frontend/./src/resources/pages/avatar-component-set-page/avatar-component-set-page.ts","webpack://lernplattform-admin-frontend/./src/resources/pages/avatar-component-set-page/index.ts","webpack://lernplattform-admin-frontend/./src/resources/pages/avatar-component-sets-page/avatar-component-sets-page.ts","webpack://lernplattform-admin-frontend/./src/resources/pages/avatar-component-sets-page/index.ts","webpack://lernplattform-admin-frontend/./src/resources/pages/billing-csv-export-page/billing-csv-export-page.ts","webpack://lernplattform-admin-frontend/./src/resources/pages/block-overview-page/block-overview-page.ts","webpack://lernplattform-admin-frontend/./src/resources/pages/blocklist-page/blocklist-page.ts"],"sourcesContent":["// Module\nvar code = \"<template> <div class=\\\"page-header\\\"> <h1 class=\\\"page-title\\\" t=\\\"pages.administrative-staff.title\\\"></h1> <filtered-export-widget if.to-view=\\\"isFrp\\\" class=\\\"ml-auto\\\" filter.to-view=\\\"filter\\\" t=\\\"[btn-text]globals.personnelExport\\\" export-csv.trigger=\\\"exportCsv($event)\\\"></filtered-export-widget> </div> <div class=\\\"row row-cards row-deck\\\"> <div class=\\\"col-12\\\"> <custom-table columns.to-view=\\\"tableColumns\\\" extendable.to-view=\\\"true\\\" table-id=\\\"administrative-staff-page\\\" refresh-version.to-view=\\\"tableRefreshVersion\\\" get-shown-entries-data.call=\\\"fetchTableData(limit, offset)\\\"></custom-table> </div> </div> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <div class=\\\"announcements\\\"> <div class=\\\"announcements__header\\\"> <div class=\\\"announcements__header-title-bar\\\"> <div class=\\\"announcements__header-title\\\" t=\\\"pages.announcements.title\\\"> </div> <div class=\\\"announcements__header-buttons-wrapper\\\"> <button-standard class=\\\"announcements__header-button\\\" btn-type=\\\"outline\\\" disabled.to-view=\\\"!selectMode\\\" click.delegate=\\\"deleteSelection()\\\" t=\\\"[btnText]globals.delete\\\" icon-name=\\\"System/Tabler/Trash\\\" btn-color=\\\"danger\\\"></button-standard> <button-standard class=\\\"announcements__header-button\\\" disabled.to-view=\\\"!selectMode\\\" click.delegate=\\\"archiveRestoreSelection()\\\" t=\\\"${ showArchived ? '[btnText]globals.restore' : '[btnText]globals.archiveAction' }\\\" icon-name=\\\"${ showArchived ? 'Distinar/WhiteboardUndo' : 'System/Tabler/Archive' }\\\" btn-color=\\\"secondary\\\"></button-standard> <button-standard class=\\\"announcements__header-button\\\" click.delegate=\\\"createNew()\\\" t=\\\"[btnText]pages.announcements.new\\\" icon-name=\\\"System/Tabler/Alert-Octagon\\\" btn-color=\\\"secondary\\\"></button-standard> </div> </div> <div class=\\\"announcements__header-options\\\"> <label class=\\\"custom-control custom-checkbox custom-control-inline\\\"> <input type=\\\"checkbox\\\" class=\\\"custom-control-input\\\" checked.bind=\\\"showArchived\\\"> <span class=\\\"custom-control-label\\\" t=\\\"globals.showArchived\\\"></span> </label> </div> </div> <div class=\\\"announcements__content-wrapper\\\"> <div repeat.for=\\\"announcement of allAnnouncements\\\" if.to-view=\\\"(!showArchived && !announcement.isArchived) || (showArchived && announcement.isArchived)\\\" class=\\\"announcements__info-card\\\"> <announcement-info-card title.to-view=\\\"announcement.title\\\" id.to-view=\\\"announcement.id\\\" read-count.to-view=\\\"announcement.readCount\\\" visible-from.to-view=\\\"announcement.visibleFrom\\\" visible-to.to-view=\\\"announcement.visibleTo\\\" archived.to-view=\\\"announcement.isArchived\\\" select-mode.to-view=\\\"selectMode\\\" selected.to-view=\\\"selectedAnnouncements[announcement.id]\\\" click.trigger=\\\"announcementSelected(announcement.id)\\\" edit-me.trigger=\\\"editAnnouncement(announcement.id)\\\"></announcement-info-card> </div> </div> </div> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <button-standard click.trigger=\\\"navigateBack()\\\" class=\\\"d-inline-block\\\" t=\\\"[btnText]globals.back\\\" icon-name=\\\"System/Tabler/Chevron-Left\\\" btn-size=\\\"btn-sm\\\" btn-color=\\\"secondary\\\"></button-standard> <div class=\\\"page-header\\\"> <div class=\\\"row\\\"> <h1 class=\\\"page-title col-12\\\">${assessmentCode.code}</h1> <small class=\\\"text-muted col-12\\\" t=\\\"pages.assessment-code-details.description\\\"></small> </div> </div> <div if.to-view=\\\"assessmentCode\\\" class=\\\"card\\\"> <div class=\\\"card-header\\\"> <h2 class=\\\"card-title\\\" t=\\\"pages.assessment-code-details.information\\\"></h2> </div> <div class=\\\"card-body\\\"> <div class=\\\"two-column-grid\\\"> <div class=\\\"form-group\\\"> <label class=\\\"form-label\\\" t=\\\"pages.assessment-code-details.code\\\"></label> <input class=\\\"form-control\\\" type=\\\"text\\\" disabled=\\\"disabled\\\" value.to-view=\\\"assessmentCode.code\\\"> </div> <div class=\\\"form-group\\\"> <label class=\\\"form-label\\\" t=\\\"pages.assessment-code-details.createdAt\\\"></label> <input class=\\\"form-control\\\" type=\\\"text\\\" disabled=\\\"disabled\\\" value.to-view=\\\"assessmentCodeCreatedAt\\\"> </div> </div> </div> </div> <div if.to-view=\\\"result\\\" class=\\\"card\\\"> <div class=\\\"card-header d-flex justify-content-between\\\"> <h3 class=\\\"card-title\\\" t=\\\"pages.assessment-code-details.assessmentProtocol\\\"></h3> <div class=\\\"d-flex\\\"> <button-standard class=\\\"mr-2\\\" click.delegate=\\\"downloadResult(EProtocolLength.SHORT)\\\" icon-name=\\\"System/Tabler/Download\\\" btn-color=\\\"secondary\\\" t=\\\"[btnText]pages.assessment-code-details.downloadShortVersion\\\"></button-standard> <button-standard click.delegate=\\\"downloadResult(EProtocolLength.LONG)\\\" icon-name=\\\"System/Tabler/Download\\\" btn-color=\\\"secondary\\\" t=\\\"[btnText]pages.assessment-code-details.downloadLongVersion\\\"></button-standard> </div> </div> <testexam-result-overview result.to-view=\\\"result\\\" is-assessment.to-view=\\\"true\\\"></testexam-result-overview> <div class=\\\"card-body\\\"> <button-standard btn-fill-mode=\\\"fill-width\\\" btn-type=\\\"outlined\\\" click.delegate=\\\"toggleShowCorrectQuestions()\\\" t=\\\"[btnText]globals.${ showCorrectQuestions ? 'hideCorrectQuestions' : 'showCorrectQuestions' }\\\"></button-standard> <exam-result-legend></exam-result-legend> <quiz-result-question-block repeat.for=\\\"question of result.questions\\\" question.to-view=\\\"question\\\" question-index.to-view=\\\"$index\\\" show.bind=\\\"!question.isCorrect || showCorrectQuestions\\\"> </quiz-result-question-block> </div> </div> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <assessment-code-print-list if.to-view=\\\"!printHandout && !printInfo\\\" planned-date.to-view=\\\"plannedDate\\\" codes.to-view=\\\"assessmentCodes\\\"></assessment-code-print-list> <assessment-code-print-handout if.to-view=\\\"printHandout\\\" repeat.for=\\\"code of assessmentCodes\\\" code.to-view=\\\"code\\\"></assessment-code-print-handout> <assessment-print-info if.to-view=\\\"printInfo\\\"></assessment-print-info> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template class=\\\"assessment-code-protocol-print-page\\\"> <div if.to-view=\\\"result\\\" class=\\\"assessment-code-protocol-print-page__print-container\\\"> <div class=\\\"lp-site-content-small w-100 mt-0\\\"> <h1 class=\\\"assessment-code-protocol-print-page__code\\\">${assessmentCode.code}</h1> <testexam-result-overview result.to-view=\\\"result\\\" is-assessment.to-view=\\\"true\\\" collapsable.to-view=\\\"false\\\"></testexam-result-overview> <template if.to-view=\\\"resultLength === EProtocolLength.LONG\\\"> <exam-result-legend></exam-result-legend> <quiz-result-question-block repeat.for=\\\"question of result.questions\\\" class=\\\"assessment-code-protocol-print-page__question\\\" blink-enabled.to-view=\\\"false\\\" question.to-view=\\\"question\\\" question-index.to-view=\\\"$index\\\"></quiz-result-question-block> </template> </div> </div> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <button-standard click.trigger=\\\"navigateBack()\\\" class=\\\"d-inline-block\\\" t=\\\"[btnText]globals.back\\\" icon-name=\\\"System/Tabler/Chevron-Left\\\" btn-size=\\\"btn-sm\\\" btn-color=\\\"secondary\\\"></button-standard> <div if.to-view=\\\"assessment\\\" class=\\\"page-header\\\"> <div class=\\\"row\\\"> <h1 class=\\\"page-title col-5\\\">${pageTitle}</h1> <div class=\\\"d-flex col-7\\\"> <div if.to-view=\\\"assessment.startedAt != null\\\" class=\\\"assessment-details-page__runtime ml-auto mr-2\\\" t=\\\"pages.assessment-details.activeSince\\\" t-params.to-view=\\\"{\\n               runtime: formattedRuntime\\n             }\\\"></div> <div else class=\\\"ml-auto\\\"></div> <dropdown-button class=\\\"mr-2 d-inline-block\\\" disabled.to-view=\\\"!assessment.participantCount\\\" show-arrow.to-view=\\\"true\\\" offset.to-view=\\\"[-5, 6]\\\"> <button-standard slot=\\\"button\\\" t=\\\"[btnText]pages.assessment-details.downloads\\\" t-params.to-view=\\\"{\\n                            count: selectedAssessmentCodes.length ,\\n                            context: !selectedAssessmentCodes.length ? 'zero' : ''\\n                          }\\\" disabled.to-view=\\\"!assessment.participantCount\\\" dropdown-open.to-view=\\\"false\\\" btn-color=\\\"secondary\\\"></button-standard> <button class=\\\"dropdown-item pointer\\\" click.delegate=\\\"handleDownloadAssessmentCodesExcelClick()\\\" t=\\\"pages.assessment-details.downloadCodesExcel\\\"></button> <button class=\\\"dropdown-item pointer\\\" click.delegate=\\\"handleDownloadAssessmentCodesPdfClick()\\\" t=\\\"pages.assessment-details.downloadCodesPdf\\\"></button> <div class=\\\"dropdown-item pointer pb-0 m-0 assessment-details-page__download-button\\\"> <button class=\\\"dropdown-item d-flex flex-column\\\" disabled.to-view=\\\"!canDownloadProtocol\\\" click.delegate=\\\"handleDownloadAssessmentProtocolClick(EProtocolLength.LONG)\\\"> <span t=\\\"pages.assessment-details.assessmentProtocols\\\" t-params.to-view=\\\"{ context: !selectedAssessmentCodes.length ? 'all' : '' }\\\"> </span> <span t=\\\"pages.assessment-details.assessmentProtocolsLong\\\" t-params.to-view=\\\"{ context: !selectedAssessmentCodes.length ? 'all' : '' }\\\"></span> </button> <safe-icon class=\\\"assessment-details-page__download-button-icon\\\" icon-name=\\\"System/Tabler/File\\\"></safe-icon> </div> <div class=\\\"dropdown-item pointer m-0 assessment-details-page__download-button\\\"> <button class=\\\"dropdown-item d-flex flex-column\\\" disabled.to-view=\\\"!canDownloadProtocol\\\" click.delegate=\\\"handleDownloadAssessmentProtocolClick(EProtocolLength.SHORT)\\\"> <span t=\\\"pages.assessment-details.assessmentProtocols\\\" t-params.to-view=\\\"{ context: !selectedAssessmentCodes.length ? 'all' : '' }\\\"></span> <span t=\\\"pages.assessment-details.assessmentProtocolsShort\\\"></span> </button> <safe-icon class=\\\"assessment-details-page__download-button-icon\\\" icon-name=\\\"System/Tabler/File\\\"></safe-icon> </div> <button class=\\\"dropdown-item pointer\\\" click.delegate=\\\"handleDownloadCoverSheetClick()\\\" t=\\\"pages.assessment-details.downloadCoverSheet\\\"></button> </dropdown-button> <button-standard class=\\\"mr-2 d-inline-block\\\" click.delegate=\\\"handleCreateAssessmentCodesClick()\\\" t=\\\"[btnText]globals.createAssessmentCodes\\\" icon-name=\\\"System/Tabler/Plus\\\" btn-color=\\\"secondary\\\"></button-standard> <button-standard class=\\\"d-inline-block\\\" click.delegate=\\\"handleToggleAssessmentReleasedClick()\\\" disabled.to-view=\\\"!assessment.participantCount\\\" t.to-view=\\\"assessmentReleasedToggleBtnText\\\" btn-color.to-view=\\\"assessmentReleasedToggleBtnColor\\\"></button-standard> </div> <small class=\\\"text-muted col-4\\\" t=\\\"pages.assessment-details.description\\\"></small> </div> </div> <div class=\\\"row row-cards row-deck\\\"> <div class=\\\"col-12\\\"> <custom-table extendable.to-view=\\\"true\\\" selectable.to-view=\\\"true\\\" selected.two-way=\\\"selectedAssessmentCodes\\\" table-id=\\\"assessment-codes\\\" refresh-version.to-view=\\\"tableRefreshVersion\\\" delegate.to-view=\\\"$this\\\" view-model.ref=\\\"table\\\"></custom-table> </div> </div> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <div class=\\\"page-header\\\"> <div class=\\\"row mb-2\\\"> <h1 class=\\\"page-title col-12\\\" t=\\\"pages.assessments.title\\\"></h1> <small class=\\\"text-muted col-12\\\" t=\\\"pages.assessments.description\\\"></small> </div> <div class=\\\"ml-auto\\\"> <button-standard click.delegate=\\\"handleCreateAssessmentClick()\\\" class=\\\"mb-2 d-inline-block\\\" t=\\\"[btnText]globals.createAssessment\\\" icon-name=\\\"System/Tabler/Plus\\\" btn-color=\\\"primary\\\"></button-standard> </div> </div> <alert-message message.to-view=\\\"{ tk: `elements.teacherTutorials.warning` }\\\" message-style=\\\"alert-info\\\" show-icon.to-view=\\\"true\\\"></alert-message> <div class=\\\"row row-cards row-deck\\\"> <div class=\\\"col-12\\\"> <custom-table columns.to-view=\\\"tableColumns\\\" extendable.to-view=\\\"true\\\" table-id=\\\"assessments\\\" refresh-version.to-view=\\\"tableRefreshVersion\\\" get-shown-entries-data.call=\\\"fetchTableData(limit, offset)\\\"></custom-table> </div> </div> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <div class=\\\"avatar-component-set-page\\\"> <avatar-component-set-editor set-id-from-page.to-view=\\\"setId\\\"></avatar-component-set-editor> </div> </template> \";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <div class=\\\"avatar-component-sets-page\\\"> <div class=\\\"avatar-component-sets.page__header\\\"> <div class=\\\"avatar-component-sets-page__header-title-bar\\\"> <div class=\\\"avatar-component-sets-page__header-title\\\" t=\\\"pages.avatar-component-sets.title\\\"> </div> <div class=\\\"avatar-component-sets-page__header-buttons-wrapper\\\"> <button-standard class=\\\"avatar-component-sets-page__header-button\\\" click.delegate=\\\"createNew()\\\" t=\\\"[btnText]pages.avatar-component-sets.new\\\" icon-name=\\\"System/Tabler/Plus\\\" btn-color=\\\"secondary\\\"></button-standard> </div> </div> </div> <div class=\\\"row row-cards row-deck\\\"> <div class=\\\"col-12\\\"> <custom-table columns.to-view=\\\"tableColumns\\\" extendable=\\\"true\\\" table-id=\\\"avatar-component-sets-page\\\" current-sort-column.two-way=\\\"sortColumn\\\" current-sort-direction.two-way=\\\"sortDirection\\\" get-shown-entries-data.call=\\\"fetchTableData(limit, offset)\\\" refresh-version.to-view=\\\"tableRefreshVersion\\\"> </custom-table> </div> </div> </div> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <div class=\\\"page-header\\\"> <h1 class=\\\"page-title\\\" t=\\\"pages.billing-csv-export.title\\\"></h1> </div> <export-sap-billing-form if.to-view=\\\"isProvider\\\"></export-sap-billing-form> <export-details-billing-form></export-details-billing-form> </template> \";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <button-standard click.trigger=\\\"navigateToCourse()\\\" t=\\\"[btnText]globals.back\\\" icon-name=\\\"System/Tabler/Chevron-Left\\\" btn-size=\\\"btn-sm\\\" btn-color=\\\"secondary\\\"></button-standard> <div class=\\\"page-header\\\"> <h1 class=\\\"page-title\\\" t=\\\"pages.block-overview.title\\\"></h1> <info-icon t=\\\"[info-text]pages.block-overview.infoText\\\" class=\\\"ml-2\\\"></info-icon> </div> <div class=\\\"row row-cards row-deck\\\"> <div class=\\\"col-12\\\"> <custom-table columns.to-view=\\\"tableColumns\\\" extendable=\\\"true\\\" table-id.to-view=\\\"`block-overview-page-${courseId}`\\\" refresh-version.to-view=\\\"tableRefreshVersion\\\" get-shown-entries-data.call=\\\"fetchTableData(limit, offset)\\\"></custom-table> </div> </div> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <div class=\\\"page-header\\\"> <h1 class=\\\"page-title\\\" t=\\\"pages.blocklist.title\\\"></h1> <div class=\\\"ml-auto\\\"> <button class=\\\"btn btn-secondary\\\" click.delegate=\\\"openAddNicknameToBlocklistDialog()\\\"><i class=\\\"fe fe-plus mr-2\\\"></i>${ 'pages.blocklist.addNickname' | t}</button> </div> </div> <div class=\\\"row row-cards row-deck\\\"> <div class=\\\"col-12\\\"> <custom-table columns.to-view=\\\"columns\\\" extendable=\\\"true\\\" table-id=\\\"blocklist-page\\\" current-sort-column.two-way=\\\"sortColumn\\\" refresh-version.to-view=\\\"tableRefreshVersion\\\" get-shown-entries-data.call=\\\"fetchTableData(limit, offset)\\\"></custom-table> </div> </div> </template>\";\n// Exports\nexport default code;","import { PLATFORM } from 'aurelia-pal';\nexport function configure(config) {\n    config.feature(PLATFORM.moduleName('resources/attributes/index'));\n    config.feature(PLATFORM.moduleName('resources/converters/index'));\n    config.feature(PLATFORM.moduleName('resources/elements/index'));\n    config.feature(PLATFORM.moduleName('resources/pages/avatar-component-set-page/index'));\n    config.feature(PLATFORM.moduleName('resources/pages/avatar-component-sets-page/index'));\n    config.feature(PLATFORM.moduleName('resources/pages/distinar-separate-window/index'));\n    config.feature(PLATFORM.moduleName('resources/pages/content-editor-preview-window/index'));\n    config.feature(PLATFORM.moduleName('resources/pages/content-editor-preview-frame/index'));\n    config.feature(PLATFORM.moduleName('resources/pages/messages-page/index'));\n    config.feature(PLATFORM.moduleName('resources/pages/presentation-print-page/index'));\n    config.feature(PLATFORM.moduleName('resources/pages/user-editor-page/index'));\n}\n//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvcmVzb3VyY2VzL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFHdkMsTUFBTSxVQUFVLFNBQVMsQ0FBQyxNQUE4QjtJQUN0RCxNQUFNLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsNEJBQTRCLENBQUMsQ0FBQyxDQUFDO0lBQ2xFLE1BQU0sQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyw0QkFBNEIsQ0FBQyxDQUFDLENBQUM7SUFDbEUsTUFBTSxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLDBCQUEwQixDQUFDLENBQUMsQ0FBQztJQUNoRSxNQUFNLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsaURBQWlELENBQUMsQ0FBQyxDQUFDO0lBQ3ZGLE1BQU0sQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxrREFBa0QsQ0FBQyxDQUFDLENBQUM7SUFDeEYsTUFBTSxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLGdEQUFnRCxDQUFDLENBQUMsQ0FBQztJQUN0RixNQUFNLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMscURBQXFELENBQUMsQ0FBQyxDQUFDO0lBQzNGLE1BQU0sQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxvREFBb0QsQ0FBQyxDQUFDLENBQUM7SUFDMUYsTUFBTSxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLHFDQUFxQyxDQUFDLENBQUMsQ0FBQztJQUMzRSxNQUFNLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsK0NBQStDLENBQUMsQ0FBQyxDQUFDO0lBQ3JGLE1BQU0sQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyx3Q0FBd0MsQ0FBQyxDQUFDLENBQUM7QUFDaEYsQ0FBQyJ9","import { autoinject } from 'aurelia-framework';\nimport { I18N } from 'aurelia-i18n';\nimport { Router } from 'aurelia-router';\nimport { ColumnSortDirection, FilterOperationType } from 'common/dto/filter/filter.enum';\nimport { IFilterDto } from 'common/dto/filter/filter.interface';\nimport { ITenantUserInfoDto } from 'common/dto/tenant/other/tenant.user.info.interface';\nimport { RoleName } from 'common/enum/role.enums';\nimport { Config } from 'common/fe-config/config';\nimport { AppUtils } from 'common/utils/AppUtils';\nimport { assertNotNullOrUndefined } from 'common/utils/assert';\nimport { LogUtils } from 'common/utils/LogUtils';\nimport { AuthApiService } from '../../../services/api/AuthApiService';\nimport { TenantApiService } from '../../../services/api/TenantApiService';\nimport { ActionsCell } from '../../elements/custom-table/actions-cell/actions-cell';\nimport { CustomTableCell } from '../../elements/custom-table/custom-table-cell/custom-table-cell';\nimport { createPrepareServersideSortingFunction, prepareServersideFiltering } from '../../elements/custom-table/custom-table-helpers';\nimport { IColumnDefinition } from '../../elements/custom-table/custom-table-interfaces';\nimport { FilteredExportWidgetExportCsvEvent } from '../../elements/filtered-export-widget/filtered-export-widget';\n\n@autoinject()\nexport class AdministrativeStaffPage {\n  private isFrp = AppUtils.isFrp;\n  private tableRefreshVersion: number = 0;\n  private filter: IFilterDto<ITenantUserInfoDto> = {\n    offset: 0,\n    limit: 10,\n    order: [{\n      field: 'tenantName',\n      dir: ColumnSortDirection.ASC,\n    }],\n    filter: []\n  };\n\n  private tableColumns: Array<IColumnDefinition<CustomTableCell<ITenantUserInfoDto> | ActionsCell<ITenantUserInfoDto>>> = [\n    {\n      id: 'firstname',\n      nameTk: 'globals.firstname',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        transformer: user => user.firstname ?? '-'\n      },\n      prepareSorting: createPrepareServersideSortingFunction(() => this.filter, 'firstname'),\n      filter: {\n        placeholderTk: 'globals.filter',\n        prepare: user => prepareServersideFiltering(this.filter, 'firstname', user),\n      },\n    },\n    {\n      id: 'lastname',\n      nameTk: 'globals.lastname',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        transformer: user => user.lastname ?? '-'\n      },\n      prepareSorting: createPrepareServersideSortingFunction(() => this.filter, 'lastname'),\n      filter: {\n        placeholderTk: 'globals.filter',\n        prepare: user => prepareServersideFiltering(this.filter, 'lastname', user),\n      },\n    },\n    {\n      id: 'email',\n      nameTk: 'globals.email',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        transformer: user => user.email\n      },\n      prepareSorting: createPrepareServersideSortingFunction(() => this.filter, 'email'),\n      filter: {\n        placeholderTk: 'globals.filter',\n        prepare: user => prepareServersideFiltering(this.filter, 'email', user),\n      },\n    },\n    {\n      id: 'phone',\n      nameTk: 'globals.phone',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        transformer: user => user.phoneNumber ?? '-'\n      },\n      prepareSorting: createPrepareServersideSortingFunction(() => this.filter, 'phoneNumber'),\n      filter: {\n        placeholderTk: 'globals.filter',\n        prepare: user => prepareServersideFiltering(this.filter, 'phoneNumber', user),\n      },\n    },\n    {\n      id: 'tenant',\n      nameTk: 'globals.tenant',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        transformer: user => user.tenantName ?? '-'\n      },\n      prepareSorting: createPrepareServersideSortingFunction(() => this.filter, 'tenantName'),\n      filter: {\n        placeholderTk: 'globals.filter',\n        prepare: user => prepareServersideFiltering(this.filter, 'tenantName', user),\n      },\n    },\n    {\n      id: 'status',\n      nameTk: 'globals.status',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        tk: user => user.enabled ? 'globals.active' : 'globals.inactive',\n        spanClasses: user => user.enabled ? '' : 'text-danger'\n      },\n      prepareSorting: createPrepareServersideSortingFunction(this.filter, 'enabled'),\n      filter: {\n        initialValue: Config().defaultTableFilter.enabled ? 'true' : undefined,\n        placeholderTk: 'globals.filter',\n        prepare: user => prepareServersideFiltering(this.filter, 'enabled', user, FilterOperationType.Eq),\n        options: [\n          { keyTk: 'globals.active', value: 'true', },\n          { keyTk: 'globals.inactive', value: 'false' }\n        ]\n      }\n    },\n    {\n      id: 'roles',\n      nameTk: 'pages.administrative-staff.role',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        transformer: user => this.i18n.tr(`globals.${user.role.toLowerCase()}`)\n      },\n      prepareSorting: createPrepareServersideSortingFunction(this.filter, 'role'),\n      filter: {\n        placeholderTk: 'globals.filter',\n        prepare: user => prepareServersideFiltering(this.filter, 'role', user, FilterOperationType.Eq),\n        options: Object.values(RoleName).map(role => ({\n          key: this.i18n.tr(`globals.${role.toLowerCase()}`),\n          value: role\n        }))\n      }\n    },\n    {\n      visible: user => user?.role !== RoleName.Provider,\n      id: 'actions',\n      nameTk: '',\n      cellViewModel: ActionsCell,\n      cellOptions: {\n        entries: [\n          {\n            labelTk: 'globals.edit',\n            icon: 'fe-edit',\n            clickCallback: user => { this.editOperator(user); },\n            showCallback: user => user.role === RoleName.Operator\n          },\n          {\n            labelTk: user => user.enabled ? 'globals.deactivate' : 'globals.activate',\n            icon: 'fe-activity',\n            clickCallback: user => { this.setUserEnabled(user, !user.enabled); },\n          },\n          {\n            labelTk: 'globals.loginAs',\n            icon: 'fe-log-in',\n            clickCallback: user => { this.loginAsUser(user); },\n          }\n        ]\n      },\n      shrink: true,\n    },\n  ];\n\n  constructor(\n    private tenantApiService: TenantApiService,\n    private i18n: I18N,\n    private router: Router,\n    private authApiService: AuthApiService\n  ) { }\n\n  private async fetchTableData(limit: number, offset: number): Promise<{ total: number; data: ITenantUserInfoDto[] }> {\n    this.filter.limit = limit;\n    this.filter.offset = offset;\n\n    const response = await this.tenantApiService.getTenantUsers(this.filter);\n\n    return {\n      data: response.rows,\n      total: response.count,\n    };\n  }\n\n  private async setUserEnabled(user: ITenantUserInfoDto, enabled: boolean): Promise<void> {\n    assertNotNullOrUndefined(user.tenantId, 'user.tenantId  cannot be null or undefined');\n    await this.tenantApiService.setTenantUserStatus(user.tenantId, user.id, enabled);\n    this.tableRefreshVersion++;\n  }\n\n  private async loginAsUser(user: ITenantUserInfoDto): Promise<void> {\n    try {\n      await this.authApiService.loginAs(user.id);\n      this.router.navigateToRoute('home');\n    } catch(e) {\n      LogUtils.exception(e);\n    }\n  }\n\n  private editOperator(user: ITenantUserInfoDto): void {\n    this.router.navigateToRoute('tenants/id/operator/id', { tenantId: user.tenantId, userId: user.id });\n  }\n\n  private async exportCsv(event: FilteredExportWidgetExportCsvEvent): Promise<void> {\n    this.tenantApiService.exportTenantUsers(this.filter, event.detail.csvType);\n  }\n}\n","import { autoinject, observable } from 'aurelia-framework';\nimport { StoreService } from 'common/aurelia-services/StoreService';\nimport {\n  IAnnouncementDto,\n  IAnnouncementIdsDto,\n  ICreateAnnouncementDto\n} from 'common/dto/announcement/announcement.interface';\nimport { ArrayUtils } from 'common/utils/ArrayUtils';\nimport { LogUtils } from 'common/utils/LogUtils';\nimport { IState } from '../../../store/state';\nimport { CustomDialogService } from '../../../services/CustomDialogService';\nimport { ToastService } from '../../../services/ToastService';\nimport { AnnouncementApiService } from './../../../services/api/AnnouncementApiService';\n\n@autoinject()\nexport class AnnouncemntsPage {\n  @observable() private showArchived = false;\n  private tenantId: number | null = null;\n  private allAnnouncements: Array<IAnnouncementDto<string>> | null = null;\n  private selectMode = false;\n  private selectedAnnouncements: Record<number, boolean> = {};\n\n  constructor(\n    private customDialogService: CustomDialogService,\n    private toastService: ToastService,\n    private announcementApiService: AnnouncementApiService,\n    private storeService: StoreService<IState>,\n  ) { }\n\n  protected attached(): void {\n    this.storeService.subscribeToDetailedChanges(this, [\n      state => state.currentRole?.tenantId ?? null,\n    ], (_state, tenantId) => {\n      this.tenantId = tenantId;\n      this.fetchData();\n    });\n  }\n\n  protected detached(): void {\n    this.storeService.disposeAllSubscriptions(this);\n  }\n\n  private showArchivedChanged(): void {\n    if(this.allAnnouncements == null) return;\n    if(this.selectMode) this.resetSelection();\n    if(this.showArchived) {\n      this.allAnnouncements = this.allAnnouncements.sort((a, b) => new Date(b.visibleFrom).getTime() - new Date(a.visibleFrom).getTime());\n    } else {\n      this.allAnnouncements = this.allAnnouncements.sort((a, b) => new Date(a.visibleFrom).getTime() - new Date(b.visibleFrom).getTime());\n    }\n  }\n\n  private async deleteSelection(): Promise<void> {\n    if(!Object.values(this.selectedAnnouncements).includes(true)) return;\n\n    const confirmation = await this.customDialogService.confirmDeletion();\n    if(!confirmation) return;\n\n    const idsToDelete = (Object.keys(this.selectedAnnouncements) as Array<unknown> as Array<number>)\n      .filter(key => this.selectedAnnouncements[key] === true).map(Number);\n    const toDelete: IAnnouncementIdsDto = { announcementIds: idsToDelete };\n\n    try {\n      const deletePromises = toDelete.announcementIds.map(id => this.announcementApiService.deleteAnnouncement(id));\n      await Promise.all(deletePromises);\n      this.toastService.genericDeleteSuccess(toDelete.announcementIds.length > 1 ? 'globals.announcements' : 'globals.announcement');\n      await this.fetchData();\n      return;\n    } catch(e) {\n      LogUtils.exception(e);\n      this.toastService.genericDeleteError(toDelete.announcementIds.length > 1 ? 'globals.announcements' : 'globals.announcement', e);\n      throw e;\n    }\n  }\n\n  private async archiveRestoreSelection(): Promise<void> {\n    if(!Object.values(this.selectedAnnouncements).includes(true)) return;\n\n    const idsToArchiveRestore = (Object.keys(this.selectedAnnouncements) as Array<unknown> as Array<number>)\n      .filter(key => this.selectedAnnouncements[key] === true).map(Number);\n    const archiveRestore: IAnnouncementIdsDto = { announcementIds: idsToArchiveRestore };\n\n    try {\n      if(this.showArchived) {\n        await this.announcementApiService.restoreAnnouncements(archiveRestore);\n      } else {\n        const confirmed = await this.customDialogService.confirmAction({\n          icon: { name: 'alert-triangle', color: 'var(--warning)' },\n          titleTk: 'dialogs.editEvent.warningTitle',\n          textTk: 'pages.announcements.archiveWarning',\n          confirmTk: 'globals.archiveAction',\n        });\n        if(!confirmed) return;\n        await this.announcementApiService.archiveAnnouncements(archiveRestore);\n      }\n      this.toastService.genericUpdateSuccess(archiveRestore.announcementIds.length > 1 ? 'globals.announcements' : 'globals.announcement');\n      await this.fetchData();\n      return;\n    } catch(e) {\n      LogUtils.exception(e);\n      this.toastService.genericUpdateError(archiveRestore.announcementIds.length > 1 ? 'globals.announcements' : 'globals.announcement', e);\n      throw e;\n    }\n  }\n\n  private announcementSelected(id: number): void {\n    this.selectedAnnouncements[id] = !this.selectedAnnouncements[id];\n    this.selectMode = Object.values(this.selectedAnnouncements).includes(true);\n  }\n\n  private async createNew(): Promise<void> {\n    if(this.selectMode) this.resetSelection();\n    const announcement: ICreateAnnouncementDto<string> | null = null;\n    const returnFromDialog = await this.customDialogService.showAnnouncementEditDialog({\n      tenantId: this.tenantId,\n      value: announcement,\n      editingDisabled: false,\n      createCallback: async (value: ICreateAnnouncementDto<string>) => {\n        if(this.showArchived) this.showArchived = false;\n        try {\n          await this.announcementApiService.createAnnouncement(value);\n          this.toastService.genericCreateSuccess('globals.announcement');\n          await this.fetchData();\n          return true;\n        } catch(e) {\n          LogUtils.exception(e);\n          this.toastService.genericCreateError('globals.announcement', e);\n          throw e;\n        }\n      },\n    });\n    if(returnFromDialog == null) return;\n  }\n\n  private async editAnnouncement(idToEdit: number): Promise<void> {\n    if(this.selectMode) this.resetSelection();\n    const announcement = this.allAnnouncements?.find(a => a.id === idToEdit);\n    if(announcement == null) return;\n    const announcementForDialog: ICreateAnnouncementDto<string> = {\n      title: announcement.title,\n      text: announcement.text,\n      link: announcement.link,\n      visibleFrom: announcement.visibleFrom,\n      visibleTo: announcement.visibleTo,\n      audience: announcement.audience,\n    };\n    await this.customDialogService.showAnnouncementEditDialog({\n      tenantId: this.tenantId,\n      value: announcementForDialog,\n      editingDisabled: this.showArchived,\n      id: idToEdit,\n      updateCallback: async (value: ICreateAnnouncementDto<string>, id: number | undefined) => {\n        try {\n          if(id == null) return false;\n          await this.announcementApiService.updateAnnouncement(id, value);\n          this.toastService.genericUpdateSuccess('globals.announcement');\n          await this.fetchData();\n          return true;\n        } catch(e) {\n          LogUtils.exception(e);\n          this.toastService.genericUpdateError('globals.announcement', e);\n          throw e;\n        }\n      },\n      deleteCallback: async (id: number | undefined) => {\n        const confirmation = await this.customDialogService.confirmDeletion();\n        if(!confirmation) return false;\n        try {\n          if(id == null) return false;\n          await this.announcementApiService.deleteAnnouncement(id);\n          this.toastService.genericDeleteSuccess('globals.announcement');\n          await this.fetchData();\n          return true;\n        } catch(e) {\n          LogUtils.exception(e);\n          this.toastService.genericDeleteError('globals.announcement', e);\n          throw e;\n        }\n      }\n    });\n  }\n\n  private async fetchData(): Promise<void> {\n    try {\n      this.allAnnouncements = await this.announcementApiService.getAnnouncements({});\n      this.allAnnouncements = this.allAnnouncements.sort((a, b) => new Date(a.visibleFrom).getTime() - new Date(b.visibleFrom).getTime());\n      this.resetSelection();\n    } catch(e) {\n      LogUtils.exception(e);\n      return;\n    }\n  }\n\n  private resetSelection(): void {\n    if(this.allAnnouncements == null) return;\n    // eslint-disable-next-line @typescript-eslint/no-unused-vars\n    this.selectedAnnouncements = ArrayUtils.toRecord(this.allAnnouncements, a => a.id, a => false);\n    this.selectMode = false;\n  }\n}\n","import { autoinject, computedFrom } from 'aurelia-framework';\nimport { I18N } from 'aurelia-i18n';\nimport { Router } from 'aurelia-router';\nimport { IAssessmentCodeDto } from 'common/dto/assessment/assessment-code.interface';\nimport { IExportAssessmentCodesPdfDto, IExportAssessmentCodesPdfOperationDto, IExportAssessmentCodesProtocolDto } from 'common/dto/assessment/assessment-export.interface';\nimport { QuizQuestionsResponse } from 'common/dto/course/quiz/quiz.response';\nimport { ECodeDownloadOption, EProtocolLength } from 'common/enum/assessment.enum';\nimport { QuizAttemptStatus } from 'common/enum/quiz.attempt.status.enum';\nimport { DownloadUtils } from 'common/utils/DownloadUtils';\nimport { assertNotNullOrUndefined } from 'common/utils/assert';\nimport { CustomDialogService } from '../../../services/CustomDialogService';\nimport { DateService } from '../../../services/DateService';\nimport { ToastService } from '../../../services/ToastService';\nimport { AssessmentsApiService } from '../../../services/api/AssessmentsApiService';\nimport { TestexamApiService } from '../../../services/api/TestexamApiService';\n\ninterface IParams {\n  assessmentId?: string;\n  assessmentCodeId?: string;\n}\n\n@autoinject()\nexport class AssessmentCodeDetailsPage {\n  private assessmentId: number | null = null;\n  private assessmentCodeId: number | null = null;\n  private assessmentCode: IAssessmentCodeDto<string> | null = null;\n\n  private showCorrectQuestions: boolean = false;\n  private result: QuizQuestionsResponse | null = null;\n  private EProtocolLength = EProtocolLength;\n\n  constructor(\n    private assessmentsApiService: AssessmentsApiService,\n    private router: Router,\n    private dateService: DateService,\n    private testExamApiService: TestexamApiService,\n    private customDialogService: CustomDialogService,\n    private toastService: ToastService,\n    private i18n: I18N\n  ) { }\n\n  protected async activate(params: IParams): Promise<void> {\n    this.assessmentId = params.assessmentId ? parseInt(params.assessmentId) : null;\n    this.assessmentCodeId = params.assessmentCodeId ? parseInt(params.assessmentCodeId) : null;\n    this.fetchData();\n  }\n\n  private async fetchData(): Promise<void> {\n    if(!this.assessmentCodeId) return;\n    this.assessmentCode = await this.assessmentsApiService.getAssessmentCode(this.assessmentCodeId);\n    if(!this.assessmentCode.quizSessionId || ![QuizAttemptStatus.Finished, QuizAttemptStatus.Suspended].includes(this.assessmentCode.quizState)) return;\n    this.result = await this.testExamApiService.getResult(this.assessmentCode.quizSessionId);\n    // don't display course title\n    this.result.courseTitle = '';\n  }\n\n  private async downloadResult(length: EProtocolLength): Promise<void> {\n    assertNotNullOrUndefined(this.assessmentId, 'assessmentId cannot be null or undefined');\n    assertNotNullOrUndefined(this.assessmentCodeId, 'assessmentCodeId cannot be null or undefined');\n\n    const dto: IExportAssessmentCodesProtocolDto = {\n      assessmentCodeIds: [this.assessmentCodeId],\n      length,\n    };\n    const operationId = await this.assessmentsApiService.exportProtocolPdf(this.assessmentId, dto);\n    await this.showOperationToast(operationId);\n  }\n\n  private async showOperationToast(operationId: number): Promise<void> {\n    this.toastService.operation<IExportAssessmentCodesPdfOperationDto>({\n      operationId,\n      operationName: this.i18n.tr('globals.generatePdf'),\n      successBtnTextTk: 'globals.save',\n      successCallback: result => {\n        DownloadUtils.download({\n          url: result.download.url,\n          filename: result.download.name,\n        });\n        return true;\n      }\n    });\n  }\n\n  private toggleShowCorrectQuestions(): void {\n    this.showCorrectQuestions = !this.showCorrectQuestions;\n  }\n\n  @computedFrom('assessmentCode')\n  private get assessmentCodeCreatedAt(): string {\n    if(!this.assessmentCode) return '';\n    return this.dateService.formatDate(this.assessmentCode?.createdAt);\n  }\n\n  private navigateBack(): void {\n    assertNotNullOrUndefined(this.assessmentId, 'assessmentId cannot be null or undefined');\n    this.router.navigateToRoute('assessment-details/id', { assessmentId: this.assessmentId, });\n  }\n}\n","import { autoinject } from 'aurelia-framework';\nimport { SubscriptionManager, SubscriptionManagerService } from 'common/aurelia-services/SubscriptionManagerService';\nimport { PDF_CONTENT_LOADED_CBNAME } from 'common/constants/print.constants';\nimport { HtmlUtils } from '../../../utils/HtmlUtils';\n\ninterface IParams {\n  codes: string;\n  plannedDate?: string;\n  handout?: string;\n  info?: string;\n}\n\n@autoinject()\nexport class AssessmentCodePrintPage {\n  private assessmentCodes: string[] = [];\n  private plannedDate: string = '';\n  private printHandout: boolean = false;\n  private printInfo: boolean = false;\n\n  private element: HTMLElement;\n  private subscriptionManager: SubscriptionManager;\n  private printStyleTag: HTMLStyleElement | undefined;\n\n  constructor(\n    subscriptionManagerService: SubscriptionManagerService,\n    element: Element,\n  ) {\n    this.element = element as HTMLElement;\n    this.subscriptionManager = subscriptionManagerService.createSubscriptionManager();\n  }\n\n  protected activate(params: IParams): void {\n    this.printHandout = !!params.handout;\n    this.printInfo = !!params.info;\n    this.assessmentCodes = decodeURIComponent(params.codes).split(',');\n    this.plannedDate = decodeURIComponent(params.plannedDate || '');\n\n    this.contentLoaded();\n  }\n\n  protected attached(): void {\n    this.subscriptionManager.subscribeToWindowEvent('beforeprint', () => this.setPrintStyleTag());\n    this.subscriptionManager.subscribeToWindowEvent('afterprint', () => this.printStyleTag?.remove());\n  }\n\n  protected detached(): void {\n    this.subscriptionManager.disposeSubscriptions();\n    this.printStyleTag?.remove();\n  }\n\n  private async contentLoaded(): Promise<void> {\n    if(PDF_CONTENT_LOADED_CBNAME in window && typeof window[PDF_CONTENT_LOADED_CBNAME] === 'function') {\n      window[PDF_CONTENT_LOADED_CBNAME]();\n    }\n  }\n\n  private setPrintStyleTag(): void {\n    if(!this.element.isConnected) return;\n    this.printStyleTag?.remove();\n    this.printStyleTag = HtmlUtils.createAndAppendStyleTag(`html, body {\n      overflow: unset;\n      background-color: white;\n    }`);\n  }\n}\n","import { autoinject } from 'aurelia-framework';\nimport { SubscriptionManager, SubscriptionManagerService } from 'common/aurelia-services/SubscriptionManagerService';\nimport { PDF_CONTENT_LOADED_CBNAME } from 'common/constants/print.constants';\nimport { IAssessmentCodeDto } from 'common/dto/assessment/assessment-code.interface';\nimport { QuizQuestionsResponse } from 'common/dto/course/quiz/quiz.response';\nimport { EProtocolLength } from 'common/enum/assessment.enum';\nimport { QuizAttemptStatus } from 'common/enum/quiz.attempt.status.enum';\nimport { AssessmentsApiService } from '../../../services/api/AssessmentsApiService';\nimport { TestexamApiService } from '../../../services/api/TestexamApiService';\nimport { HtmlUtils } from '../../../utils/HtmlUtils';\n\ninterface IParams {\n  assessmentCodeId: string;\n  length: EProtocolLength;\n}\n\n@autoinject()\nexport class AssessmentCodeProtocolPrintPage {\n  private assessmentCodeId: number | null = null;\n  private assessmentCode: IAssessmentCodeDto<string> | null = null;\n  private result: QuizQuestionsResponse | null = null;\n  private resultLength: EProtocolLength = EProtocolLength.LONG;\n\n  private element: HTMLElement;\n  private subscriptionManager: SubscriptionManager;\n  private printStyleTag: HTMLStyleElement | undefined;\n  private EProtocolLength = EProtocolLength;\n\n  constructor(\n    private assessmentsApiService: AssessmentsApiService,\n    private testExamApiService: TestexamApiService,\n    subscriptionManagerService: SubscriptionManagerService,\n    element: Element,\n  ) {\n    this.element = element as HTMLElement;\n    this.subscriptionManager = subscriptionManagerService.createSubscriptionManager();\n  }\n\n  protected activate(params: IParams): void {\n    this.assessmentCodeId = parseInt(params.assessmentCodeId, 10);\n    this.resultLength = params.length;\n    this.loadAssessmentCodeProtocol();\n  }\n\n  protected attached(): void {\n    this.subscriptionManager.subscribeToWindowEvent('beforeprint', () => this.setPrintStyleTag());\n    this.subscriptionManager.subscribeToWindowEvent('afterprint', () => this.printStyleTag?.remove());\n  }\n\n  protected detached(): void {\n    this.subscriptionManager.disposeSubscriptions();\n    this.printStyleTag?.remove();\n  }\n\n  private async loadAssessmentCodeProtocol(): Promise<void> {\n    if(!this.assessmentCodeId) return;\n    this.assessmentCode = await this.assessmentsApiService.getAssessmentCode(this.assessmentCodeId);\n    if(!this.assessmentCode.quizSessionId || ![QuizAttemptStatus.Finished, QuizAttemptStatus.Suspended].includes(this.assessmentCode.quizState)) return;\n    this.result = await this.testExamApiService.getResult(this.assessmentCode.quizSessionId);\n    // don't display course title\n    this.result.courseTitle = '';\n    if(PDF_CONTENT_LOADED_CBNAME in window && typeof window[PDF_CONTENT_LOADED_CBNAME] === 'function') {\n      window[PDF_CONTENT_LOADED_CBNAME]();\n    }\n  }\n\n  private setPrintStyleTag(): void {\n    if(!this.element.isConnected) return;\n    this.printStyleTag?.remove();\n    this.printStyleTag = HtmlUtils.createAndAppendStyleTag(`html {\n      overflow: unset;\n      background-color: white;\n    }\n\n    body > :not(.assessment-code-protocol-print-page__print-container) {\n      display: none;\n    }`);\n  }\n}\n","import { autoinject, computedFrom } from 'aurelia-framework';\nimport { I18N } from 'aurelia-i18n';\nimport { Router } from 'aurelia-router';\nimport { SubscriptionManager, SubscriptionManagerService } from 'common/aurelia-services/SubscriptionManagerService';\nimport { IAssessmentCodeDto } from 'common/dto/assessment/assessment-code.interface';\nimport {\n  IExportAssessmentCodesCsvDto,\n  IExportAssessmentCodesPdfDto,\n  IExportAssessmentCodesPdfOperationDto,\n  IExportAssessmentCodesProtocolDto\n} from 'common/dto/assessment/assessment-export.interface';\nimport { IAssessmentDto } from 'common/dto/assessment/assessment.interface';\nimport { ColumnSortDirection, FilterOperationType } from 'common/dto/filter/filter.enum';\nimport { IFilterDto, OrderField } from 'common/dto/filter/filter.interface';\nimport { ECodeDownloadOption, EProtocolLength } from 'common/enum/assessment.enum';\nimport { QuizAttemptStatus } from 'common/enum/quiz.attempt.status.enum';\nimport { AssessmentCodeStatus } from 'common/enum/status.enum';\nimport { IText } from 'common/interfaces/translations.interface';\nimport { DateUtils } from 'common/utils/DateUtils';\nimport { DownloadUtils } from 'common/utils/DownloadUtils';\nimport { StatisticsUtils } from 'common/utils/StatisticsUtils';\nimport { assertNotNullOrUndefined } from 'common/utils/assert';\nimport { AssessmentCodePayload, AssessmentPayload, AssessmentWebSocketService } from '../../../services/AssessmentWebSocketService';\nimport { CustomDialogService } from '../../../services/CustomDialogService';\nimport { DateService } from '../../../services/DateService';\nimport { ToastService } from '../../../services/ToastService';\nimport { AssessmentsApiService } from '../../../services/api/AssessmentsApiService';\nimport { ButtonColor } from '../../elements/button-standard/button-standard';\nimport { ButtonCell } from '../../elements/custom-table/button-cell/button-cell';\nimport { CustomTable, CustomTableDefinition, ICustomTableDelegate, ICustomTablePaginationData } from '../../elements/custom-table/custom-table';\nimport { CustomTableCell } from '../../elements/custom-table/custom-table-cell/custom-table-cell';\nimport {\n  VirtualFilterField,\n  createFilterFuntion,\n  createSortFunction\n} from '../../elements/custom-table/custom-table-helpers';\nimport { NavigationActionCell } from '../../elements/custom-table/navigation-action-cell/navigation-action-cell';\nimport { ProgressCell } from '../../elements/custom-table/progress-cell/progress-cell';\n\ninterface IParams {\n  assessmentId?: string;\n}\n\n@autoinject()\nexport class AssessmentDetailsPage implements ICustomTableDelegate<IAssessmentCodeDto<string>,\n  CustomTableCell<IAssessmentCodeDto<string>>\n  | ProgressCell<IAssessmentCodeDto<string>>\n  | ButtonCell<IAssessmentCodeDto<string>>\n  | NavigationActionCell<IAssessmentCodeDto<string>>> {\n\n  private assessmentId: number | null = null;\n  private assessment: IAssessmentDto<string> | null = null;\n  private selectedAssessmentCodes: IAssessmentCodeDto<string>[] = [];\n  private subscriptionManager: SubscriptionManager;\n  private formattedRuntime: string | null = null;\n  private hasCompletedAssessments: boolean = false;\n\n  private EProtocolLength = EProtocolLength;\n  private tableRefreshVersion: number = 0;\n\n  private table!: CustomTable;\n\n  constructor(\n    private assessmentsApiService: AssessmentsApiService,\n    private dateService: DateService,\n    private customDialogService: CustomDialogService,\n    private router: Router,\n    private toastService: ToastService,\n    private i18n: I18N,\n    private webSocketService: AssessmentWebSocketService,\n    subscriptionManagerService: SubscriptionManagerService\n  ) {\n    this.subscriptionManager = subscriptionManagerService.createSubscriptionManager();\n  }\n\n  public async fetchData(filter: IFilterDto<IAssessmentCodeDto<string>>): Promise<ICustomTablePaginationData<IAssessmentCodeDto<string>>> {\n    if(!this.assessmentId) return { data: [], total: 0 };\n\n    const response = await this.assessmentsApiService.getAssessmentCodes(this.assessmentId, filter);\n    this.hasCompletedAssessments = response.hasCompletedAssessments;\n\n    return {\n      data: response.rows,\n      total: response.count,\n    };\n  }\n\n  public getTableBaseOrdering(): OrderField<IAssessmentCodeDto<string>>[] {\n    return [{\n      field: 'activatedAt',\n      dir: ColumnSortDirection.ASC,\n    }];\n  }\n\n  public getTableDefinition(): CustomTableDefinition<\n    IAssessmentCodeDto<string>,\n    CustomTableCell<IAssessmentCodeDto<string>>\n    | ProgressCell<IAssessmentCodeDto<string>>\n    | ButtonCell<IAssessmentCodeDto<string>>\n    | NavigationActionCell<IAssessmentCodeDto<string>>> {\n    return [{\n      id: 'code',\n      nameTk: 'globals.assessmentCode',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        transformer: c => c.code,\n        callback: c => this.navigateToAssessmentCodeDetails(c),\n        clickable: c => !!c.activatedAt,\n      },\n      prepareSorting: createSortFunction('code'),\n      filter: {\n        placeholderTk: 'globals.filter',\n        prepare: c => createFilterFuntion('code', c, FilterOperationType.Like),\n      },\n    }, {\n      id: 'status',\n      nameTk: 'globals.status',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        tk: c => this.getStatusTableCellFlair(c).tk,\n        spanClasses: c => this.getStatusTableCellFlair(c).spanClasses,\n      },\n      prepareSorting: createSortFunction('status' as VirtualFilterField),\n      filter: {\n        placeholderTk: 'globals.filter',\n        prepare: c => createFilterFuntion('status' as VirtualFilterField, c, FilterOperationType.Eq),\n        options: Object\n          .values(AssessmentCodeStatus)\n          .map(status => ({ keyTk: `pages.assessment-details.status.${status}`, value: status }))\n      }\n    }, {\n      id: 'progress',\n      nameTk: 'pages.assessment-details.progress',\n      cellViewModel: ProgressCell,\n      prepareSorting: createSortFunction('progress' as VirtualFilterField),\n      cellOptions: {\n        percent: c => StatisticsUtils.getPercentage(c.numAnsweredQuestions, c.numTotalQuestions)\n      },\n    }, {\n      id: 'export',\n      nameTk: '',\n      cellViewModel: ButtonCell,\n      shrink: true,\n      cellOptions: {\n        buttonText: c => this.getButtonCellFlair(c).buttonText,\n        buttonColor: c => this.getButtonCellFlair(c).buttonColor,\n        iconName: c => this.getButtonCellFlair(c).iconName,\n        disabled: c => this.getButtonCellFlair(c).disabled,\n        clickCallback: c => this.getButtonCellFlair(c).clickCallback(c),\n      }\n    }, {\n      id: 'navigation',\n      nameTk: '',\n      cellViewModel: NavigationActionCell,\n      shrink: true,\n      cellOptions: {\n        styleClasses: ['navigation-action-cell'],\n        clickCallback: c => this.navigateToAssessmentCodeDetails(c),\n      }\n    },];\n  }\n\n  protected async activate(params: IParams): Promise<void> {\n    this.assessmentId = params.assessmentId ? parseInt(params.assessmentId) : null;\n    if(this.assessmentId != null) {\n      this.webSocketService.addAnswerProgressListener(\n        this,\n        (data: AssessmentCodePayload) => this.handleAnswerProgress(data),\n      );\n      this.webSocketService.addCodeStateListener(\n        this,\n        (data: AssessmentCodePayload) => this.handleCodeState(data),\n      );\n      this.webSocketService.addClosedDueToInactivityStateListener(\n        this,\n        (data: AssessmentPayload) => this.handleClosedDueToInactivity(data),\n      );\n      this.webSocketService.startReceiving(this.assessmentId);\n    }\n    this.fetchAssessmentDetails();\n  }\n\n  protected detached(): void {\n    this.webSocketService.stopReceiving();\n    this.webSocketService.removeListenersByScope(this);\n    this.subscriptionManager.disposeSubscriptions();\n  }\n\n  private setAssessment(assessment: IAssessmentDto<string>): void {\n    this.formattedRuntime = null;\n    this.subscriptionManager.disposeSubscriptions();\n\n    this.assessment = assessment;\n\n    if(this.assessment.startedAt != null) {\n      this.subscriptionManager.subscribeToInterval(() => {\n        this.updateRuntime();\n      }, 1000);\n    }\n  }\n\n  private handleCodeState(payload: AssessmentCodePayload): void {\n    const { code } = payload;\n\n    // enable protocol download if a quiz is completed during an active assessment\n    if(!this.hasCompletedAssessments) {\n      this.hasCompletedAssessments = [\n        QuizAttemptStatus.Finished,\n        QuizAttemptStatus.Suspended,\n      ].includes(code.quizState);\n    }\n\n    this.table.updateTableEntry((tableEntry: IAssessmentCodeDto<string>) =>\n      tableEntry.id === code.id, code);\n  }\n\n\n  private handleAnswerProgress(payload: AssessmentCodePayload): void {\n    const { code } = payload;\n\n    this.table.updateTableEntry((tableEntry: IAssessmentCodeDto<string>) =>\n      tableEntry.id === code.id, code);\n  }\n\n  @computedFrom('assessment.startedAt')\n  private get assessmentReleasedToggleBtnColor(): string {\n    return this.assessment?.startedAt != null ? ButtonColor.Danger : ButtonColor.Primary;\n  }\n\n  @computedFrom('assessment.startedAt')\n  private get assessmentReleasedToggleBtnText(): string {\n    return `[btnText]pages.assessment-details.${this.assessment?.startedAt != null ? 'stop' : 'release'}Assessment`;\n  }\n\n  private async fetchAssessmentDetails(): Promise<void> {\n    if(!this.assessmentId) return;\n    this.setAssessment(await this.assessmentsApiService.getAssessment(this.assessmentId));\n  }\n\n  private async handleCreateAssessmentCodesClick(): Promise<void> {\n    assertNotNullOrUndefined(this.assessment, 'assessment cannot be null or undefined');\n    const dto = await this.customDialogService.showCreateAssessmentCodesDialog({ assessment: this.assessment });\n    if(!dto) return;\n    await this.assessmentsApiService.createAssessmentCodes(this.assessment.id, dto);\n    await this.fetchAssessmentDetails();\n    this.tableRefreshVersion++;\n    this.toastService.success('globals.editSuccess');\n  }\n\n  private async handleDownloadAssessmentCodesPdfClick(): Promise<void> {\n    assertNotNullOrUndefined(this.assessmentId, 'assessmentId cannot be null or undefined');\n    const dto: IExportAssessmentCodesPdfDto = {\n      assessmentCodeIds: this.selectedAssessmentCodes.map(c => c.id),\n      options: [ECodeDownloadOption.OVERVIEW]\n    };\n    const operationId = await this.assessmentsApiService.exportPdf(this.assessmentId, dto);\n    await this.showOperationToast(operationId);\n  }\n\n  private async handleDownloadAssessmentCodesExcelClick(): Promise<void> {\n    assertNotNullOrUndefined(this.assessmentId, 'assessmentId cannot be null or undefined');\n    const exportType = await this.customDialogService.showChooseCsvTypeDialog();\n    if(!exportType) return;\n\n    const filename = `${this.pageTitle}.csv`;\n    const dto: IExportAssessmentCodesCsvDto = {\n      assessmentCodeIds: this.selectedAssessmentCodes.map(c => c.id),\n      exportType\n    };\n    await this.assessmentsApiService.exportCsv(this.assessmentId, dto, filename, this.table.getCurrentFilterValue());\n  }\n\n  private async handleDownloadAssessmentProtocolClick(length: EProtocolLength): Promise<void> {\n    assertNotNullOrUndefined(this.assessmentId, 'assessmentId cannot be null or undefined');\n\n    const dto: IExportAssessmentCodesProtocolDto = {\n      assessmentCodeIds: this.selectedAssessmentCodes.map(c => c.id),\n      length,\n    };\n    const operationId = await this.assessmentsApiService.exportProtocolPdf(this.assessmentId, dto);\n    await this.showOperationToast(operationId);\n  }\n\n  private async handleDownloadCoverSheetClick(): Promise<void> {\n    assertNotNullOrUndefined(this.assessmentId, 'assessmentId cannot be null or undefined');\n    await this.assessmentsApiService.exportCoverSheetPdf(this.assessmentId, 'Deckblatt_Pruefungsbogen.pdf');\n  }\n\n  private async showOperationToast(operationId: number): Promise<void> {\n    this.toastService.operation<IExportAssessmentCodesPdfOperationDto>({\n      operationId,\n      operationName: this.i18n.tr('globals.generatePdf'),\n      successBtnTextTk: 'globals.save',\n      successCallback: result => {\n        DownloadUtils.download({\n          url: result.download.url,\n          filename: result.download.name,\n        });\n        return true;\n      }\n    });\n  }\n\n  private async handleToggleAssessmentReleasedClick(): Promise<void> {\n    assertNotNullOrUndefined(this.assessment?.id, 'expected assessment to exist');\n\n    if(this.assessment.startedAt == null) {\n      this.setAssessment(await this.assessmentsApiService.releaseAssessment(this.assessment.id));\n    } else {\n      this.setAssessment(await this.assessmentsApiService.stopAssessment(this.assessment.id));\n    }\n  }\n\n  private async handleRemoveAssessmentCodeClick(code: IAssessmentCodeDto<string>): Promise<void> {\n    await this.assessmentsApiService.removeAssessmentCode(code.id);\n    this.tableRefreshVersion++;\n    await this.fetchAssessmentDetails();\n    this.toastService.success('globals.editSuccess');\n  }\n\n  public async terminateStudentAssessment(code: IAssessmentCodeDto<string>): Promise<void> {\n    await this.assessmentsApiService.terminateStudentAssessment(code.id);\n  }\n\n\n  @computedFrom('selectedAssessmentCodes.length', 'hasCompletedAssessments')\n  private get canDownloadProtocol(): boolean {\n    if(!this.selectedAssessmentCodes.length) return this.hasCompletedAssessments;\n    return this.selectedAssessmentCodes.some(code => [\n      QuizAttemptStatus.Finished,\n      QuizAttemptStatus.Suspended,\n    ].includes(code.quizState));\n  }\n\n  @computedFrom('assessment')\n  private get pageTitle(): string {\n    if(!this.assessment) return '';\n    return this.dateService.formatDate(this.assessment.plannedDate);\n  }\n\n  private getStatusTableCellFlair(\n    code: IAssessmentCodeDto<string>,\n  ): { spanClasses: string; tk: string } {\n    if(!code.activatedAt) {\n      return { tk: 'pages.assessment-details.status.notActivated', spanClasses: 'text-muted' };\n    }\n    if(code.passed) {\n      return { tk: 'pages.assessment-details.status.passed', spanClasses: 'text-success' };\n    }\n    if(code.quizState === QuizAttemptStatus.Finished\n      || code.quizState === QuizAttemptStatus.Suspended) {\n      return { tk: 'pages.assessment-details.status.notPassed', spanClasses: 'text-danger' };\n    }\n    return { tk: 'pages.assessment-details.status.activated', spanClasses: 'text-warning' };\n  }\n\n  private getButtonCellFlair(code: IAssessmentCodeDto<string>): IButtonCellFlair {\n    if(code.quizState === QuizAttemptStatus.Finished\n      || code.quizState === QuizAttemptStatus.Suspended) {\n      return {\n        buttonText: { tk: 'pages.assessment-details.assessmentFinished' },\n        buttonColor: ButtonColor.Secondary,\n        iconName: '',\n        disabled: true,\n        clickCallback: () => { }\n      };\n    }\n    if(code.activatedAt) return {\n      buttonText: { tk: 'pages.assessment-details.unreleaseAssessment' },\n      buttonColor: ButtonColor.Danger,\n      iconName: '',\n      disabled: false,\n      clickCallback: () => this.terminateStudentAssessment(code)\n    };\n    return {\n      buttonText: { tk: 'globals.delete', },\n      buttonColor: ButtonColor.Secondary,\n      iconName: 'System/Tabler/Trash',\n      disabled: false,\n      clickCallback: () => this.handleRemoveAssessmentCodeClick(code)\n    };\n  }\n\n  private updateRuntime(): void {\n    this.formattedRuntime = this.assessment?.startedAt != null\n      ? DateUtils.formatUnixTime(DateUtils.diffSeconds(this.assessment.startedAt))\n      : null;\n  }\n\n  private navigateToAssessmentCodeDetails(code: IAssessmentCodeDto<string>): void {\n    assertNotNullOrUndefined(this.assessmentId, 'assessmentId cannot be null or undefined');\n    this.router.navigateToRoute('assessment-code-details/id/id', {\n      assessmentCodeId: code.id,\n      assessmentId: this.assessmentId,\n    });\n  }\n\n  private async handleClosedDueToInactivity(data: AssessmentPayload): Promise<void> {\n    if(data.assessmentId !== this.assessmentId) return;\n    this.fetchAssessmentDetails();\n  }\n\n  private navigateBack(): void {\n    this.router.navigateToRoute('assessments');\n  }\n}\n\ninterface IButtonCellFlair {\n  buttonText: IText;\n  buttonColor: ButtonColor;\n  iconName: string;\n  disabled: boolean;\n  clickCallback: (code: IAssessmentCodeDto<string>) => Promise<void> | void;\n}\n","import { autoinject } from 'aurelia-framework';\nimport { Router } from 'aurelia-router';\nimport { I18N } from 'aurelia-i18n';\nimport { IAssessmentDto } from 'common/dto/assessment/assessment.interface';\nimport { ColumnSortDirection, FilterOperationType } from 'common/dto/filter/filter.enum';\nimport { IFilterDto } from 'common/dto/filter/filter.interface';\nimport { CustomDialogService } from '../../../services/CustomDialogService';\nimport { DateService } from '../../../services/DateService';\nimport { ToastService } from '../../../services/ToastService';\nimport { AssessmentsApiService } from '../../../services/api/AssessmentsApiService';\nimport { CustomTableCell } from '../../elements/custom-table/custom-table-cell/custom-table-cell';\nimport { VirtualFilterField, createPrepareServersideSortingFunction, prepareServersideFiltering, prepareServersideRangeFiltering } from '../../elements/custom-table/custom-table-helpers';\nimport { IColumnDefinition } from '../../elements/custom-table/custom-table-interfaces';\nimport { NavigationActionCell } from '../../elements/custom-table/navigation-action-cell/navigation-action-cell';\n\n@autoinject()\nexport class AssessmentsPage {\n  private tableRefreshVersion: number = 0;\n  private filter: IFilterDto<IAssessmentDto<string>> = {\n    offset: 0,\n    limit: 10,\n    order: [{\n      field: 'plannedDate',\n      dir: ColumnSortDirection.DESC,\n    }],\n    filter: []\n  };\n  private tableColumns: Array<IColumnDefinition<CustomTableCell<IAssessmentDto<string>> | NavigationActionCell<IAssessmentDto<string>>>> = [\n    {\n      id: 'plannedDate',\n      nameTk: 'pages.assessments.plannedDate',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        transformer: a => {\n          const date = this.dateService.formatDate(a.plannedDate);\n          if(a.startedAt) return `${date} (${this.i18n.tr('pages.assessments.started')})`;\n          return date;\n        },\n      },\n      prepareSorting: createPrepareServersideSortingFunction(() => this.filter, 'plannedDate'),\n      filter: {\n        placeholderTk: 'globals.filter',\n        daterange: true,\n        prepare: a => prepareServersideRangeFiltering(this.filter, 'plannedDate', a),\n      },\n    },\n    {\n      id: 'participantCount',\n      nameTk: 'pages.assessments.participants',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        transformer: a => a.participantCount.toString(),\n      },\n      filter: {\n        placeholderTk: 'globals.filter',\n        prepare: a => prepareServersideFiltering(this.filter, 'cachedParticipantCount' as VirtualFilterField, a, FilterOperationType.Eq),\n      },\n    },\n    {\n      id: 'completedCount',\n      nameTk: 'pages.assessments.completedAssessments',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        transformer: a => a.completedCount.toString(),\n      },\n      filter: {\n        placeholderTk: 'globals.filter',\n        prepare: a => prepareServersideFiltering(this.filter, 'cachedCompletedCount' as VirtualFilterField, a, FilterOperationType.Eq),\n      },\n    },\n    {\n      id: 'navigation',\n      nameTk: '',\n      cellViewModel: NavigationActionCell,\n      shrink: true,\n      cellOptions: {\n        styleClasses: ['navigation-action-cell'],\n        clickCallback: tR => this.navigateToAssementDetails(tR),\n      }\n    },\n  ];\n\n  constructor(\n    private assessmentsApiService: AssessmentsApiService,\n    private dateService: DateService,\n    private router: Router,\n    private customDialogService: CustomDialogService,\n    private toastService: ToastService,\n    private i18n: I18N,\n  ) { }\n\n  private async fetchTableData(limit: number, offset: number): Promise<{ total: number; data: IAssessmentDto<string>[] }> {\n    this.filter.limit = limit;\n    this.filter.offset = offset;\n\n    const response = await this.assessmentsApiService.getAssessments(this.filter);\n\n    return {\n      data: response.rows,\n      total: response.count,\n    };\n  }\n\n  private async handleCreateAssessmentClick(): Promise<void> {\n    const dto = await this.customDialogService.showCreateAssessmentDialog();\n    if(!dto) return;\n    const response = await this.assessmentsApiService.createAssessment(dto);\n    this.toastService.success('globals.editSuccess');\n    this.navigateToAssementDetails(response);\n  }\n\n  private navigateToAssementDetails(assessment: IAssessmentDto<string>): void {\n    this.router.navigateToRoute('assessment-details/id', { assessmentId: assessment.id, });\n  }\n}","import { autoinject } from 'aurelia-framework';\n\ninterface IParam {\n  setId?: string;\n}\n\n@autoinject()\nexport class AvatarComponentSetPage {\n  private setId: number | null = null;\n\n  protected activate(param: IParam): void {\n    this.setId = param.setId == null ? null : Number(param.setId);\n  }\n}\n","import { FrameworkConfiguration, PLATFORM } from 'aurelia-framework';\n\nexport function configure(config: FrameworkConfiguration): void {\n  config.globalResources(PLATFORM.moduleName('resources/pages/avatar-component-set-page/avatar-component-set-page'));\n}\n","import { autoinject } from 'aurelia-framework';\nimport { Router } from 'aurelia-router';\nimport { IAvatarComponentSetResponseDto } from 'common/dto/avatar/avatar.interface';\nimport { ColumnSortDirection, FilterOperationType } from 'common/dto/filter/filter.enum';\nimport { AvatarComponentSetManagementApiService } from '../../../services/api/AvatarComponentSetManagementApiService';\nimport { CustomDialogService } from '../../../services/CustomDialogService';\nimport { CustomTableCell } from '../../elements/custom-table/custom-table-cell/custom-table-cell';\nimport {\n  createPrepareServersideSortingFunction,\n  IGroupableFilterDto,\n  prepareServersideFiltering,\n  VirtualFilterField\n} from '../../elements/custom-table/custom-table-helpers';\nimport { IColumnDefinition } from '../../elements/custom-table/custom-table-interfaces';\nimport { NavigationActionCell } from '../../elements/custom-table/navigation-action-cell/navigation-action-cell';\n\n@autoinject()\nexport class AvatarComponentSetsPage {\n  private selectMode = false;\n  protected tableRefreshVersion = 0;\n  private avatarComponentSets: IAvatarComponentSetResponseDto[] = [];\n  protected filter: IGroupableFilterDto<IAvatarComponentSetResponseDto> = {\n    offset: 0,\n    limit: 10,\n    order: [{\n      field: 'importId',\n      dir: ColumnSortDirection.ASC,\n    }],\n  };\n  private tableColumns: Array<IColumnDefinition<CustomTableCell<IAvatarComponentSetResponseDto> | NavigationActionCell<IAvatarComponentSetResponseDto>>> = [\n    {\n      id: 'importId',\n      nameTk: 'globals.id',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        transformer: set => set.importId.toString(),\n      },\n      prepareSorting: createPrepareServersideSortingFunction(this.filter, 'importId'),\n      filter: {\n        placeholderTk: 'globals.filter',\n        prepare: v => prepareServersideFiltering(this.filter, 'importId', v, FilterOperationType.Like)\n      }\n    },\n    {\n      id: 'name',\n      nameTk: 'globals.name',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        transformer: set => set.name,\n      },\n      prepareSorting: createPrepareServersideSortingFunction(this.filter, 'name'),\n      filter: {\n        placeholderTk: 'globals.filter',\n        prepare: v => prepareServersideFiltering(this.filter, 'name', v, FilterOperationType.Like)\n      }\n    },\n    {\n      id: 'courses',\n      nameTk: 'globals.courses',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        transformer: set => set.courses.map(course => course.title).join(', '),\n      },\n      filter: {\n        placeholderTk: 'globals.filter',\n        prepare: v => prepareServersideFiltering(this.filter, 'courseTitles' as VirtualFilterField, v, FilterOperationType.Like)\n      },\n      wrap: true\n    },\n    {\n      id: 'navigation',\n      nameTk: '',\n      cellViewModel: NavigationActionCell,\n      shrink: true,\n      cellOptions: {\n        styleClasses: ['navigation-action-cell'],\n        clickCallback: set => this.router.navigateToRoute('avatar-component-set', { setId: set.id })\n      }\n    },\n  ];\n  private sortColumn = this.tableColumns[0];\n  private sortDirection = ColumnSortDirection.ASC;\n\n  constructor(\n    private customDialogService: CustomDialogService,\n    private avatarComponentSetManagementApiService: AvatarComponentSetManagementApiService,\n    private router: Router,\n  ) { }\n\n  public async fetchTableData(limit: number, offset: number): Promise<{ total: number; data: Array<IAvatarComponentSetResponseDto> }> {\n    this.filter.limit = limit;\n    this.filter.offset = offset;\n    const results = await this.avatarComponentSetManagementApiService.getSets(this.filter);\n    return { data: results.rows, total: results.count };\n  }\n\n  private async createNew(): Promise<void> {\n    await this.customDialogService.showCreateAvatarComponentSetDialog();\n  }\n}\n","import { FrameworkConfiguration, PLATFORM } from 'aurelia-framework';\n\nexport function configure(config: FrameworkConfiguration): void {\n  config.globalResources(PLATFORM.moduleName('resources/pages/avatar-component-sets-page/avatar-component-sets-page'));\n}\n","import { autoinject } from 'aurelia-framework';\nimport { StoreService } from 'common/aurelia-services/StoreService';\nimport { RoleName } from 'common/enum/role.enums';\nimport { IState } from 'store/state';\n\n@autoinject()\nexport class BillingCsvExportPage {\n  private isProvider = false;\n  \n  constructor( \n    private storeService: StoreService<IState>,\n  ) { }\n\n  protected activate(): void {\n    this.storeService.subscribeToDetailedChanges(this, [\n      state => state.currentRole,\n    ], (_, currentRole) => {\n      this.isProvider = currentRole?.role === RoleName.Provider;\n    });\n  }\n\n  protected deactivate(): void {\n    this.storeService.disposeAllSubscriptions(this);\n  }\n}\n","import { autoinject } from 'aurelia-framework';\nimport { Router } from 'aurelia-router';\nimport { StoreService, StoreSubscriptionManager } from 'common/aurelia-services/StoreService';\nimport { ICourseContentDto } from 'common/dto/course/content/course.interface';\nimport { IBlockEditResponseDto } from 'common/dto/course/management/course.management.interface';\nimport { ColumnSortDirection, FilterOperationType } from 'common/dto/filter/filter.enum';\nimport { IFilterDto } from 'common/dto/filter/filter.interface';\nimport { assertNotNullOrUndefined } from 'common/utils/assert';\nimport { CourseContentManagementApiService } from '../../../services/api/CourseContentManagementApiService';\nimport { CourseCacheService } from '../../../services/CourseCacheService';\nimport { IState } from '../../../store/state';\nimport { CustomTableCell } from '../../elements/custom-table/custom-table-cell/custom-table-cell';\nimport { createPrepareServersideSortingFunction, prepareServersideFiltering } from '../../elements/custom-table/custom-table-helpers';\nimport { IColumnDefinition, SortNulls } from '../../elements/custom-table/custom-table-interfaces';\nimport { NavigationActionCell } from '../../elements/custom-table/navigation-action-cell/navigation-action-cell';\nimport { SubPages } from '../course-page/course-page';\n\ninterface IParams {\n  courseId: string;\n}\n\n@autoinject\nexport class BlockOverviewPage {\n  private courseId: number | null = null;\n  private storeSubscriptionManager: StoreSubscriptionManager<IState>;\n  private course: ICourseContentDto | null = null;\n  protected tableRefreshVersion = 0;\n  protected filter: IFilterDto<IBlockEditResponseDto<string>> = {\n    offset: 0,\n    limit: 10,\n    order: [\n      {\n        field: 'title',\n        dir: ColumnSortDirection.DESC_NULLS_LAST,\n      }\n    ],\n    filter: []\n  };\n\n  protected tableColumns: Array<IColumnDefinition<CustomTableCell<IBlockEditResponseDto<string>> | NavigationActionCell<IBlockEditResponseDto<string>>>> = [\n    {\n      id: 'title',\n      nameTk: 'globals.title',\n      cellViewModel: CustomTableCell,\n      sortNulls: SortNulls.Last,\n      cellOptions: {\n        transformer: b => b.title,\n      },\n      prepareSorting: createPrepareServersideSortingFunction(this.filter, 'title'),\n      filter: {\n        placeholderTk: 'globals.filter',\n        prepare: v => prepareServersideFiltering(this.filter, 'title', v, FilterOperationType.Like)\n      }\n    },\n    {\n      id: 'text',\n      nameTk: 'pages.block-overview.text',\n      cellViewModel: CustomTableCell,\n      sortNulls: SortNulls.Last,\n      cellOptions: {\n        transformer: b => b.text ?? '-',\n      },\n      prepareSorting: createPrepareServersideSortingFunction(this.filter, 'text'),\n      filter: {\n        placeholderTk: 'globals.filter',\n        prepare: v => prepareServersideFiltering(this.filter, 'text', v, FilterOperationType.Like)\n      },\n      wrap: true\n    },\n    {\n      id: 'pictureTitle',\n      nameTk: 'pages.block-overview.pictureTitle',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        transformer: b => b.pictureTitle ?? '-',\n      },\n      prepareSorting: createPrepareServersideSortingFunction(this.filter, 'pictureTitle'),\n      filter: {\n        placeholderTk: 'globals.filter',\n        prepare: v => prepareServersideFiltering(this.filter, 'pictureTitle', v, FilterOperationType.Like)\n      }\n    },\n    {\n      id: 'audioTitle',\n      nameTk: 'pages.block-overview.audioTitle',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        transformer: b => b.audioTitle ?? '-',\n      },\n      prepareSorting: createPrepareServersideSortingFunction(this.filter, 'audioTitle'),\n      filter: {\n        placeholderTk: 'globals.filter',\n        prepare: v => prepareServersideFiltering(this.filter, 'audioTitle', v, FilterOperationType.Like)\n      }\n    },\n    {\n      id: 'fileTitle',\n      nameTk: 'pages.block-overview.fileTitle',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        transformer: b => b.fileTitle ?? '-',\n      },\n      prepareSorting: createPrepareServersideSortingFunction(this.filter, 'fileTitle'),\n      filter: {\n        placeholderTk: 'globals.filter',\n        prepare: v => prepareServersideFiltering(this.filter, 'fileTitle', v, FilterOperationType.Like)\n      }\n    },\n    {\n      id: 'navigation',\n      nameTk: '',\n      cellViewModel: NavigationActionCell,\n      shrink: true,\n      cellOptions: {\n        styleClasses: ['navigation-action-cell'],\n        clickCallback: b => this.navigateToPage(b.pageId),\n      }\n    },\n  ];\n\n  constructor(\n    private router: Router,\n    private api: CourseContentManagementApiService,\n    private courseCacheService: CourseCacheService,\n    storeService: StoreService<IState>,\n  ) {\n    this.storeSubscriptionManager = storeService.createStoreSubscriptionManager();\n  }\n\n  public async fetchTableData(limit: number, offset: number): Promise<{ total: number; data: Array<IBlockEditResponseDto<string>> }> {\n    if(!this.courseId) return { data: [], total: 0 };\n    this.filter.limit = limit;\n    this.filter.offset = offset;\n\n    const results = await this.api.getBlocks(this.courseId, this.filter);\n    return { data: results.rows, total: results.count };\n  }\n\n  protected async activate(params: IParams): Promise<void> {\n    this.courseId = parseInt(params.courseId);\n    this.courseCacheService.ensureCourseInCache(this.courseId);\n  }\n\n  protected attached(): void {\n    this.storeSubscriptionManager.subscribeToDetailedChanges([\n      state => state.courseContentById,\n    ], state => {\n      if(this.courseId) {\n        this.course = state.courseContentById?.[this.courseId] ?? null;\n      }\n    });\n  }\n\n  protected detached(): void {\n    this.storeSubscriptionManager.unsubscribe();\n  }\n\n  private navigateToCourse(): void {\n    this.router.navigateToRoute('courses/id', { id: this.courseId, subPage: SubPages.Structure });\n  }\n\n  private navigateToPage(pageId: number | null): void {\n    assertNotNullOrUndefined(this.course, 'course cannot be null or undefined');\n    this.router.navigateToRoute('content-editor-course-page', { courseId: this.courseId, pageId });\n  }\n}\n","import { autoinject } from 'aurelia-framework';\nimport { ColumnSortDirection, FilterOperationType } from 'common/dto/filter/filter.enum';\nimport { IFilterDto } from 'common/dto/filter/filter.interface';\nimport { IBlocklistedNicknameDto } from 'common/dto/user/management/user.management.interface';\nimport { LogUtils } from 'common/utils/LogUtils';\nimport { CustomDialogService } from '../../../services/CustomDialogService';\nimport { DateService } from '../../../services/DateService';\nimport { ToastService } from '../../../services/ToastService';\nimport { BlocklistApiService } from '../../../services/api/BlocklistApiService';\nimport { CustomTableCell } from '../../elements/custom-table/custom-table-cell/custom-table-cell';\nimport { createPrepareServersideSortingFunction, prepareServersideFiltering, prepareServersideRangeFiltering } from '../../elements/custom-table/custom-table-helpers';\nimport { IColumnDefinition } from '../../elements/custom-table/custom-table-interfaces';\nimport { IconCell } from '../../elements/custom-table/icon-cell/icon-cell';\n\n@autoinject()\nexport class BlocklistPage {\n  private tableRefreshVersion = 0;\n  private filter: IFilterDto<IBlocklistedNicknameDto<string>> = {\n    offset: 0,\n    limit: 10,\n    order: [\n      {\n        field: 'createdAt',\n        dir: ColumnSortDirection.DESC,\n      }\n    ],\n    filter: []\n  };\n\n  private columns: Array<IColumnDefinition<CustomTableCell<IBlocklistedNicknameDto<string>> | IconCell<IBlocklistedNicknameDto<string>>>> = [\n    {\n      id: 'name',\n      nameTk: 'pages.blocklist.name',\n      cellViewModel: CustomTableCell,\n      cellOptions: { property: 'nickname' },\n      prepareSorting: createPrepareServersideSortingFunction(this.filter, 'nickname'),\n      filter: {\n        placeholderTk: 'globals.filter',\n        prepare: v => prepareServersideFiltering(this.filter, 'nickname', v)\n      }\n    },\n    {\n      id: 'matchModeColumnName',\n      nameTk: 'pages.blocklist.matchModeColumnName',\n      cellViewModel: CustomTableCell,\n      cellOptions: { tk: e => e.partialMatch ? 'pages.blocklist.matchModePartial' : 'pages.blocklist.matchModeFull', },\n      filter: {\n        placeholderTk: 'globals.filter',\n        prepare: v => prepareServersideFiltering(this.filter, 'partialMatch', v, FilterOperationType.Eq),\n        options: [\n          { keyTk: 'pages.blocklist.matchModePartial', value: 'true' },\n          { keyTk: 'pages.blocklist.matchModeFull', value: 'false' }\n        ]\n      }\n    },\n    {\n      id: 'createdAt',\n      nameTk: 'globals.createdAt',\n      cellViewModel: CustomTableCell,\n      prepareSorting: createPrepareServersideSortingFunction(this.filter, 'createdAt'),\n      cellOptions: {\n        transformer: e => {\n          const date = new Date(e.createdAt);\n          return this.dateService.formatDateTime(date);\n        }\n      },\n      filter: {\n        placeholderTk: 'globals.filter',\n        daterange: true,\n        prepare: v => prepareServersideRangeFiltering(this.filter, 'createdAt', v),\n      },\n    },\n    {\n      id: 'delete',\n      nameTk: '',\n      cellViewModel: IconCell,\n      cellOptions: {\n        iconTransformer: () => 'System/Tabler/Trash',\n        callback: e => { this.deleteBlocklistedNickname(e.id); }\n      }\n    },\n  ];\n\n  private sortColumn = this.columns[0];\n\n  constructor(\n    private blockListApiService: BlocklistApiService,\n    private customDialogService: CustomDialogService,\n    private dateService: DateService,\n    private toastService: ToastService\n  ) { }\n\n  private async fetchTableData(limit: number, offset: number): Promise<{ total: number; data: Array<IBlocklistedNicknameDto<string>> }> {\n    this.filter.limit = limit;\n    this.filter.offset = offset;\n    const results = await this.blockListApiService.getBlocklistedNicknames(this.filter);\n    return { data: results.rows, total: results.count };\n  }\n\n  private openAddNicknameToBlocklistDialog(): void {\n    this.customDialogService.showAddBlocklistedNicknameDialog({ updateTableCallback: () => { this.tableRefreshVersion++; } });\n  }\n\n  private async deleteBlocklistedNickname(nicknameId: number): Promise<void> {\n    try {\n      await this.blockListApiService.deleteBlocklistedNickname(nicknameId);\n      this.toastService.success('pages.blocklist.deleteNicknameSuccess');\n      this.tableRefreshVersion++;\n    } catch(e) {\n      LogUtils.exception(e);\n      this.toastService.error('pages.blocklist.deleteNicknameError');\n    }\n  }\n}\n"],"names":[],"sourceRoot":""}