{"version":3,"file":"app-ea3afa86.f584a1446b2fc034d810.bundle.js","mappings":"AAGA","sources":["webpack://lernplattform-admin-frontend/./src/resources/dialogs/crop-image-dialog/crop-image-dialog.html","webpack://lernplattform-admin-frontend/./src/resources/dialogs/data-protection-dialog/data-protection-dialog.html","webpack://lernplattform-admin-frontend/./src/resources/dialogs/delete-confirmation-dialog/delete-confirmation-dialog.html","webpack://lernplattform-admin-frontend/./src/resources/dialogs/distinar-start-conference-dialog/distinar-start-conference-dialog.html","webpack://lernplattform-admin-frontend/./src/resources/dialogs/download-assessment-codes-dialog/download-assessment-codes-dialog.html","webpack://lernplattform-admin-frontend/./src/resources/dialogs/edit-announcement-dialog/edit-announcement-dialog.html","webpack://lernplattform-admin-frontend/./src/resources/dialogs/edit-block-preview-dialog/edit-block-preview-dialog.html","webpack://lernplattform-admin-frontend/./src/resources/dialogs/edit-event-dialog/edit-event-dialog.html","webpack://lernplattform-admin-frontend/./src/resources/dialogs/edit-learning-journey-course-dialog/edit-learning-journey-course-dialog.html","webpack://lernplattform-admin-frontend/./src/resources/dialogs/edit-page-dialog/edit-page-dialog.html","webpack://lernplattform-admin-frontend/./src/resources/dialogs/edit-question-dialog/edit-question-dialog.html","webpack://lernplattform-admin-frontend/./src/resources/dialogs/edit-student-skill-dialog/edit-student-skill-dialog.html","webpack://lernplattform-admin-frontend/./src/resources/dialogs/edit-tile-dialog/edit-tile-dialog.html","webpack://lernplattform-admin-frontend/./src/resources/dialogs/crop-image-dialog/crop-image-dialog.ts","webpack://lernplattform-admin-frontend/./src/resources/dialogs/data-protection-dialog/data-protection-dialog.ts","webpack://lernplattform-admin-frontend/./src/resources/dialogs/delete-confirmation-dialog/delete-confirmation-dialog.ts","webpack://lernplattform-admin-frontend/./src/resources/dialogs/distinar-start-conference-dialog/distinar-start-conference-dialog.ts","webpack://lernplattform-admin-frontend/./src/resources/dialogs/download-assessment-codes-dialog/download-assessment-codes-dialog.ts","webpack://lernplattform-admin-frontend/./src/resources/dialogs/edit-announcement-dialog/edit-announcement-dialog.ts","webpack://lernplattform-admin-frontend/./src/resources/dialogs/edit-block-preview-dialog/edit-block-preview-dialog.ts","webpack://lernplattform-admin-frontend/./src/resources/dialogs/edit-event-dialog/edit-event-dialog.ts","webpack://lernplattform-admin-frontend/./src/resources/dialogs/edit-learning-journey-course-dialog/edit-learning-journey-course-dialog.ts","webpack://lernplattform-admin-frontend/./src/resources/dialogs/edit-page-dialog/edit-page-dialog.ts","webpack://lernplattform-admin-frontend/./src/resources/dialogs/edit-question-dialog/edit-question-dialog.ts","webpack://lernplattform-admin-frontend/./src/resources/dialogs/edit-student-skill-dialog/edit-student-skill-dialog.ts","webpack://lernplattform-admin-frontend/./src/resources/dialogs/edit-tile-dialog/edit-tile-dialog.ts"],"sourcesContent":["// Module\nvar code = \"<template> <div class=\\\"card\\\"> <div class=\\\"card-header\\\"> <h3 class=\\\"card-title\\\" t=\\\"dialogs.cropImage.title\\\"></h3> </div> <div class=\\\"card-body\\\"> <div class=\\\"card crop-image-dialog__body-card\\\"> <image-cropper url.to-view=\\\"model.imageUrl\\\" image-cropper-update.trigger=\\\"handleCropUpdate($event)\\\" initial-data.to-view=\\\"model.initialData\\\" use-wide-screen-view-port.to-view=\\\"model.useWideScreenViewPort\\\"></image-cropper> </div> </div> <div class=\\\"card-footer text-right\\\"> <button class=\\\"btn btn-primary\\\" t=\\\"globals.save\\\" click.delegate=\\\"controller.ok(value)\\\"></button> <button t=\\\"globals.cancel\\\" class=\\\"btn btn-secondary\\\" click.delegate=\\\"controller.cancel()\\\"></button> </div> </div> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template class=\\\"data-protection-dialog\\\"> <div class=\\\"container\\\"> <div class=\\\"card data-protection-dialog__card\\\"> <div class=\\\"card-header\\\"> <h3 class=\\\"card-title\\\" t=\\\"dialogs.dataProtection.title\\\"></h3> </div> <div if.to-view=\\\"isOperator\\\" class=\\\"card-body data-protection-dialog__body\\\"> <markdown-text text.to-view=\\\"termsOfUseText\\\"></markdown-text> <loading-overlay visible.to-view=\\\"loading\\\"></loading-overlay> </div> <div else class=\\\"card-body\\\"> <p t=\\\"dialogs.dataProtection.dataProtectionNotAgreedByOperatorInfo\\\"></p> </div> <div if.to-view=\\\"isOperator\\\" ref=\\\"domButtonElement\\\" class=\\\"data-protection-dialog__accept-btn-wrapper my-4 mx-6\\\"> <button-lightswitch value.to-view=\\\"termsOfUseAccepted\\\" click.trigger=\\\"toggleTermsOfUseAccepted()\\\" t=\\\"[label]dialogs.dataProtection.termsOfUseCheckbox\\\"></button-lightswitch> </div> <div class=\\\"card-footer\\\"> <div class=\\\"row\\\"> <div class=\\\"col-md-6\\\"> <a href.to-view=\\\"dataPrivacyAgreementLink\\\" target=\\\"_blank\\\" class=\\\"btn btn-link\\\" t=\\\"globals.dataPrivacyAgreement\\\"></a> </div> <div class=\\\"col-md-6 data-protection-dialog__footer-btn-wrapper\\\" click.trigger=\\\"highlightButton()\\\" mouseenter.trigger=\\\"toggleShowHelpText()\\\" mouseleave.trigger=\\\"toggleShowHelpText()\\\"> <button-standard if.to-view=\\\"isOperator\\\" click.delegate=\\\"ok()\\\" disabled.to-view=\\\"!termsOfUseAccepted\\\" t=\\\"[btn-text]globals.next\\\"></button-standard> <button-standard else click.delegate=\\\"signOut()\\\" t=\\\"[btn-text]navigation.signOut\\\" icon-name=\\\"System/Tabler/Logout\\\" icon-color=\\\"text-secondary\\\"></button-standard> <small class=\\\"form-text text-muted data-protection-dialog__help-text ${ showHelpText ? 'data-protection-dialog__help-text--visible' : '' }\\\" t=\\\"dialogs.dataProtection.helpText\\\"></small> </div> </div> </div> </div> </div> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template class=\\\"delete-confirmation-dialog\\\"> <div class=\\\"card\\\"> <div class=\\\"card-header\\\"> <div class=\\\"card-title\\\" t=\\\"dialogs.deleteConfirmation.title\\\"></div> </div> <div class=\\\"card-body\\\"> <div class=\\\"text-center text-mute delete-confirmation-dialog__icon-container\\\"> <i class=\\\"fe fe-file\\\" repeat.for=\\\"n of 3\\\" css=\\\"opacity: ${0.25 - n * 0.1}\\\"></i> </div> <div class=\\\"text-center\\\" t.to-view=\\\"textTk\\\"></div> </div> <div class=\\\"card-footer text-right\\\"> <button class=\\\"btn btn-danger\\\" t=\\\"globals.delete\\\" click.delegate=\\\"controller.ok()\\\"></button> <button class=\\\"btn btn-secondary\\\" t=\\\"globals.cancel\\\" click.delegate=\\\"controller.cancel()\\\"></button> </div> </div> </template> \";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <div class=\\\"distinar-start-conference-dialog\\\"> <form> <div class=\\\"card\\\"> <div class=\\\"card-header\\\"> <span t=\\\"dialogs.distinarStartConference.title\\\"></span> </div> <div class=\\\"card-body distinar-start-conference-dialog__content\\\"> <div class=\\\"distinar-start-conference-dialog__alert-info\\\" t=\\\"dialogs.distinarStartConference.permissionNotice\\\" t-params.to-view=\\\"{ appName: appName }\\\"></div> <div if.to-view=\\\"!loading && (couldntLoadMicrophones || couldntLoadCameras)\\\" class=\\\"distinar-start-conference-dialog__alert-error\\\"> <span t=\\\"dialogs.distinarStartConference.couldNotEnumerateDevices\\\"></span> </div> <div show.to-view=\\\"!loading && (microphoneSelectionError || cameraSelectionError)\\\" class=\\\"distinar-start-conference-dialog__alert-error\\\"> <span t=\\\"dialogs.distinarStartConference.couldNotChooseDevice\\\"></span> </div> <div class=\\\"distinar-start-conference-dialog__input-group\\\"> <audio-input-select selected.two-way=\\\"selectedMicrophone\\\" activated.two-way=\\\"microphoneActivated\\\" show-activation.to-view=\\\"true\\\" error.to-view=\\\"microphoneSelectionError\\\" loading.from-view=\\\"microphonesLoading\\\" couldnt-load-devices.from-view=\\\"couldntLoadMicrophones\\\"></audio-input-select> <video-input-select selected.two-way=\\\"selectedCamera\\\" activated.two-way=\\\"cameraActivated\\\" show-activation.to-view=\\\"true\\\" error.to-view=\\\"cameraSelectionError\\\" loading.from-view=\\\"camerasLoading\\\" couldnt-load-devices.from-view=\\\"couldntLoadCameras\\\"></video-input-select> <audio-output-select if.to-view=\\\"supportsOutputDevice\\\" selected.two-way=\\\"selectedAudioOutput\\\" error.to-view=\\\"audioOutputSelectionError\\\" loading.from-view=\\\"audioOutputsLoading\\\"></audio-output-select> </div> <aspect-ratio-container class=\\\"distinar-start-conference-dialog__preview-video-arc\\\" ratio.to-view=\\\"4/3\\\"> <div class=\\\"distinar-start-conference-dialog__preview-video-wrapper\\\"> <safe-icon show.to-view=\\\"!showPreviewVideo\\\" class=\\\"distinar-start-conference-dialog__preview-video-wrapper-icon\\\" icon-name=\\\"Distinar/LecturerCamOff\\\"></safe-icon> <video show.to-view=\\\"showPreviewVideo\\\" ref=\\\"previewVideoElement\\\"></video> </div> </aspect-ratio-container> <loading-overlay visible.to-view=\\\"loading\\\" status-messages.to-view=\\\"loadingStatusMessages\\\"></loading-overlay> </div> <div class=\\\"distinar-start-conference-dialog__footer card-footer\\\"> <button-standard click.delegate=\\\"ok()\\\" t=\\\"[btn-text]dialogs.distinarStartConference.startConference\\\" disabled.to-view=\\\"loading\\\" btn-fill-mode=\\\"fill-width\\\"></button-standard> <button-standard click.delegate=\\\"cancel()\\\" t=\\\"[btn-text]globals.cancel\\\" btn-color=\\\"secondary\\\" disabled.to-view=\\\"loading\\\" btn-fill-mode=\\\"fill-width\\\"></button-standard> </div> </div> </form> </div> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <form submit.trigger=\\\"saveSelection()\\\" class=\\\"card download-assessment-codes-dialog\\\"> <div class=\\\"card-header\\\"> <h1 class=\\\"card-title\\\" t=\\\"dialogs.downloadAssessmentCodes.title\\\"></h1> </div> <div class=\\\"card-body\\\"> <div repeat.for=\\\"downloadOption of downloadOptions\\\" class=\\\"form-group download-assessment-codes-dialog__checkbox ${ downloadOption.selected ? 'download-assessment-codes-dialog__checkbox--selected' : '' }\\\"> <label class=\\\"custom-control custom-checkbox custom-control-inline\\\"> <input class=\\\"custom-control-input\\\" type=\\\"checkbox\\\" checked.two-way=\\\"downloadOption.selected\\\" change.delegate=\\\"downloadOptionsChanged()\\\"> <div class=\\\"custom-control-label download-assessment-codes-dialog__checkbox-label\\\"> <h5 class=\\\"mb-1 ml-2\\\" t=\\\"dialogs.downloadAssessmentCodes.downloadOptionTitles.${downloadOption.value}\\\"></h5> <span class=\\\"ml-2\\\" t=\\\"dialogs.downloadAssessmentCodes.downloadOptionTexts.${downloadOption.value}\\\"></span> </div> </label> </div> </div> <div class=\\\"card-footer text-right\\\"> <button class=\\\"btn btn-primary\\\" style.to-view=\\\"{'cursor': !isAnyOptionSelected ? 'not-allowed' : 'pointer'}\\\" disabled.to-view=\\\"!isAnyOptionSelected\\\" type=\\\"submit\\\" t=\\\"dialogs.downloadAssessmentCodes.download\\\"></button> <button class=\\\"btn btn-secondary\\\" click.delegate=\\\"cancel()\\\" t=\\\"globals.cancel\\\"></button> </div> </form> </template> \";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <div class=\\\"edit-announcement-dialog__content-wrapper\\\"> <div class=\\\"edit-announcement-dialog__content-area edit-announcement-dialog__editor\\\"> <div class=\\\"edit-announcement-dialog__editor-upper-content\\\"> <div class=\\\"edit-announcement-dialog__header\\\"> <div class=\\\"edit-announcement-dialog__header-title\\\" t=\\\"${ editingDisabled ? 'globals.announcement' : 'pages.announcements.editAnnouncement' }\\\"></div> </div> <div class=\\\"edit-announcement-dialog__editor-tab-panel\\\"> <div class=\\\"edit-announcement-dialog__editor-tabs\\\"> <navigation-tabs tabs.to-view=\\\"navigationTabs\\\" wide-tabs.to-view=\\\"true\\\"></navigation-tabs> </div> <div if.to-view=\\\"activeTab === EditAnnouncementTabs.Compose\\\" class=\\\"edit-announcement-dialog__editor-compose\\\"> <div class=\\\"edit-announcement-dialog__editor-compose-left\\\"> <label class=\\\"form-group\\\"> <div class=\\\"form-label\\\" t=\\\"globals.title\\\"></div> <input type=\\\"text\\\" disabled.to-view=\\\"editingDisabled\\\" class=\\\"form-control ${errorTks.title ? 'is-invalid' : ''}\\\" attach-focus t=\\\"[placeholder]globals.placeholderTitle\\\" input.trigger=\\\"errorTks.title = null\\\" value.two-way=\\\"value.title\\\"> <div if.to-view=\\\"errorTks.title != null\\\" class=\\\"invalid-feedback d-flex\\\" t=\\\"${errorTks.title.errorTk}\\\" t-params.to-view=\\\"errorTks.title.errorTkParams\\\"></div> </label> <div class=\\\"form-group edit-announcement-dialog__rich-text-editor-wrapper\\\"> <div class=\\\"form-label\\\" t=\\\"globals.text\\\"></div> <text-editor class=\\\"edit-announcement-dialog__rich-text-editor\\\" editor-context.to-view=\\\"editorContext\\\" text.one-time=\\\"value.text\\\" mode=\\\"system-text\\\" autofocus.to-view=\\\"true\\\" dispatch-null-if-empty.to-view=\\\"true\\\" rich-text-changed.trigger=\\\"handleRichTextChanged($event)\\\"></text-editor> <div if.to-view=\\\"errorTks.text != null\\\" class=\\\"invalid-feedback d-flex\\\" t=\\\"${errorTks.text.errorTk}\\\" t-params.to-view=\\\"errorTks.text.errorTkParams\\\"></div> <rich-text-editor-toolbar class=\\\"edit-announcement-dialog__rich-text-editor-toolbar\\\" editor-context.to-view=\\\"editorContext\\\" mode=\\\"system-text\\\"></rich-text-editor-toolbar> </div> <div class=\\\"edit-announcement-dialog__editor-compose-row\\\"> <label class=\\\"form-group\\\"> <div class=\\\"form-label\\\" t=\\\"pages.announcements.announcementLinkText\\\"></div> <input type=\\\"text\\\" disabled.to-view=\\\"editingDisabled\\\" class=\\\"form-control ${errorTks.linkText ? 'is-invalid' : ''}\\\" t=\\\"[placeholder]pages.announcements.placeholderButtonText\\\" input.trigger=\\\"errorTks.linkText = null\\\" value.two-way=\\\"value.link.name\\\"> <div if.to-view=\\\"errorTks.linkText != null\\\" class=\\\"invalid-feedback d-flex\\\" t=\\\"${errorTks.linkText}\\\"></div> </label> <label class=\\\"form-group\\\"> <div class=\\\"form-label\\\" t=\\\"globals.link\\\"></div> <input disabled.to-view=\\\"editingDisabled\\\" type=\\\"text\\\" class=\\\"form-control ${errorTks.linkLink ? 'is-invalid' : ''}\\\" t=\\\"[placeholder]pages.announcements.placeholderLink\\\" input.trigger=\\\"errorTks.linkLink = null\\\" value.two-way=\\\"value.link.url\\\"> <div if.to-view=\\\"errorTks.linkLink != null\\\" class=\\\"invalid-feedback d-flex\\\" t=\\\"${errorTks.linkLink}\\\"></div> </label> </div> </div> <div class=\\\"edit-announcement-dialog__editor-compose-right\\\"> <label class=\\\"form-group\\\"> <div class=\\\"form-label\\\" t=\\\"pages.announcements.announcementVisibleFrom\\\"></div> <date-input disabled.to-view=\\\"editingDisabled\\\" value.two-way=\\\"value.visibleFrom\\\" change-notifier.to-view=\\\"true\\\" date-changed.trigger=\\\"resetDateErrors()\\\" is-invalid.to-view=\\\"errorTks.visibleFrom\\\" mode=\\\"date\\\"></date-input> <div if.to-view=\\\"errorTks.visibleFrom != null\\\" class=\\\"invalid-feedback d-flex\\\" t=\\\"${errorTks.visibleFrom}\\\"></div> </label> <label class=\\\"form-group\\\"> <div class=\\\"form-label\\\" t=\\\"pages.announcements.announcementVisibleTo\\\"></div> <date-input disabled.to-view=\\\"editingDisabled\\\" value.two-way=\\\"value.visibleTo\\\" change-notifier.to-view=\\\"true\\\" date-changed.trigger=\\\"resetDateErrors()\\\" is-invalid.to-view=\\\"errorTks.visibleTo\\\" mode=\\\"date\\\"></date-input> <div if.to-view=\\\"errorTks.visibleTo != null\\\" class=\\\"invalid-feedback d-flex\\\" t=\\\"${errorTks.visibleTo}\\\"></div> </label> </div> </div> <div if.to-view=\\\"activeTab === EditAnnouncementTabs.Audience\\\" class=\\\"edit-announcement-dialog__editor-audience\\\"> <div class=\\\"edit-announcement-dialog__audience-info\\\" t=\\\"pages.announcements.${ tenantId ? 'audienceInfoTenant' : 'audienceInfo' }\\\"></div> <div class=\\\"edit-announcement-dialog__audience-filter\\\"> <announcement-filter tenant-id.to-view=\\\"tenantId\\\" disabled.to-view=\\\"editingDisabled\\\" tenant-ids.two-way=\\\"audienceFilterTenants\\\" course-ids.two-way=\\\"audienceFilterCourses\\\"></announcement-filter> </div> </div> </div> </div> <div class=\\\"edit-announcement-dialog__editor-footer ${ editExisting ? 'edit-announcement-dialog__editor-footer--spread' : ''}\\\"> <button-standard if.to-view=\\\"editExisting\\\" btn-type=\\\"outline\\\" click.delegate=\\\"handleDelete()\\\" t=\\\"[btnText]globals.delete\\\" icon-name=\\\"System/Tabler/Trash\\\" btn-color=\\\"danger\\\"></button-standard> <div class=\\\"edit-announcement-dialog__editor-footer-right-btn-wrapper\\\"> <button-standard if.to-view=\\\"!editingDisabled\\\" btn-type=\\\"outline\\\" click.delegate=\\\"saveOrCreate()\\\" t=\\\"[btnText]${ editExisting ? 'globals.saveChange' : 'pages.announcements.post'}\\\" btn-color=\\\"primary\\\"></button-standard> <button-standard class=\\\"edit-announcement-dialog__editor-footer-btn\\\" btn-type=\\\"outline\\\" click.delegate=\\\"cancel()\\\" t=\\\"${ editingDisabled ? '[btnText]globals.close' : '[btnText]globals.cancel' }\\\" btn-color=\\\"secondary\\\"></button-standard> </div> </div> </div> <div class=\\\"edit-announcement-dialog__content-area\\\"> <div class=\\\"edit-announcement-dialog__header\\\"> <div class=\\\"edit-announcement-dialog__header-title\\\" t=\\\"globals.preview\\\"></div> </div> <div class=\\\"edit-announcement-dialog__preview-body\\\"> <announcement-body announcement.to-view=\\\"value\\\" annotate.to-view=\\\"true\\\"></announcement-body> </div> </div> </div> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template class=\\\"edit-block-preview-dialog\\\"> <div class=\\\"card edit-block-preview-dialog__card\\\"> <div class=\\\"card-header\\\"> <h1 class=\\\"card-title\\\">${value.title}</h1> <button-standard class=\\\"ml-auto\\\" click.trigger=\\\"controller.cancel()\\\" icon-name=\\\"System/Tabler/X\\\" btn-type=\\\"outline\\\" btn-color=\\\"danger\\\"></button-standard> </div> <div class=\\\"card-body\\\"> <preview-block course-id.to-view=\\\"model.courseId\\\" block.to-view=\\\"value\\\"></preview-block> </div> </div> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <form submit.trigger=\\\"ok()\\\" novalidate> <div class=\\\"card edit-event-dialog__card\\\"> <div class=\\\"card-header\\\"> <h1 class=\\\"card-title\\\" t=\\\"dialogs.editEvent.title\\\"></h1> </div> <div class=\\\"card-body edit-event-dialog__card-body\\\"> <div class=\\\"edit-event-dialog__card-column\\\"> <div class=\\\"form-group\\\"> <div class=\\\"d-flex\\\"> <label class=\\\"form-label\\\" t=\\\"dialogs.editEvent.eventType\\\"></label> <info-icon t=\\\"[info-text]dialogs.editEvent.infoText\\\" class=\\\"ml-2\\\"></info-icon> </div> <select class=\\\"custom-select\\\" attach-focus value.bind=\\\"value.type\\\"> <option repeat.for=\\\"option of eventTypeOptions\\\" model.to-view=\\\"option.value\\\"> ${option.label} </option> </select> </div> <div class=\\\"form-group\\\"> <label class=\\\"form-label\\\" t=\\\"globals.date\\\"></label> <date-input value.two-way=\\\"plannedDateFrom\\\" click.trigger=\\\"resetErrorTkByKey('eventDate')\\\" is-invalid.to-view=\\\"!!errorTks.eventDate\\\" mode=\\\"date\\\"></date-input> <div if.to-view=\\\"errorTks.eventDate\\\" class=\\\"invalid-feedback d-flex\\\" t=\\\"${errorTks.eventDate}\\\"></div> </div> <div class=\\\"edit-event-dialog__time-select-group\\\"> <div class=\\\"form-group\\\"> <label class=\\\"form-label\\\" t=\\\"globals.from\\\"></label> <date-input mode=\\\"time\\\" click.trigger=\\\"resetErrorTkByKey('endTimeBeforeStartTime')\\\" value.two-way=\\\"eventStartTimeString\\\"></date-input> </div> <div class=\\\"form-group\\\"> <label class=\\\"form-label\\\" t=\\\"globals.to\\\"></label> <date-input mode=\\\"time\\\" click.trigger=\\\"resetErrorTkByKey('endTimeBeforeStartTime')\\\" value.two-way=\\\"eventEndTimeString\\\" is-invalid.to-view=\\\"errorTks.endTimeBeforeStartTime\\\"></date-input> </div> <div if.to-view=\\\"errorTks.endTimeBeforeStartTime\\\" class=\\\"invalid-feedback edit-event-dialog__time-select-group-error\\\" t=\\\"${errorTks.endTimeBeforeStartTime}\\\"></div> </div> <div class=\\\"form-group\\\"> <label class=\\\"form-label\\\" t=\\\"dialogs.editEvent.labels.title\\\"></label> <input type=\\\"text\\\" focus.trigger=\\\"resetErrorTkByKey('title')\\\" value.two-way=\\\"value.title\\\" class=\\\"form-control ${ errorTks.title ? 'is-invalid' : '' }\\\" maxlength=\\\"255\\\"> <div if.to-view=\\\"errorTks.title\\\" class=\\\"invalid-feedback\\\" t=\\\"${errorTks.title}\\\"></div> </div> <div class=\\\"form-group\\\"> <label class=\\\"form-label\\\" t.to-view=\\\"eventLecturerDesignation\\\"></label> <select class=\\\"custom-select ${ errorTks.lecturer ? 'is-invalid' : '' }\\\" focus.trigger=\\\"resetErrorTkByKey('lecturer')\\\" value.two-way=\\\"value.lecturerId\\\"> <option model.to-view=\\\"null\\\">-</option> <option repeat.for=\\\"lecturer of lecturers\\\" model.to-view=\\\"lecturer.id\\\"> ${lecturer | tenantUserName} </option> </select> <div if.to-view=\\\"errorTks.lecturer\\\" class=\\\"invalid-feedback\\\" t=\\\"${errorTks.lecturer}\\\" t-params.to-view=\\\"{ designation: eventLecturerDesignation }\\\"></div> </div> <div class=\\\"form-group\\\"> <label class=\\\"form-label\\\" t=\\\"dialogs.editEvent.labels.text\\\"></label> <textarea type=\\\"text\\\" focus.trigger=\\\"resetErrorTkByKey('description')\\\" value.two-way=\\\"value.description\\\" class=\\\"form-control edit-event-dialog__textarea ${ errorTks.description ? 'is-invalid' : '' }\\\"></textarea> <div if.to-view=\\\"errorTks.description\\\" class=\\\"invalid-feedback\\\" t=\\\"${errorTks.description}\\\"></div> </div> <div class=\\\"form-group\\\"> <label class=\\\"custom-control custom-checkbox custom-control-inline\\\"> <input class=\\\"custom-control-input\\\" type=\\\"checkbox\\\" checked.two-way=\\\"value.mandatory\\\"> <span class=\\\"custom-control-label\\\" t=\\\"dialogs.editEvent.mandatory\\\"></span> </label> </div> </div> <div class=\\\"edit-event-dialog__card-column\\\"> <div class=\\\"form-group\\\"> <label class=\\\"form-label\\\" t=\\\"globals.address\\\"></label> <select class=\\\"custom-select\\\" value.bind=\\\"value.addressId\\\"> <option model.to-view=\\\"null\\\">-</option> <option repeat.for=\\\"option of model.availableAddresses\\\" model.bind=\\\"option.id\\\"> ${option.title} </option> </select> </div> <div class=\\\"form-group\\\"> <label class=\\\"form-label\\\" t=\\\"globals.courses\\\"></label> <multi-select-combo-box selected-items-changed.trigger=\\\"handleSelectedCoursesChanged($event)\\\" total-items.to-view=\\\"courses\\\" label-property-name=\\\"title\\\" selected-items.to-view=\\\"selectedCourses\\\"></multi-select-combo-box> </div> <div class=\\\"form-group\\\"> <label class=\\\"form-label\\\" t=\\\"dialogs.editEvent.courseGroups\\\"></label> <multi-select-combo-box selected-items-changed.trigger=\\\"handleSelectedCourseGroupsChanged($event)\\\" total-items.to-view=\\\"courseGroups\\\" label-property-name=\\\"name\\\" is-invalid.to-view=\\\"!!errorTks.courseGroup\\\" selected-items.to-view=\\\"selectedCourseGroups\\\"></multi-select-combo-box> <div if.to-view=\\\"errorTks.courseGroup\\\" class=\\\"invalid-feedback d-flex\\\" t=\\\"${errorTks.courseGroup}\\\"></div> </div> <div class=\\\"form-group\\\"> <label class=\\\"form-label\\\" t=\\\"dialogs.editEvent.labels.students\\\"></label> <text-tags-widget table-columns.to-view=\\\"userTableColumns\\\" table-headers.to-view=\\\"userTableHeaders\\\" table-data.to-view=\\\"foundUsers\\\" tags.to-view=\\\"selectedUsersTags\\\" mark-selected.to-view=\\\"true\\\" fixed-height.to-view=\\\"true\\\" should-list-stay-open.to-view=\\\"true\\\" is-selected-callback.call=\\\"isUserSelected($event)\\\" tags-edited.trigger=\\\"handleUserEntryRemoved($event)\\\" entry-selected.trigger=\\\"handleUserEntrySelected($event)\\\" is-invalid.to-view=\\\"!!errorTks.users\\\" search-string-changed.trigger=\\\"handleUserSearchStringChanged($event)\\\"></text-tags-widget> <div if.to-view=\\\"errorTks.users\\\" class=\\\"invalid-feedback d-flex\\\" t=\\\"${errorTks.users}\\\"></div> </div> <div if.to-view=\\\"selectedCourseGroups.length\\\" class=\\\"form-group\\\"> <label class=\\\"form-label\\\" t=\\\"dialogs.editEvent.selectedCoursesAndGroups\\\"></label> <ul class=\\\"edit-event-dialog__selected-courses-and-groups\\\"> <li repeat.for=\\\"[courseTitle, courseGroupNames] of selectedCourseGroupNamesByCourseTitles\\\"> ${courseTitle} <ul> <li repeat.for=\\\"courseGroupName of courseGroupNames\\\" class=\\\"text-dark\\\">${courseGroupName}</li> </ul> </li> </ul> </div> </div> </div> <div class=\\\"card-footer edit-event-dialog__card-footer\\\"> <div class=\\\"d-flex align-items-end\\\"> <div if.to-view=\\\"!model.createNew\\\" class=\\\"form-group mb-0\\\"> <label class=\\\"custom-control custom-checkbox custom-control-inline edit-event-dialog__footer-checkbox\\\"> <input type=\\\"checkbox\\\" class=\\\"custom-control-input\\\" checked.two-way=\\\"value.notifyOfChanges\\\"> <span class=\\\"custom-control-label text-nowrap\\\" t=\\\"dialogs.editEvent.notifyOfChanges\\\"></span> </label> </div> </div> <div class=\\\"text-right\\\"> <button type=\\\"submit\\\" class=\\\"btn btn-primary\\\" t=\\\"globals.save\\\"></button> <button class=\\\"btn btn-secondary\\\" click.delegate=\\\"cancel()\\\" t=\\\"globals.cancel\\\"></button> </div> </div> </div> </form> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <form submit.trigger=\\\"ok()\\\" class=\\\"card edit-learning-journey-course-dialog\\\"> <div class=\\\"card-header\\\"> <h1 class=\\\"card-title\\\" t=\\\"dialogs.editLearningJourneyCourseDialog.title\\\"></h1> </div> <div class=\\\"card-body\\\"> <div class=\\\"row\\\"> <div class=\\\"form-group col-12\\\"> <h5 t=\\\"globals.settings\\\"></h5> <div class=\\\"two-column-grid edit-learning-journey-course-dialog__radio-group\\\"> <div class=\\\"form-check\\\"> <input class=\\\"form-check-input\\\" type=\\\"radio\\\" id=\\\"editLearningJourneyCourseDialogRadio-1\\\" name=\\\"editLearningJourneyCourseDialogRadio\\\" model.bind=\\\"true\\\" checked.two-way=\\\"value.required\\\"> <label class=\\\"form-check-label\\\" for=\\\"editLearningJourneyCourseDialogRadio-1\\\" t=\\\"globals.required\\\"></label> </div> <div class=\\\"form-check\\\"> <input class=\\\"form-check-input\\\" type=\\\"radio\\\" id=\\\"editLearningJourneyCourseDialogRadio-2\\\" name=\\\"editLearningJourneyCourseDialogRadio\\\" model.bind=\\\"false\\\" checked.two-way=\\\"value.required\\\"> <label class=\\\"form-check-label\\\" for=\\\"editLearningJourneyCourseDialogRadio-2\\\" t=\\\"globals.optional\\\"></label> </div> </div> </div> </div> <hr class=\\\"mt-2 mb-6\\\"/> <div class=\\\"row\\\"> <div class=\\\"form-group col-12\\\"> <h5 t=\\\"globals.requirements\\\"></h5> <p class=\\\"text-muted small\\\" t=\\\"dialogs.editLearningJourneyCourseDialog.requirementDescription\\\"></p> <alert-message if.to-view=\\\"errorTk\\\" message.to-view=\\\"{ tk: errorTk }\\\" message-style=\\\"alert-danger\\\"></alert-message> <multi-select-combo-box if.to-view=\\\"availableLearningJourneyCourses.length\\\" selected-items-changed.trigger=\\\"resetErrorTk()\\\" total-items.to-view=\\\"availableLearningJourneyCourses\\\" label-property-name=\\\"title\\\" selected-items.to-view=\\\"selectedLearningJourneyCourses\\\"></multi-select-combo-box> <p else class=\\\"text-muted small\\\" t=\\\"dialogs.editLearningJourneyCourseDialog.noCoursesAvailable\\\"></p> </div> <div class=\\\"col-12\\\"> <small class=\\\"text-muted\\\" t=\\\"dialogs.editLearningJourneyCourseDialog.${ selectedLearningJourneyCourses.length ? 'activeRequirements' : 'noActiveRequirements' }\\\"></small> <div class=\\\"edit-learning-journey-course-dialog__requirement-list\\\"> <ul if.to-view=\\\"selectedLearningJourneyCourses.length\\\" class=\\\"pl-5 mb-0\\\"> <li repeat.for=\\\"course of selectedLearningJourneyCourses\\\">${course.title}</li> </ul> </div> </div> </div> </div> <div class=\\\"card-footer text-right\\\"> <button class=\\\"btn btn-primary\\\" type=\\\"submit\\\" t=\\\"globals.save\\\"></button> <button class=\\\"btn btn-secondary\\\" click.delegate=\\\"cancel()\\\" t=\\\"globals.cancel\\\"></button> </div> </form> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <form submit.trigger=\\\"ok()\\\" class=\\\"card edit-page-dialog\\\"> <div class=\\\"card-header\\\"> <h1 class=\\\"card-title\\\" t=\\\"dialogs.editPageDialog.title\\\"></h1> </div> <div class=\\\"card-body\\\"> <div class=\\\"row\\\"> <div class=\\\"form-group col-12\\\"> <h5 t=\\\"globals.basics\\\"></h5> <label class=\\\"form-label\\\" t=\\\"dialogs.editPageDialog.summary\\\"></label> <textarea type=\\\"text\\\" value.two-way=\\\"summary\\\" class=\\\"form-control\\\"></textarea> </div> </div> <div class=\\\"row\\\"> <div class=\\\"form-group col-12\\\"> <label class=\\\"custom-control custom-checkbox custom-control-inline form-check\\\"> <input class=\\\"custom-control-input form-check-input\\\" disabled.to-view=\\\"parentId != null\\\" type=\\\"checkbox\\\" id=\\\"editPageDialogCheckboxIsTrainingOnly\\\" change.delegate=\\\"handleIsTrainingOnlyChanged()\\\" checked.two-way=\\\"isTrainingOnly\\\"> <span class=\\\"custom-control-label\\\" t=\\\"dialogs.addPageDialog.isTrainingOnly\\\"></span> </label> </div> </div> <template if.to-view=\\\"showRequirementOptions\\\"> <hr class=\\\"mt-2 mb-6\\\"/> <div class=\\\"row\\\"> <div class=\\\"form-group col-12\\\"> <h5 t=\\\"globals.requirements\\\"></h5> <p class=\\\"text-muted small\\\" t=\\\"dialogs.editPageDialog.requirementDescription\\\"></p> <alert-message if.to-view=\\\"errorTk\\\" message.to-view=\\\"{ tk: errorTk }\\\" message-style=\\\"alert-danger\\\"></alert-message> <multi-select-combo-box if.to-view=\\\"availableChapters.length\\\" selected-items-changed.trigger=\\\"resetErrorTk()\\\" total-items.to-view=\\\"availableChapters\\\" label-property-name=\\\"title\\\" selected-items.to-view=\\\"selectedChapters\\\"></multi-select-combo-box> <p else class=\\\"text-muted small\\\" t=\\\"dialogs.editPageDialog.noChaptersAvailable\\\"></p> </div> </div> </template> </div> <div class=\\\"card-footer text-right\\\"> <button class=\\\"btn btn-primary\\\" type=\\\"submit\\\" t=\\\"globals.save\\\"></button> <button class=\\\"btn btn-secondary\\\" click.delegate=\\\"cancel()\\\" t=\\\"globals.cancel\\\"></button> </div> </form> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <form class=\\\"card edit-question-dialog__card\\\" submit.trigger=\\\"saveQuestion()\\\"> <div class=\\\"card-header\\\"> <h3 class=\\\"page-title\\\" t=\\\"pages.questions.editTitle\\\"></h3> <div if.to-view=\\\"isSupported\\\" class=\\\"form-group mb-0 ml-auto\\\"> <label class=\\\"form-label\\\" t=\\\"pages.questions.questionType\\\"></label> <select class=\\\"custom-select\\\" change.delegate=\\\"handleQuestionTypeChange()\\\" value.two-way=\\\"question.type\\\"> <option repeat.for=\\\"questionType of supportedQuestionTypes\\\" model.bind=\\\"questionType\\\" t=\\\"globals.questionTypes.questionType${questionType}\\\"></option> </select> </div> <div if.to-view=\\\"isSupported && supportedQuestionMediaTypes.length > 1\\\" class=\\\"form-group mb-0 ml-2\\\"> <label class=\\\"form-label\\\" t=\\\"pages.questions.questionMediaType\\\"></label> <select class=\\\"custom-select\\\" disabled.to-view=\\\"!mediaTypeSelectable\\\" change.trigger=\\\"handleQuestionMediaTypeChange()\\\" value.two-way=\\\"selectedQuestionMediaType\\\"> <option repeat.for=\\\"questionMediaType of supportedQuestionMediaTypes\\\" model.bind=\\\"questionMediaType\\\" t=\\\"pages.questions.${questionMediaType}Label\\\"></option> </select> </div> <div if.to-view=\\\"isSupported && supportedAnswerMediaTypes.length > 1\\\" class=\\\"form-group mb-0 ml-2\\\"> <label class=\\\"form-label\\\" t=\\\"pages.questions.answerMediaType\\\"></label> <select class=\\\"custom-select\\\" disabled.to-view=\\\"!mediaTypeSelectable\\\" change.trigger=\\\"handleAnswerMediaTypeChange()\\\" value.two-way=\\\"selectedAnswerMediaType\\\"> <option repeat.for=\\\"answerMediaType of supportedAnswerMediaTypes\\\" model.bind=\\\"answerMediaType\\\" t=\\\"pages.questions.${answerMediaType}Label\\\"></option> </select> </div> </div> <div class=\\\"card-body\\\"> <div class=\\\"row\\\"> <div class=\\\"col-md-12\\\"> <alert-message if.to-view=\\\"question && !isSupported\\\" message.to-view=\\\"alertMessage\\\" message-style=\\\"alert-danger\\\"></alert-message> <alert-message if.to-view=\\\"isSupported\\\" message.to-view=\\\"{ tk: 'pages.questions.editingWarning' }\\\" message-style.to-view=\\\"`alert-danger`\\\"></alert-message> <section if.to-view=\\\"isSupported && question.type !== QuestionType.LT\\\"> <edit-question-form question-media-type.to-view=\\\"selectedQuestionMediaType\\\" original-question.to-view=\\\"originalQuestion\\\" question.two-way=\\\"question\\\" course-id.to-view=\\\"courseId\\\"></edit-question-form> </section> <label if.to-view=\\\"isTrueOrFalseQuestionWithText\\\" class=\\\"custom-control custom-checkbox custom-control-inline form-check mt-6\\\"> <input class=\\\"custom-control-input\\\" type=\\\"checkbox\\\" change.delegate=\\\"resetAnswerTexts()\\\" checked.two-way=\\\"answerAsText\\\"> <span class=\\\"custom-control-label\\\" t=\\\"pages.questions.answerAsText\\\"></span> </label> <section show.to-view=\\\"isSupported\\\" repeat.for=\\\"answer of answers\\\"> <edit-lt-question-form if.to-view=\\\"question.type === QuestionType.LT\\\" text.two-way=\\\"ltQuestionParts[$index]\\\" help-text.two-way=\\\"ltHelpTextParts[$index]\\\" learning-only.two-way=\\\"question.learningOnly\\\"></edit-lt-question-form> <div class=\\\"row\\\"> <div class=\\\"col-6\\\"> <edit-answer-form answer-media-type.to-view=\\\"selectedAnswerMediaType\\\" show-text.to-view=\\\"!isTrueOrFalseQuestionWithText || answerAsText\\\" text.two-way=\\\"answer.text1\\\" picture-id.two-way=\\\"answer.picture1Id\\\" audio-id.two-way=\\\"answer.audio1Id\\\" is-correct.to-view=\\\"answer.correct1\\\" audio-url.two-way=\\\"originalAnswers[$index].audioUrl1\\\" picture-url.two-way=\\\"originalAnswers[$index].picture1.url\\\" course-id.to-view=\\\"courseId\\\" answer-checked.trigger=\\\"handleAnswerChecked('correct1', $index, $event)\\\" letter=\\\"A\\\"></edit-answer-form> </div> <div class=\\\"col-6\\\"> <edit-answer-form answer-media-type.to-view=\\\"selectedAnswerMediaType\\\" show-text.to-view=\\\"!isTrueOrFalseQuestionWithText || answerAsText\\\" text.two-way=\\\"answer.text2\\\" picture-id.two-way=\\\"answer.picture2Id\\\" audio-id.two-way=\\\"answer.audio2Id\\\" is-correct.to-view=\\\"answer.correct2\\\" audio-url.two-way=\\\"originalAnswers[$index].audioUrl2\\\" picture-url.two-way=\\\"originalAnswers[$index].picture2.url\\\" course-id.to-view=\\\"courseId\\\" answer-checked.trigger=\\\"handleAnswerChecked('correct2', $index, $event)\\\" letter=\\\"B\\\"></edit-answer-form> </div> </div> <div if.to-view=\\\"question.type !== QuestionType.TF\\\" class=\\\"row\\\"> <div class=\\\"col-6\\\"> <edit-answer-form answer-media-type.to-view=\\\"selectedAnswerMediaType\\\" text.two-way=\\\"answer.text3\\\" picture-id.two-way=\\\"answer.picture3Id\\\" audio-id.two-way=\\\"answer.audio3Id\\\" is-correct.to-view=\\\"answer.correct3\\\" audio-url.two-way=\\\"originalAnswers[$index].audioUrl3\\\" picture-url.two-way=\\\"originalAnswers[$index].picture3.url\\\" course-id.to-view=\\\"courseId\\\" answer-checked.trigger=\\\"handleAnswerChecked('correct3', $index, $event)\\\" letter=\\\"C\\\"></edit-answer-form> </div> <div class=\\\"col-6\\\"> <edit-answer-form answer-media-type.to-view=\\\"selectedAnswerMediaType\\\" text.two-way=\\\"answer.text4\\\" picture-id.two-way=\\\"answer.picture4Id\\\" audio-id.two-way=\\\"answer.audio4Id\\\" is-correct.to-view=\\\"answer.correct4\\\" audio-url.to-view=\\\"originalAnswers[$index].audioUrl4\\\" picture-url.to-view=\\\"originalAnswers[$index].picture4.url\\\" course-id.to-view=\\\"courseId\\\" answer-checked.trigger=\\\"handleAnswerChecked('correct4', $index, $event)\\\" letter=\\\"D\\\"></edit-answer-form> </div> </div> </section> <alert-message if.to-view=\\\"errorTk\\\" message.to-view=\\\"{ tk: errorTk }\\\" message-style=\\\"alert-danger\\\"></alert-message> </div> </div> </div> <div class=\\\"card-footer\\\"> <div class=\\\"row\\\"> <div class=\\\"col-6\\\"> <div class=\\\"text-muted small edit-question-dialog__attribution\\\" t=\\\"pages.questions.addedAt\\\" t-params.to-view=\\\"{ date: createdAt, creatorName: creatorName }\\\"></div> <div class=\\\"text-muted small pt-1 edit-question-dialog__attribution\\\" t=\\\"pages.questions.editedAt\\\" t-params.to-view=\\\"{ date: editedAt, editorName: editorName }\\\"></div> </div> <div class=\\\"col-6 text-right\\\"> <button class=\\\"btn btn-primary\\\" type=\\\"submit\\\" t=\\\"pages.questions.saveQuestion\\\"></button> <button class=\\\"btn btn-secondary\\\" click.delegate=\\\"showPreview()\\\" t=\\\"pages.questions.showPreview\\\"></button> <button t=\\\"globals.cancel\\\" class=\\\"btn btn-secondary\\\" click.delegate=\\\"controller.cancel()\\\"></button> </div> </div> </div> </form> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <form if.to-view=\\\"value\\\" submit.trigger=\\\"ok()\\\" class=\\\"card edit-student-skill-dialog\\\"> <div class=\\\"card-header\\\"> <h1 class=\\\"card-title\\\" t=\\\"dialogs.editStudentSkill.title\\\"></h1> </div> <div class=\\\"card-body\\\"> <div class=\\\"form-group\\\"> <label class=\\\"form-label\\\" t=\\\"globals.skill\\\"></label> <div class=\\\"edit-student-skill-dialog__skill-field\\\"> <safe-icon class=\\\"edit-student-skill-dialog__skill-icon\\\" icon-name=\\\"Twemoji/${model.skill.iconKey}\\\"></safe-icon> <input class=\\\"form-control edit-student-skill-dialog__skill-input\\\" type=\\\"text\\\" disabled.to-view=\\\"true\\\" value.to-view=\\\"model.skill.title\\\"> </div> </div> <div class=\\\"form-group\\\"> <label class=\\\"form-label\\\" t=\\\"globals.status\\\"></label> <div class=\\\"${ studentHasSkill ? 'input-group' : '' }\\\"> <input type=\\\"text\\\" disabled.to-view=\\\"true\\\" class=\\\"form-control\\\" t=\\\"[value]${statusTk}\\\"> <span class=\\\"input-group-append\\\"> <button-standard if.to-view=\\\"studentHasSkill\\\" click.trigger=\\\"onUnassignSkillClick()\\\" t=\\\"[btnText]globals.revoke\\\" btn-color=\\\"primary\\\"></button-standard> </span> </div> </div> <div class=\\\"two-column-grid\\\"> <div> <div class=\\\"form-group\\\"> <label class=\\\"form-label\\\" t=\\\"globals.validUntil\\\"></label> <date-input value.two-way=\\\"value.overrideExpirationDate\\\" disabled.to-view=\\\"unlimitedValidity || devalidate\\\" click.trigger=\\\"resetErrorTkByKey('validUntil')\\\" is-invalid.to-view=\\\"!!errorTks.validUntil\\\" mode=\\\"date\\\"></date-input> <div if.to-view=\\\"errorTks.validUntil\\\" class=\\\"invalid-feedback d-flex\\\" t=\\\"${errorTks.validUntil}\\\"></div> </div> <div class=\\\"form-group\\\"> <label class=\\\"form-label\\\" t=\\\"elements.studentSkillsTable.extensionFrom\\\"></label> <date-input value.two-way=\\\"value.overrideRenewabilityDate\\\" disabled.to-view=\\\"unlimitedValidity || devalidate\\\" click.trigger=\\\"resetErrorTkByKey('offerExtensionFrom')\\\" is-invalid.to-view=\\\"!!errorTks.offerExtensionFrom\\\" mode=\\\"date\\\"></date-input> <div if.to-view=\\\"errorTks.offerExtensionFrom\\\" class=\\\"invalid-feedback d-flex\\\" t=\\\"${errorTks.offerExtensionFrom}\\\"></div> </div> </div> <label class=\\\"custom-control custom-checkbox custom-control-inline form-check mt-6 edit-student-skill-dialog__checkbox\\\"> <input class=\\\"custom-control-input\\\" type=\\\"checkbox\\\" disabled.to-view=\\\"devalidate\\\" change.delegate=\\\"resetErrorTks()\\\" checked.two-way=\\\"unlimitedValidity\\\"> <span class=\\\"custom-control-label\\\" t=\\\"globals.unlimited\\\"></span> </label> </div> </div> <div class=\\\"card-footer text-right\\\"> <button class=\\\"btn btn-primary\\\" type=\\\"submit\\\" t=\\\"globals.save\\\"></button> <button class=\\\"btn btn-secondary\\\" click.delegate=\\\"cancel()\\\" t=\\\"globals.cancel\\\"></button> </div> </form> </template>\";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <form submit.trigger=\\\"ok()\\\" class=\\\"edit-tile-dialog\\\"> <div class=\\\"card\\\"> <div class=\\\"card-header\\\"> <h3 class=\\\"card-title\\\" t=\\\"dialogs.editTileDialog.${ tileInfo.title || tileInfo.subtitle ? 'editTitle' : 'createTitle' }\\\"></h3> </div> <div class=\\\"card-body\\\"> <div class=\\\"row\\\"> <div class=\\\"col-12\\\"> <div class=\\\"form-group\\\"> <label class=\\\"form-label\\\" t=\\\"globals.title\\\"></label> <input type=\\\"text\\\" class=\\\"form-control\\\" attach-focus value.two-way=\\\"title\\\"> </div> </div> </div> <div class=\\\"row\\\"> <div class=\\\"col-12\\\"> <div class=\\\"form-group\\\"> <label class=\\\"form-label\\\" t=\\\"dialogs.editTileDialog.subTitle\\\"></label> <input type=\\\"text\\\" class=\\\"form-control\\\" value.two-way=\\\"subtitle\\\"> </div> </div> </div> <template if.to-view=\\\"tileInfo\\\"> <picture-select-form t=\\\"[title]dialogs.editTileDialog.picture\\\" picture-id.two-way=\\\"tileInfo.pictureId\\\" picture.two-way=\\\"picture\\\" picture-url.two-way=\\\"picture.url\\\"></picture-select-form> <audio-select-form t=\\\"[title]dialogs.editTileDialog.audio\\\" audio-id.two-way=\\\"tileInfo.audioId\\\" show-title.to-view=\\\"false\\\" audio-url.two-way=\\\"audioUrl\\\"></audio-select-form> </template> </div> <div class=\\\"card-footer text-right\\\"> <button type=\\\"submit\\\" class=\\\"btn btn-primary\\\" t=\\\"globals.save\\\"></button> <button class=\\\"btn btn-secondary\\\" click.trigger=\\\"controller.cancel()\\\" t=\\\"globals.cancel\\\"></button> </div> </div> </form> </template>\";\n// Exports\nexport default code;","import { IImageCropperData, ImageCropperUpdateEvent } from 'common/aurelia-components/image-cropper/image-cropper';\nimport { FullWidthCustomDialog } from '../../../services/CustomDialog';\n\nexport class CropImageDialog extends FullWidthCustomDialog {\n  public model!: ICropImageDialogModel;\n  public value!: IImageCropperData | null;\n\n  public initValue(model: ICropImageDialogModel): void {\n    this.value = model?.initialData ?? null;\n  }\n\n  private handleCropUpdate(event: ImageCropperUpdateEvent): void {\n    this.value = event.detail;\n  }\n}\n\nexport interface ICropImageDialogModel {\n  imageUrl: string;\n  initialData?: IImageCropperData;\n  useWideScreenViewPort?: boolean;\n}","import { DialogController } from 'aurelia-dialog';\nimport { autoinject } from 'aurelia-framework';\nimport { Router } from 'aurelia-router';\nimport { StoreService } from 'common/aurelia-services/StoreService';\nimport { SubscriptionManagerService } from 'common/aurelia-services/SubscriptionManagerService';\nimport { TermsOfUseApiService } from 'common/aurelia-services/TermsOfUseApiService';\nimport { LoginClient } from 'common/enum/auth.enum';\nimport { AnimationUtils } from 'common/utils/AnimationUtils';\nimport { AppUtils as CommonAppUtils } from 'common/utils/AppUtils';\nimport { assertNotNullOrUndefined } from 'common/utils/assert';\nimport { FocusTrapCustomDialog } from '../../../services/CustomDialog';\nimport { AuthApiService } from '../../../services/api/AuthApiService';\nimport { UserSettingsApiService } from '../../../services/api/UserSettingsApiService';\nimport { logoutAction, setSettingsAction } from '../../../store/actions';\nimport { IState } from '../../../store/state';\n\n@autoinject()\nexport class DataProtectionDialog extends FocusTrapCustomDialog {\n  public model!: IDataProtectionDialogModel;\n  private isOperator = false;\n  private termsOfUseAccepted = false;\n  private showHelpText = false;\n  private loading: boolean = false;\n  private termsOfUseText: string = '';\n  private dataPrivacyAgreementLink = CommonAppUtils.getDataPrivacyAgreementLink();\n  private domButtonElement: HTMLElement | null = null;\n\n  constructor(\n    protected controller: DialogController,\n    private settingsApi: UserSettingsApiService,\n    private authApi: AuthApiService,\n    private router: Router,\n    private storeService: StoreService<IState>,\n    private termsOfUseApiService: TermsOfUseApiService,\n    element: Element,\n    subscriptionManagerService: SubscriptionManagerService\n  ) {\n    super(controller, element, subscriptionManagerService);\n  }\n\n  public initValue(model: IDataProtectionDialogModel): void {\n    this.isOperator = model.isOperator;\n    this.updateTermsOfUse();\n  }\n\n  protected async ok(): Promise<void> {\n    await this.settingsApi.updateSettings({ isDataPrivacyAgreementAccepted: true });\n    const settings = await this.settingsApi.getSettings();\n    this.storeService.dispatch(setSettingsAction, settings);\n    this.controller.ok();\n  }\n\n  private async updateTermsOfUse(): Promise<void> {\n    this.loading = true;\n    const termsOfUse = await this.termsOfUseApiService.getTermsOfUse(LoginClient.AdminFrontend);\n    this.termsOfUseText = termsOfUse.text;\n    this.loading = false;\n  }\n\n  private async signOut(): Promise<void> {\n    try {\n      await this.authApi.logout();\n    } catch(e) {\n      console.error('Error reaching logout endpoint');\n    }\n    this.controller.ok();\n    this.storeService.dispatch(logoutAction);\n    this.router.navigateToRoute('login');\n  }\n\n  private toggleTermsOfUseAccepted(): void {\n    this.termsOfUseAccepted = !this.termsOfUseAccepted;\n  }\n\n  private toggleShowHelpText(): void {\n    if(!this.isOperator || this.termsOfUseAccepted) {\n      this.showHelpText = false;\n      return;\n    }\n    this.showHelpText = !this.showHelpText;\n  }\n\n  private highlightButton(): void {\n    if(!this.isOperator || this.termsOfUseAccepted) return;\n    assertNotNullOrUndefined(this.domButtonElement, 'forgot to ref domButtonElement');\n    AnimationUtils.colorFlash(this.domButtonElement, { backgroundColor: 'var(--data-protection-dialog-flash-highlight)' });\n  }\n}\n\nexport interface IDataProtectionDialogModel {\n  isOperator: boolean;\n}","import { autoinject } from 'aurelia-framework';\nimport { DialogController } from 'aurelia-dialog';\n\n@autoinject()\nexport class DeleteConfirmationDialog {\n  private textTk = 'dialogs.deleteConfirmation.noUndoWarning';\n\n  constructor(protected controller: DialogController) { }\n\n  protected activate(model: IDeleteConfirmationDialogModel): void {\n    this.textTk = model.textTk ?? this.textTk;\n  }\n}\n\ninterface IDeleteConfirmationDialogModel {\n  textTk?: string;\n}\n","import { DefaultBrowserBehavior } from 'amazon-chime-sdk-js';\nimport { DialogController } from 'aurelia-dialog';\nimport { autoinject, computedFrom, observable } from 'aurelia-framework';\nimport { ILoadingOverlayStatus, LoadingOverlayType } from 'common/aurelia-components/loading-overlay/loading-overlay';\nimport { ChimeService, IDeviceErrors } from 'common/aurelia-services/ChimeService';\nimport { StoreService } from 'common/aurelia-services/StoreService';\nimport * as ConferenceContextMutations from 'common/store/mutations/conference.context.mutations';\nimport {\n  setConferenceStreamAudio,\n  setConferenceStreamVideo,\n  setSelectedAudioInputDevice,\n  setSelectedAudioOutputDevice,\n  setSelectedVideoInputDevice\n} from 'common/store/mutations/distinar.mutations';\nimport { AppUtils } from 'common/utils/AppUtils';\nimport { assertNotNullOrUndefined } from 'common/utils/assert';\nimport { CustomDialog } from '../../../services/CustomDialog';\nimport { IState } from '../../../store/state';\n\n@autoinject()\nexport class DistinarStartConferenceDialog extends CustomDialog {\n  public model!: IDistinarStartConferenceDialogModel;\n\n  @observable() private selectedMicrophone: MediaDeviceInfo | null = null;\n  @observable() private selectedCamera: MediaDeviceInfo | null = null;\n  @observable() private selectedAudioOutput: MediaDeviceInfo | null = null;\n\n  private microphoneActivated = false;\n  @observable() private cameraActivated = false;\n\n  private microphoneSelectionError = false;\n  private microphonesLoading = false;\n\n  private audioOutputSelectionError = false;\n  private audioOutputsLoading = false;\n\n  private cameraSelectionError = false;\n  private camerasLoading = false;\n\n  private loadingStatusMessages: Array<ILoadingOverlayStatus> = [{\n    textTk: 'dialogs.distinarStartConference.waitForBrowserPermissionDialog',\n    type: LoadingOverlayType.Info,\n  }];\n\n  private previewVideoElement: HTMLVideoElement | null = null;\n  private showPreviewVideo: boolean = false;\n\n  private isLeavingConference = false;\n  private isStartingConference = false;\n\n  private supportsOutputDevice = new DefaultBrowserBehavior().supportsSetSinkId();\n  private appName = AppUtils.getAppName();\n\n  constructor(\n    private element: Element,\n    dialogController: DialogController,\n    private storeService: StoreService<IState>,\n    private chimeService: ChimeService,\n  ) {\n    super(dialogController);\n  }\n\n  public initValue(model: IDistinarStartConferenceDialogModel): void {\n    this.model = model;\n  }\n\n  protected async ok(): Promise<void> {\n    this.loadingStatusMessages = [];\n    this.isStartingConference = true;\n    await this.stopVideoPreview();\n\n    await this.storeService\n      .pipe(setConferenceStreamAudio, this.microphoneActivated)\n      .pipe(setConferenceStreamVideo, this.cameraActivated)\n      .pipe(setSelectedAudioInputDevice, this.selectedMicrophone)\n      .pipe(setSelectedAudioOutputDevice, this.selectedAudioOutput)\n      .pipe(setSelectedVideoInputDevice, this.selectedCamera)\n      .dispatch();\n\n    const deviceErrors = await this.model.startConferenceCallback();\n\n    if(deviceErrors != null) {\n      this.audioOutputSelectionError = !!deviceErrors.audioOutput;\n      this.microphoneSelectionError = !!deviceErrors.audioInput;\n      this.cameraSelectionError = !!deviceErrors.videoInput;\n      this.isStartingConference = false;\n      return;\n    }\n\n    super.ok();\n    this.isStartingConference = false;\n  }\n\n  protected async cancel(): Promise<void> {\n    this.loadingStatusMessages = [];\n    this.isLeavingConference = true;\n    this.stopVideoPreview();\n\n    await this.storeService.dispatch(ConferenceContextMutations.resetConferenceState);\n    super.cancel();\n    this.isLeavingConference = false;\n  }\n\n  private async selectedCameraChanged(): Promise<void> {\n    if(!this.element.isConnected && this.selectedCamera == null) return;\n\n    await this.storeService.dispatch(setSelectedVideoInputDevice, this.selectedCamera);\n    if(this.cameraActivated) this.startVideoPreview();\n    else this.stopVideoPreview();\n  }\n\n  private cameraActivatedChanged(): void {\n    if(!this.element.isConnected) return;\n\n    if(this.cameraActivated) this.startVideoPreview();\n    else this.stopVideoPreview();\n  }\n\n  @computedFrom('microphonesLoading', 'camerasLoading', 'audioOutputsLoading', 'isStartingConference', 'isLeavingConference')\n  private get loading(): boolean {\n    return this.isStartingConference\n      || this.isLeavingConference\n      || this.microphonesLoading\n      || this.camerasLoading\n      || this.audioOutputsLoading;\n  }\n\n  private async startVideoPreview(): Promise<void> {\n    const deviceId = (await this.storeService.getState()).distinar.conferenceConfiguration.selectedDevices.videoInput?.deviceId;\n    if(deviceId == null) return;\n    await this.chimeService.chooseVideoInputDevice(deviceId);\n\n    assertNotNullOrUndefined(this.previewVideoElement, 'previewVideoElement cannot be null or undefined');\n    this.chimeService.startVideoPreviewForVideoInput(this.previewVideoElement);\n    this.showPreviewVideo = true;\n  }\n\n  private async stopVideoPreview(): Promise<void> {\n    assertNotNullOrUndefined(this.previewVideoElement, 'previewVideoElement cannot be null or undefined');\n\n    this.chimeService.stopVideoPreviewForVideoInput(this.previewVideoElement);\n    await this.chimeService.chooseVideoInputDevice(null, false);\n    this.showPreviewVideo = false;\n  }\n}\n\nexport interface IDistinarStartConferenceDialogModel {\n  startConferenceCallback: () => Promise<IDeviceErrors | null>;\n}\n","import { DialogController } from 'aurelia-dialog';\nimport { autoinject } from 'aurelia-framework';\nimport { ECodeDownloadOption } from 'common/enum/assessment.enum';\nimport { CustomDialog } from '../../../services/CustomDialog';\n\n@autoinject()\nexport class DownloadAssessmentCodesDialog extends CustomDialog {\n  public value: ECodeDownloadOption[] | null = null;\n  private downloadOptions: IDownloadOption[] = Object\n    .values(ECodeDownloadOption)\n    .map(option => ({ selected: false, value: option }));\n  private isAnyOptionSelected = false;\n\n  constructor(\n    protected controller: DialogController,\n  ) {\n    super(controller);\n  }\n\n  private downloadOptionsChanged(): void {\n    this.isAnyOptionSelected = this.downloadOptions.some(option => option.selected);\n  }\n\n  private saveSelection(): void {\n    const selectedOptions = this.downloadOptions\n      .filter(dO => dO.selected)\n      .map(dO => dO.value);\n    this.value = selectedOptions.length ? selectedOptions : null;\n    this.controller.ok(this.value);\n  }\n}\n\ninterface IDownloadOption {\n  selected: boolean;\n  value: ECodeDownloadOption;\n}\n","import { DialogController } from 'aurelia-dialog';\nimport { autoinject, bindable, observable } from 'aurelia-framework';\nimport { RichTextChangedEvent } from 'common/aurelia-components/text-editor/text-editor';\nimport { ICreateAnnouncementDto } from 'common/dto/announcement/announcement.interface';\nimport { LogUtils } from 'common/utils/LogUtils';\nimport { cloneDeep } from 'lodash';\nimport { CustomDialog } from '../../../services/CustomDialog';\nimport { AudienceApiService } from '../../../services/api/AudienceApiService';\nimport { IErrorTk, ValidationUtils } from '../../../utils/ValidationUtils';\nimport { INavigationTab } from '../../elements/navigation-tabs/navigation-tabs';\n\nenum EditAnnouncementTabs {\n  Compose = 'compose',\n  Audience = 'audience',\n}\n\nconst MAX_ANNOUNCEMENT_TITLE_LENGTH = 255;\nconst MAX_ANNOUNCEMENT_TEXT_LENGTH = 5000;\n\n@autoinject()\nexport class EditAnnouncementDialog extends CustomDialog {\n  private initialized = false;\n  @observable() private audienceFilterTenants: Array<number> = [];\n  @observable() private audienceFilterCourses: Array<number> = [];\n\n  @bindable() public editorContext = 'announcement-dialog-text';\n\n  public value: ICreateAnnouncementDto<string> | null = null;\n  public id: number | undefined = undefined;\n  public tenantId: number | null = null;\n  public model!: IEditAnnouncementDialogModel;\n\n  private EditAnnouncementTabs = EditAnnouncementTabs;\n  private activeTab = EditAnnouncementTabs.Compose;\n  private navigationTabs: INavigationTab[] = [];\n\n  private editExisting = false;\n  private editingDisabled = false;\n  private audienceCount: number = 0;\n\n  private errorTks: IAnnouncementDialogErrorTk = {\n    title: null,\n    text: null,\n    visibleFrom: null,\n    visibleTo: null,\n    linkText: null,\n    linkLink: null,\n  };\n\n  constructor(\n    dialogController: DialogController,\n    private audienceApiService: AudienceApiService,\n  ) {\n    super(dialogController);\n  }\n\n  public initValue(model: IEditAnnouncementDialogModel): void {\n    this.value = cloneDeep(model.value);\n    this.tenantId = model.tenantId;\n    this.id = model.id;\n    this.audienceCount = model.initialAudienceCount ?? 0;\n    this.editExisting = this.id != null;\n    this.editingDisabled = model.editingDisabled;\n    this.activeTab = EditAnnouncementTabs.Compose;\n    this.updateNavigationTabs();\n    if(this.editExisting) {\n      this.audienceFilterTenants = this.value?.audience?.tenantIds ?? [];\n      this.audienceFilterCourses = this.value?.audience?.courseIds ?? [];\n    }\n    this.initialized = true;\n  }\n\n  private async audienceFilterTenantsChanged(): Promise<void> {\n    this.updateAudienceCount();\n  }\n\n  private async audienceFilterCoursesChanged(): Promise<void> {\n    this.updateAudienceCount();\n  }\n\n  private async updateAudienceCount(): Promise<void> {\n    if(!this.initialized) return;\n    try {\n      const audience = {\n        tenantIds: this.audienceFilterTenants,\n        courseIds: this.audienceFilterCourses,\n      };\n      this.audienceCount = await this.audienceApiService.getAudienceCount(audience);\n      this.updateNavigationTabs();\n    } catch(e) {\n      LogUtils.exception(e);\n      throw e;\n    }\n  }\n\n  private async handleDelete(): Promise<void> {\n    if(await this.model.deleteCallback?.(this.id)) {\n      this.cancel();\n    }\n  }\n\n  private resetDateErrors(): void {\n    this.errorTks.visibleFrom = null;\n    this.errorTks.visibleTo = null;\n  }\n\n  private validateAnnouncement(): boolean {\n    this.errorTks.title = ValidationUtils.checkTitle(this.value?.title, MAX_ANNOUNCEMENT_TITLE_LENGTH);\n    this.errorTks.text = ValidationUtils.checkText(this.value?.text, MAX_ANNOUNCEMENT_TEXT_LENGTH);\n    this.errorTks.linkText = this.value?.link?.url && !this.value.link.name ? 'utils.validation.noTextError' : null;\n    this.errorTks.linkLink = this.value?.link?.name && !this.value.link.url ? 'utils.validation.noLinkError' : null;\n    this.errorTks.visibleFrom = ValidationUtils.checkStartDate(this.value?.visibleFrom, this.value?.visibleTo);\n    this.errorTks.visibleTo = ValidationUtils.checkEndDate(this.value?.visibleFrom, this.value?.visibleTo);\n\n    return !(Object.values(this.errorTks).some(e => e != null));\n  }\n\n  private saveOrCreate(): void {\n    this.activeTab = EditAnnouncementTabs.Compose;\n\n    const validationSuccess = this.validateAnnouncement();\n    if(!validationSuccess || this.value == null) return;\n    this.value.audience = { tenantIds: this.audienceFilterTenants, courseIds: this.audienceFilterCourses };\n    if(this.editExisting && this.model.updateCallback != null) {\n      this.model.updateCallback(this.value, this.id);\n    } else if(this.model.createCallback != null) {\n      this.model.createCallback(this.value);\n    }\n    this.ok();\n  }\n\n  private updateNavigationTabs(): void {\n    this.navigationTabs = [\n      {\n        title: { titleTk: this.editingDisabled ? 'globals.details' : 'pages.announcements.tabCompose' },\n        active: this.activeTab === EditAnnouncementTabs.Compose,\n        callback: () => { this.activeTab = EditAnnouncementTabs.Compose; },\n      },\n      {\n        title: { titleTk: 'pages.announcements.tabAudience', titleTkParams: { 'count': this.audienceCount } },\n        active: this.activeTab === EditAnnouncementTabs.Audience,\n        callback: () => { this.activeTab = EditAnnouncementTabs.Audience; },\n      },\n    ];\n  }\n\n  private handleRichTextChanged(event: RichTextChangedEvent): void {\n    const { value } = event.detail;\n    if(!this.value) return;\n    this.value.text = value == null ? '' : JSON.stringify(value);\n  }\n}\n\nexport interface IEditAnnouncementDialogModel {\n  value: ICreateAnnouncementDto<string> | null;\n  tenantId: number | null;\n  editingDisabled: boolean;\n  id?: number;\n  initialAudienceCount?: number;\n  createCallback?: (value: ICreateAnnouncementDto<string>) => Promise<boolean | null>;\n  updateCallback?: (value: ICreateAnnouncementDto<string>, id: number | undefined) => Promise<boolean | null>;\n  deleteCallback?: (id: number | undefined) => Promise<boolean>;\n}\n\ninterface IAnnouncementDialogErrorTk {\n  title: IErrorTk | null;\n  text: IErrorTk | null;\n  visibleFrom: string | null;\n  visibleTo: string | null;\n  linkText: string | null;\n  linkLink: string | null;\n}\n","import { IBlockEditResponseDto } from 'common/dto/course/management/course.management.interface';\nimport { BlockType } from 'common/dto/course/content/course.types';\nimport { CustomDialog } from '../../../services/CustomDialog';\n\nexport class EditBlockPreviewDialog extends CustomDialog {\n  public value!: IBlockEditResponseDto<string>;\n  private BlockType = BlockType;\n\n  public activate(model: IEditBlockPreviewDialogValue): void {\n    super.activate(model);\n    this.value = model.block;\n  }\n}\n\nexport interface IEditBlockPreviewDialogValue {\n  block: IBlockEditResponseDto<string>;\n  courseId: number;\n}","import { DialogController } from 'aurelia-dialog';\nimport { autoinject, computedFrom, observable } from 'aurelia-framework';\nimport { I18N } from 'aurelia-i18n';\nimport { CodeStatus } from 'common/dto/course/code/course.code.interface';\nimport { EventType } from 'common/dto/course/content/course.types';\nimport { ICourseGroupResponseDto } from 'common/dto/course/course.group.interface';\nimport { ICourseEditResponseDto } 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 { ITenantAddressWithIdDto } from 'common/dto/other/address.interface';\nimport { ICreateEventDto, IUpdateEventDto } from 'common/dto/tenant/event/tenant.event.interface';\nimport { ITenantUserInfoDto } from 'common/dto/tenant/other/tenant.user.info.interface';\nimport { ArrayUtils } from 'common/utils/ArrayUtils';\nimport { DateUtils } from 'common/utils/DateUtils';\nimport { NicknameUtils } from 'common/utils/NicknameUtils';\nimport { cloneDeep } from 'lodash';\nimport Moment from 'moment';\nimport { LECTURE_DURATION_IN_MINUTES } from '../../../globals/defaultValues';\nimport { CustomDialog } from '../../../services/CustomDialog';\nimport { CustomDialogService } from '../../../services/CustomDialogService';\nimport { DateService } from '../../../services/DateService';\nimport { LocalCourseUser } from '../../../services/InvitationService';\nimport { InvitationApiService } from '../../../services/api/InvitationApiService';\nimport { ValidationUtils } from '../../../utils/ValidationUtils';\nimport { VirtualFilterField } from '../../elements/custom-table/custom-table-helpers';\nimport { MultiSelectComboBoxSelectedItemChangedEvent } from '../../elements/multi-select-combo-box/multi-select-combo-box';\nimport { EntrySelectedEvent, ITextTagsWidgetTableColumn, SearchStringChangedEvent, TagsEditedEvent } from '../../elements/text-tags-widget/text-tags-widget';\n\n\n@autoinject()\nexport class EditEventDialog extends CustomDialog {\n  public model!: IEditEventDialogModel;\n  public value!: ICreateEventDto<string> | IUpdateEventDto<string>;\n  private courses: ICourseEditResponseDto[] = [];\n  private courseGroups: ICourseGroupResponseDto<string>[] = [];\n  private lecturers: ITenantUserInfoDto[] = [];\n\n  private startDateInPast = false;\n\n  private selectedCourses: ICourseEditResponseDto[] = [];\n  private selectedCourseGroups: ICourseGroupResponseDto<string>[] = [];\n  private selectedUsers: LocalCourseUser[] = [];\n\n  private allUsers: LocalCourseUser[] = [];\n  private foundUsers: LocalCourseUser[] = [];\n  private userTableHeaders: string[] = [this.i18N.tr('globals.firstname'), this.i18N.tr('globals.lastname'), this.i18N.tr('globals.nickname')];\n  private userTableColumns: ITextTagsWidgetTableColumn<LocalCourseUser>[] = [\n    {\n      transformer: e => e.firstname ?? '-'\n    },\n    {\n      transformer: e => e.lastname ?? '-'\n    },\n    {\n      transformer: e => e.nickname ?? '-'\n    },\n  ];\n\n  private selectedCourseGroupNamesByCourseTitles: Map<string, string[]> = new Map();\n\n  private eventStartTimeString: string = '';\n  private eventEndTimeString: string = '';\n  @observable() private plannedDateFrom: string | undefined = undefined;\n\n  private errorTks: IErrorTk = {\n    courseGroup: null,\n    users: null,\n    eventDate: null,\n    title: null,\n    description: null,\n    endTimeBeforeStartTime: null,\n    lecturer: null,\n  };\n\n  constructor(\n    controller: DialogController,\n    protected dateService: DateService,\n    protected customDialogService: CustomDialogService,\n    private i18N: I18N,\n    private invitationApi: InvitationApiService,\n  ) {\n    super(controller);\n  }\n\n  public async initValue(model: IEditEventDialogModel): Promise<void> {\n    this.value = cloneDeep(model.value);\n    this.value.notifyOfChanges = model.createNew;\n\n    this.courses = ArrayUtils.immutableSort(model.availableCourses, (a, b) => a.title.localeCompare(b.title));\n    this.plannedDateFrom = model.value.plannedDateFrom;\n\n    // set to already existing time or preset to start ~now and to end a average lecture duration later\n    this.eventStartTimeString = model.value.plannedDateFrom ?? this.dateService.roundToNearestMinutesISOString(5);\n    this.eventEndTimeString = model.value.plannedDateTo ?? Moment(this.eventStartTimeString).add(LECTURE_DURATION_IN_MINUTES, 'minutes').toISOString();\n\n    this.selectedCourses = this.courses.filter(course => this.value.courseIds && this.value.courseIds.includes(course.id));\n    this.lecturers = ArrayUtils.immutableSort(model.availableLecturers, (a, b) => {\n      const nameA = NicknameUtils.getFullName(a);\n      const nameB = NicknameUtils.getFullName(b);\n      return nameA.localeCompare(nameB);\n    });\n\n    const coursesToGetGroupsFor = !this.selectedCourses.length ? this.courses : this.selectedCourses;\n    this.courseGroups = await this.getSortedCourseGroups(coursesToGetGroupsFor, !this.model.createNew || this.plannedDateFrom == null ? null : this.plannedDateFrom);\n    const selectedCourseGroups = this.courseGroups.filter(courseGroup => this.value.courseGroupIds && this.value.courseGroupIds.includes(courseGroup.id));\n    this.setSelectedCourseGroups(selectedCourseGroups);\n\n    this.updateUsers();\n  }\n\n  protected async ok(): Promise<void> {\n    const value = {\n      ...this.value,\n      plannedDateFrom: this.plannedDateFrom,\n    };\n    if(!this.checkEvent(value) || !this.plannedDateFrom) return;\n\n    this.value.plannedDateFrom = this.dateService.combineDateAndTime(this.plannedDateFrom, this.eventStartTimeString);\n    this.value.plannedDateTo = this.dateService.combineDateAndTime(this.plannedDateFrom, this.eventEndTimeString);\n\n    this.startDateInPast = Moment(this.value.plannedDateFrom).endOf('minute').isBefore();\n    // only warn if the start date has changed\n    const warnStartDateInPast = (this.startDateInPast\n      && this.model.value.plannedDateFrom === undefined)\n      || (\n        this.startDateInPast\n        && !Moment(this.value.plannedDateFrom).isSame(Moment(this.model.value.plannedDateFrom)));\n\n    if(warnStartDateInPast) {\n      const confirmed = await this.customDialogService.confirmAction({\n        icon: { name: 'alert-triangle', color: 'var(--warning)' },\n        titleTk: 'dialogs.editEvent.warningTitle',\n        textTk: 'dialogs.editEvent.startDateInPastWarning',\n        confirmTk: 'globals.save',\n      });\n      if(!confirmed) return;\n    }\n\n    this.value.courseIds = ArrayUtils.mapUnique(this.selectedCourseGroups, courseGroup => courseGroup.course.id);\n    this.value.courseGroupIds = this.selectedCourseGroups.map(courseGroup => courseGroup.id);\n    this.value.userIds = this.selectedUsers.map(user => user.id);\n    this.controller.ok(this.value);\n  }\n\n  private async handleSelectedCoursesChanged(event: MultiSelectComboBoxSelectedItemChangedEvent<ICourseEditResponseDto>): Promise<void> {\n    const selectedCourses = event.detail.selectedItems.length ? event.detail.selectedItems : this.courses;\n    this.selectedCourses = event.detail.selectedItems;\n    const selectedCourseGroups = this.selectedCourseGroups\n      .filter(selectedGroup => this.selectedCourses.some(selectedCourse => selectedCourse.id === selectedGroup.course.id));\n    this.setSelectedCourseGroups(selectedCourseGroups);\n    // only check date for createNew event mode and if date has already been set\n    const checkDate: string | null = !this.model.createNew || this.plannedDateFrom == null ? null : this.plannedDateFrom;\n    this.courseGroups = await this.getSortedCourseGroups(selectedCourses, checkDate);\n  }\n\n  private async handleSelectedCourseGroupsChanged(event: MultiSelectComboBoxSelectedItemChangedEvent<ICourseGroupResponseDto<string>>): Promise<void> {\n    this.resetErrorTkByKey('courseGroup');\n    this.resetErrorTkByKey('users');\n\n    this.setSelectedCourseGroups(event.detail.selectedItems);\n    // users already in selected courseGroups can't be selected independently\n    await this.updateUsers();\n\n    // only allowed to add courses, do not remove existing ones! -> KL-5568\n    this.selectedCourses = this.courses\n      .filter(course => this.selectedCourses.some(selectedCourse => selectedCourse.id === course.id)\n        || this.selectedCourseGroups.some(selectedCourseGroup => selectedCourseGroup.course.id === course.id));\n  }\n\n  /**\n   * event must have plannedDate, title and description\n   * event must have a lecturer\n   * endTime can't be before startTime\n   * at least one courseGroup or one student has to be selected\n   */\n  private checkEvent(event: ICreateEventDto<string> | IUpdateEventDto<string>): boolean {\n    this.errorTks.endTimeBeforeStartTime = ValidationUtils.isEndTimeBeforeStartTime(this.eventStartTimeString, this.eventEndTimeString);\n    this.errorTks.eventDate = ValidationUtils.isDateValid(event.plannedDateFrom);\n    this.errorTks.title = ValidationUtils.checkEventTitle(event.title);\n    this.errorTks.lecturer = ValidationUtils.checkEventLecturer(event.lecturerId);\n    this.errorTks.description = ValidationUtils.checkEventDescription(event.description);\n    this.errorTks.courseGroup = ValidationUtils.checkCourseGroupsAndUsers(this.selectedCourseGroups, this.selectedUsers);\n    this.errorTks.users = ValidationUtils.checkCourseGroupsAndUsers(this.selectedCourseGroups, this.selectedUsers);\n    return Object.values(this.errorTks).every(value => !value);\n  }\n\n  private async plannedDateFromChanged(): Promise<void> {\n    const selectForCourses = !this.selectedCourses.length ? this.courses : this.selectedCourses;\n    this.courseGroups = await this.getSortedCourseGroups(selectForCourses, !this.model.createNew || this.plannedDateFrom == null ? null : this.plannedDateFrom);\n  }\n\n  private resetErrorTkByKey(key: keyof IErrorTk): void {\n    this.errorTks[key] = null;\n  }\n\n  private async getSortedCourseGroups(courses: ICourseEditResponseDto[], date: string | null): Promise<ICourseGroupResponseDto<string>[]> {\n    // plannedDateFrom comes in a bad format for filtering, need to correct for timezone (incl daylight saving) offset:\n    const cleanDate = date ? DateUtils.correctDateByTimezoneOffset(date) : null;\n    const courseGroups = await this.model.updateCourseGroupsDelegate(courses.map(course => course.id), cleanDate);\n\n    /**\n     * for non initial selection update: some courseGroups might not be available anymore, that previously were.\n     * filter courseGroups (and not selectedCourseGroups) - to ensure object comparison works in\n     * multi-select-combo-box and the proper selected items get their checked mark\n     */\n    const selectedCourseGroups = courseGroups\n      .filter(availableGroup => this.selectedCourseGroups.some(selectedGroup => selectedGroup.id === availableGroup.id));\n    this.setSelectedCourseGroups(selectedCourseGroups);\n\n    return ArrayUtils.immutableSort(courseGroups, (a, b) => a.name.localeCompare(b.name));\n  }\n\n  private async updateUsers(): Promise<void> {\n    const filter: IFilterDto<LocalCourseUser> = {\n      offset: 0,\n      limit: 500,\n      order: [{\n        field: 'lastname',\n        dir: ColumnSortDirection.ASC,\n      }],\n      filter: [\n        {\n          field: 'status' as VirtualFilterField,\n          value: CodeStatus.Active,\n          type: FilterOperationType.Eq\n        }, {\n          field: 'courseGroupId' as VirtualFilterField,\n          value: this.selectedCourseGroups.map(cG => cG.id),\n          type: FilterOperationType.Ne\n        }\n      ]\n    };\n\n    const result = await this.invitationApi.getUsers(filter);\n    this.allUsers = result.rows;\n    this.selectedUsers = this.allUsers.filter(user => this.value.userIds?.includes(user.id));\n    this.updateUserData();\n  }\n\n  private updateUserData(searchString?: string): void {\n    this.foundUsers = this.allUsers.filter(\n      user => searchString == null || searchString.length === 0\n        || user.firstname?.toLocaleLowerCase().includes(searchString.toLocaleLowerCase())\n        || user.lastname?.toLocaleLowerCase().includes(searchString.toLocaleLowerCase())\n        || user.nickname?.toLocaleLowerCase().includes(searchString.toLocaleLowerCase())\n        || this.getFullUserName(user).toLocaleLowerCase().includes(searchString.toLocaleLowerCase())\n    );\n  }\n\n  private handleUserSearchStringChanged(event: SearchStringChangedEvent): void {\n    this.updateUserData(event.detail.searchString.trim());\n  }\n\n  private handleUserEntrySelected(event: EntrySelectedEvent<LocalCourseUser>): void {\n    this.resetErrorTkByKey('courseGroup');\n    this.resetErrorTkByKey('users');\n\n    const selected = event.detail.entry;\n    if(!this.isUserSelected(selected)) {\n      this.selectedUsers.push(selected);\n      this.updateUserData();\n    }\n  }\n\n  protected isUserSelected(student: LocalCourseUser): boolean {\n    return !!this.selectedUsers.find(user => user.id === student.id);\n  }\n\n  private handleUserEntryRemoved(event: TagsEditedEvent): void {\n    this.selectedUsers = this.selectedUsers.filter(user => event.detail.tags.includes(this.getFullUserName(user)));\n    this.updateUserData();\n  }\n\n  private getFullUserName(user: LocalCourseUser): string {\n    return (user.lastname ?? '') + (user.lastname ? ' ' : '') + (user.firstname ?? '');\n  }\n\n  @computedFrom('selectedUsers.length')\n  private get selectedUsersTags(): string[] {\n    return this.selectedUsers.map(user => this.getFullUserName(user));\n  }\n\n  @computedFrom('value.type')\n  private get eventLecturerDesignation(): string {\n    switch(this.value.type) {\n      case EventType.Excercise:\n        return this.i18N.tr('globals.instructor');\n      case EventType.Lecture:\n        return this.i18N.tr('globals.lecturer');\n      default:\n        return this.i18N.tr('globals.contactPerson');\n    }\n  }\n\n  @computedFrom('EventType')\n  private get eventTypeOptions(): { value: EventType; label: string }[] {\n    return Object.values(EventType)\n      .map(type => ({ label: this.i18N.tr(`globals.eventTypes.${type}`), value: type }))\n      .sort((a, b) => a.label.localeCompare(b.label));\n  }\n\n  private setSelectedCourseGroups(courseGroups: ICourseGroupResponseDto<string>[]): void {\n    this.selectedCourseGroups = courseGroups;\n    const courseGroupNamesByCourseTitles = new Map();\n    for(const courseGroup of this.selectedCourseGroups) {\n      const { title: courseTitle } = courseGroup.course;\n      if(!courseGroupNamesByCourseTitles.has(courseTitle)) {\n        courseGroupNamesByCourseTitles.set(courseTitle, []);\n      }\n      const courseGroupNames = courseGroupNamesByCourseTitles.get(courseTitle);\n      courseGroupNames.push(courseGroup.name);\n      courseGroupNamesByCourseTitles.set(courseTitle, courseGroupNames);\n    }\n\n    this.selectedCourseGroupNamesByCourseTitles = courseGroupNamesByCourseTitles;\n  }\n}\n\nexport interface IEditEventDialogModel {\n  createNew: boolean;\n  value: ICreateEventDto<string> | IUpdateEventDto<string>;\n  availableCourses: ICourseEditResponseDto[];\n  availableLecturers: ITenantUserInfoDto[];\n  availableAddresses: ITenantAddressWithIdDto[];\n  updateCourseGroupsDelegate: (selectedCourses: number[], date: string | null) => Promise<ICourseGroupResponseDto<string>[]>;\n}\n\ninterface IErrorTk {\n  courseGroup: string | null;\n  users: string | null;\n  eventDate: string | null;\n  title: string | null;\n  description: string | null;\n  endTimeBeforeStartTime: string | null;\n  lecturer: string | null;\n}\n","import { autoinject } from 'aurelia-framework';\nimport { ILearningJourneyCourseDto, ILearningJourneyDto, IUpdateCourseOfLearningJourneyDto } from 'common/dto/learning-journey/management/learning-journey.management.interface';\nimport { ArrayUtils } from 'common/utils/ArrayUtils';\nimport { CustomDialog } from '../../../services/CustomDialog';\n\n@autoinject()\nexport class EditLearningJourneyCourseDialog extends CustomDialog {\n  public model!: IEditLearningJourneyCourseDialogModel;\n  public value: IUpdateCourseOfLearningJourneyDto | null = null;\n\n  private errorTk: string | null = null;\n\n  private availableLearningJourneyCourses: ILearningJourneyCourseDto[] = [];\n  private selectedLearningJourneyCourses: ILearningJourneyCourseDto[] = [];\n\n  public initValue(model: IEditLearningJourneyCourseDialogModel): void {\n    this.value = {\n      courseId: model.learningJourneyCourse.courseId,\n      required: model.learningJourneyCourse.required,\n      learningJourneyCourseToCompleteIds: model.learningJourneyCourse.learningJourneyCourseToCompleteIds\n    };\n    this.getAvailableLearningJourneyCourses();\n  }\n\n  protected ok(): void {\n    if(this.value) {\n      this.value.learningJourneyCourseToCompleteIds = this.selectedLearningJourneyCourses.map(c => c.learningJourneyCourseId);\n\n      if(this.hasCircularReferences(this.value.learningJourneyCourseToCompleteIds)) {\n        this.errorTk = 'dialogs.editLearningJourneyCourseDialog.circularReferenceError';\n        return;\n      }\n    }\n    this.controller.ok(this.value);\n  }\n\n  private getAvailableLearningJourneyCourses(): void {\n    this.availableLearningJourneyCourses = this.model.learningJourney.courses\n      .filter(c => c.learningJourneyCourseId !== this.model.learningJourneyCourse.learningJourneyCourseId)\n      .sort((a, b) => a.order - b.order);\n    this.selectedLearningJourneyCourses = this.availableLearningJourneyCourses.filter(course => this.model.learningJourneyCourse.learningJourneyCourseToCompleteIds.includes(course.learningJourneyCourseId));\n  }\n\n  private hasCircularReferences(learningJourneyCourseToCompleteIds: number[]): boolean {\n    const { learningJourneyCourse } = this.model;\n    const existingDependencyRelations: { courseId: number; courseToCompleteId: number }[] = ArrayUtils.flatten(\n      this.selectedLearningJourneyCourses.map(selectedCourse => {\n        return selectedCourse.learningJourneyCourseToCompleteIds.map(id => ({ courseId: selectedCourse.learningJourneyCourseId, courseToCompleteId: id }));\n      })\n    );\n\n    const newDependencyRelations = learningJourneyCourseToCompleteIds.map(id => ({ courseId: learningJourneyCourse.learningJourneyCourseId, courseToCompleteId: id }));\n    return ArrayUtils.hasCircularReferences([...existingDependencyRelations, ...newDependencyRelations], 'courseId', 'courseToCompleteId');\n  }\n\n  private resetErrorTk(): void {\n    this.errorTk = null;\n  }\n}\n\nexport interface IEditLearningJourneyCourseDialogModel {\n  learningJourneyCourse: ILearningJourneyCourseDto;\n  learningJourney: ILearningJourneyDto;\n}\n","import { DialogController } from 'aurelia-dialog';\nimport { autoinject, computedFrom } from 'aurelia-framework';\nimport { IGetPageResponseDto, IPageEditResponseDto, IUpdatePageDto } from 'common/dto/course/management/course.management.interface';\nimport { ArrayUtils } from 'common/utils/ArrayUtils';\nimport { CourseManagementApiService } from '../../../services/api/CourseManagementApiService';\nimport { CustomDialog } from '../../../services/CustomDialog';\n\n@autoinject()\nexport class EditPageDialog extends CustomDialog {\n  public model!: IEditPageDialogModel;\n  public value: IUpdatePageDto | null = null;\n\n  private parentId: number | null = null;\n  private summary: string = '';\n  private isTrainingOnly: boolean = false;\n  private pageIsRootChapter: boolean = false;\n  private errorTk: string | null = null;\n\n  private availableChapters: IPageEditResponseDto[] = [];\n  private selectedChapters: IPageEditResponseDto[] = [];\n\n  constructor(\n    protected controller: DialogController,\n    private courseManagementApiService: CourseManagementApiService,\n  ) {\n    super(controller);\n  }\n\n  public initValue(model: IEditPageDialogModel): void {\n    this.getPage(model.courseId, model.pageId);\n  }\n\n  private async getPage(courseId: number, pageId: number): Promise<void> {\n    const page = await this.courseManagementApiService.getPage(courseId, pageId);\n    this.value = {\n      summary: page.summary,\n      isTrainingOnly: page.isTrainingOnly,\n      pagesToCompleteIds: page.pagesToCompleteIds\n    };\n    this.parentId = page.parentId;\n    this.summary = page.summary ?? '';\n    this.isTrainingOnly = page.isTrainingOnly;\n    if(!this.isRootChapter(page)) return;\n    this.pageIsRootChapter = true;\n    this.getCourseChapters(courseId, page);\n  }\n\n  private async getCourseChapters(courseId: number, page: IGetPageResponseDto): Promise<void> {\n    const chapters = await this.courseManagementApiService.getPages(courseId);\n\n    this.availableChapters = chapters\n      .filter(p => this.isRootChapter(p) && p.id !== page.id)\n      .sort((a, b) => a.order - b.order);\n    this.selectedChapters = this.availableChapters.filter(c => page.pagesToCompleteIds.includes(c.id));\n  }\n\n  private isRootChapter(page: IPageEditResponseDto | IGetPageResponseDto): boolean {\n    return page.type === 'chapter' && page.parentId === null && !page.isIntro && !page.isTrainingOnly;\n  }\n\n  private handleIsTrainingOnlyChanged(): void {\n    if(this.isTrainingOnly) {\n      this.selectedChapters = [];\n      return;\n    }\n  }\n\n  @computedFrom('isTrainingOnly', 'pageIsRootChapter')\n  private get showRequirementOptions(): boolean {\n    return !this.isTrainingOnly && this.pageIsRootChapter;\n  }\n\n  protected ok(): void {\n    if(this.value) {\n      this.value.summary = this.summary.length ? this.summary : null;\n      this.value.isTrainingOnly = this.isTrainingOnly;\n      this.value.pagesToCompleteIds = this.selectedChapters.map(c => c.id);\n\n      if(this.hasCircularReferences(this.value.pagesToCompleteIds)) {\n        this.errorTk = 'dialogs.addPageDialog.circularReferenceError';\n        return;\n      }\n    }\n    this.controller.ok(this.value);\n  }\n\n  private hasCircularReferences(pagesToCompleteIds: number[]): boolean {\n    const { pageId } = this.model;\n    let existingDependencyRelations: { pageId: number; pageToCompleteId: number }[] = [];\n    for(const chapter of this.availableChapters) {\n      const chapterRelations = chapter.pagesToCompleteIds.map(id => ({ pageId: chapter.id, pageToCompleteId: id }));\n      existingDependencyRelations = [...existingDependencyRelations, ...chapterRelations];\n    }\n    const newDependencyRelations = pagesToCompleteIds.map(id => ({ pageId, pageToCompleteId: id }));\n    return ArrayUtils.hasCircularReferences([...existingDependencyRelations, ...newDependencyRelations], 'pageId', 'pageToCompleteId');\n  }\n\n  private resetErrorTk(): void {\n    this.errorTk = null;\n  }\n}\n\nexport interface IEditPageDialogModel {\n  pageId: number;\n  courseId: number;\n}\n","import { DialogController } from 'aurelia-dialog';\nimport { autoinject, computedFrom } from 'aurelia-framework';\nimport { LT_HELPTEXT_SEPARATOR, LT_QUESTION_SEPARATOR } from 'common/constants/quiz.constants';\nimport { IAnswerDto, ICreateAnswerDto, IEditAnswerDto } from 'common/dto/course/content/answer.interface';\nimport { QuestionType } from 'common/dto/course/content/course.types';\nimport { IQuestionDto, IQuestionEditDto, IQuestionWithEditorAndCreatorDto } from 'common/dto/course/content/question.interface';\nimport { IText } from 'common/interfaces/translations.interface';\nimport { ArrayUtils } from 'common/utils/ArrayUtils';\nimport { assertNotNullOrUndefined } from 'common/utils/assert';\nimport { LogUtils } from 'common/utils/LogUtils';\nimport { NicknameUtils } from 'common/utils/NicknameUtils';\nimport { QuestionUtils } from 'common/utils/QuestionUtils';\nimport { QuestionsApiService } from '../../../services/api/QuestionsApiService';\nimport { FullWidthCustomDialog } from '../../../services/CustomDialog';\nimport { CustomDialogService } from '../../../services/CustomDialogService';\nimport { DateService } from '../../../services/DateService';\nimport { ToastService } from '../../../services/ToastService';\nimport { AnswerCheckedEvent } from '../../elements/edit-answer-form/edit-answer-form';\n\nexport enum MediaType {\n  Audio = 'audio',\n  Text = 'text',\n  Picture = 'picture',\n  PictureAndText = 'pictureAndText',\n}\n\n@autoinject()\nexport class EditQuestionDialog extends FullWidthCustomDialog {\n  public value: IQuestionDto | null = null;\n  private courseId: number | null = null;\n  private questionId: number | null = null;\n  private floatingDto: IQuestionDto | null = null;\n  private errorTk: string | null = null;\n  private answerAsText: boolean = true;\n\n  private allowedQuestionTypes: QuestionType[] | null = null;\n  private allowedQuestionMediaTypes: MediaType[] | null = null;\n  private allowedAnswerMediaTypes: MediaType[] | null = null;\n  private selectedAnswerMediaType: MediaType = MediaType.Text;\n  private selectedQuestionMediaType: MediaType = MediaType.Text;\n\n  private QuestionType = QuestionType;\n  private MediaType = MediaType;\n\n  private originalQuestion: IQuestionWithEditorAndCreatorDto<string> | null = null;\n  private originalAnswers: IAnswerDto[] = [];\n  private question: IQuestionEditDto | null = null;\n  private ltQuestionParts: [string, string, string] = ['', '', ''];\n  private ltHelpTextParts: [string, string, string] = ['', '', ''];\n  private answers: IEditAnswerDto[] = [];\n  private dialogService: CustomDialogService | null = null;\n\n  constructor(\n    private questionsApi: QuestionsApiService,\n    private toastService: ToastService,\n    private dateService: DateService,\n    controller: DialogController,\n    element: Element\n  ) {\n    super(controller, element);\n  }\n\n  public activate(model: IEditQuestionDialogModel): void {\n    super.activate(model);\n    this.courseId = model.courseId;\n    this.questionId = model.questionId ?? null;\n    this.floatingDto = model.floatingDto ?? null;\n    this.dialogService = model.dialogService;\n    this.allowedQuestionTypes = model.allowedQuestionTypes ?? null;\n    this.allowedQuestionMediaTypes = model.allowedQuestionMediaTypes ?? null;\n    this.allowedAnswerMediaTypes = model.allowedAnswerMediaTypes ?? null;\n    this.fetchData();\n  }\n\n  private async fetchData(): Promise<void> {\n    if(this.floatingDto) {\n      this.originalQuestion = {\n        ...JSON.parse(JSON.stringify(this.floatingDto)),\n        editedAt: '',\n        editor: null,\n        createdAt: '',\n        creator: null,\n      };\n    } else if(this.questionId) {\n      this.originalQuestion = await this.questionsApi.getQuestion(this.questionId);\n    } else {\n      return;\n    }\n\n    this.createQuestionEditDto(this.originalQuestion);\n    this.originalAnswers = this.originalQuestion?.answers ?? [];\n    this.answers = this.originalAnswers.length\n      ? this.originalAnswers.map(answer => this.createAnswerEditDto(answer))\n      : [\n        this.createAnswerEditDto(null),\n        ...(\n          this.originalQuestion?.type === QuestionType.LT\n            ? [this.createAnswerEditDto(null), this.createAnswerEditDto(null)]\n            : []\n        )\n      ];\n    this.getSelectedAnswerMediaType();\n    this.getSelectedQuestionMediaType();\n    this.resetAnswersAsText();\n  }\n\n  private async saveQuestion(): Promise<void> {\n    this.errorTk = null;\n    assertNotNullOrUndefined(this.question, 'question cannot be null or undefined');\n\n    if(this.question.type === QuestionType.LT) {\n      this.question.text = this.ltQuestionParts.join(` ${LT_QUESTION_SEPARATOR}|`);\n      this.question.helpText = this.ltHelpTextParts.join(` ${LT_HELPTEXT_SEPARATOR}|`);\n    }\n\n    if(!this.checkQuestion(this.question, this.answers)) return;\n\n    try {\n      if(this.floatingDto) {\n        this.value = this.createQuestionDto(this.question, this.floatingDto);\n      } else if(this.questionId) {\n        this.value = await this.questionsApi.editQuestion(this.questionId, this.question);\n        await this.saveAnswers();\n        this.toastService.success('pages.questions.saveQuestionSuccess');\n      }\n\n      this.controller.ok(this.value);\n    } catch(e) {\n      LogUtils.exception(e);\n      this.toastService.error('pages.questions.saveQuestionError');\n    }\n  }\n\n  private async saveAnswers(): Promise<void> {\n    assertNotNullOrUndefined(this.question, 'question cannot be null or undefined');\n    assertNotNullOrUndefined(this.questionId, 'questionId cannot be null or undefined');\n\n    for(let i = 0; i < 3; i++) {\n      const answer = this.answers?.[i] ?? this.createAnswerEditDto(null);\n      const answerId = this.originalAnswers?.[i]?.id;\n\n      if(this.question.type !== QuestionType.LT) {\n        if(i === 0) {\n          if(answerId) {\n            await this.questionsApi.editAnswer(answerId, answer);\n          } else {\n            const createAnswer = this.createAnswerCreateDto(answer, i);\n            await this.questionsApi.createAnswer(this.questionId, createAnswer);\n          }\n        } else {\n          if(answerId) {\n            await this.questionsApi.deleteAnswer(answerId);\n          }\n        }\n      } else {\n        if(answerId) {\n          await this.questionsApi.editAnswer(answerId, answer);\n        } else {\n          const createAnswer = this.createAnswerCreateDto(answer, i);\n          await this.questionsApi.createAnswer(this.questionId, createAnswer);\n        }\n      }\n    }\n  }\n\n  private createQuestionDto(question: IQuestionEditDto, floatingDto: IQuestionDto): IQuestionDto {\n    return QuestionUtils.createQuestionDto({\n      audioId: question.audioId ?? null,\n      externalId: floatingDto.externalId,\n      helpText: question.helpText,\n      id: floatingDto.id,\n      learningOnly: question.learningOnly,\n      maxMediaSeenCount: question.maxMediaSeenCount,\n      pageId: floatingDto.pageId ?? null,\n      parentQuestionId: floatingDto.parentQuestionId ?? null,\n      points: floatingDto.points ?? 1,\n      text: question.text,\n      translations: floatingDto.translations ?? {},\n      type: question.type,\n      videoId: question.videoId ?? null,\n    }, this.answers.map((a, answerIndex) => ({\n      audio1Id: a.audio1Id,\n      audio2Id: a.audio2Id,\n      audio3Id: a.audio3Id,\n      audio4Id: a.audio4Id,\n      correct1: a.correct1,\n      correct2: a.correct2,\n      correct3: a.correct3,\n      correct4: a.correct4,\n      id: floatingDto?.answers[answerIndex]?.id ?? 0,\n      picture1Id: a.picture1Id,\n      picture2Id: a.picture2Id,\n      picture3Id: a.picture3Id,\n      picture4Id: a.picture4Id,\n      text1: a.text1,\n      text2: a.text2,\n      text3: a.text3,\n      text4: a.text4,\n      translations: floatingDto.answers[answerIndex]?.translations ?? {},\n    })));\n  }\n\n  @computedFrom('originalQuestion.creator')\n  private get creatorName(): string {\n    if(!this.originalQuestion?.creator) return '-';\n    return NicknameUtils.getFullName(this.originalQuestion.creator);\n  }\n\n  @computedFrom('originalQuestion.editor')\n  private get editorName(): string {\n    if(!this.originalQuestion?.editor) return '-';\n    return NicknameUtils.getFullName(this.originalQuestion.editor);\n  }\n\n  @computedFrom('originalQuestion.createdAt')\n  private get createdAt(): string {\n    if(!this.originalQuestion) return '-';\n    return this.dateService.formatDate(this.originalQuestion.createdAt);\n  }\n\n  @computedFrom('originalQuestion.editedAt')\n  private get editedAt(): string {\n    if(!this.originalQuestion) return '-';\n    return this.dateService.formatDate(this.originalQuestion.editedAt);\n  }\n\n  @computedFrom('QuestionType', 'allowedQuestionTypes')\n  private get supportedQuestionTypes(): QuestionType[] {\n    return this.allowedQuestionTypes ?? Object.values(this.QuestionType);\n  }\n\n  @computedFrom('MediaType', 'question.type', 'allowedAnswerMediaTypes')\n  private get supportedAnswerMediaTypes(): MediaType[] {\n    const allMediaTypes = [MediaType.Text, MediaType.Audio, MediaType.Picture];\n    const allowedMediaTypes = this.allowedAnswerMediaTypes ? ArrayUtils.intersect(this.allowedAnswerMediaTypes, allMediaTypes) : allMediaTypes;\n    const readonlyMediaTypes =\n      this.question?.type && [QuestionType.SCP, QuestionType.MCP].includes(this.question.type)\n        ? [MediaType.PictureAndText]\n        : [];\n    return [...allowedMediaTypes, ...readonlyMediaTypes];\n  }\n\n  @computedFrom('MediaType', 'question.type', 'allowedQuestionMediaTypes')\n  private get supportedQuestionMediaTypes(): MediaType[] {\n    const allMediaTypes = [MediaType.Audio, MediaType.Picture, ...(this.question?.type === QuestionType.LT ? [MediaType.Text] : [])];\n    return this.allowedQuestionMediaTypes ? ArrayUtils.intersect(this.allowedQuestionMediaTypes, allMediaTypes) : allMediaTypes;\n  }\n\n  @computedFrom('question.type')\n  private get alertMessage(): IText {\n    return { tk: 'pages.questions.unsupportedQuestionWarning', tkParams: { type: this.question?.type }, html: true };\n  }\n\n  @computedFrom('question.type')\n  private get isSupported(): boolean {\n    if(!this.question) return false;\n    return this.supportedQuestionTypes.includes(this.question.type);\n  }\n\n  @computedFrom('question.type', 'isSupported')\n  private get mediaTypeSelectable(): boolean {\n    if(!this.question) return false;\n    return this.isSupported && ![QuestionType.LT, QuestionType.MCP, QuestionType.SCP].includes(this.question.type);\n  }\n\n  @computedFrom('question.type')\n  private get canOnlyHaveOneCorrectAnswer(): boolean {\n    if(!this.question) return false;\n    return [QuestionType.TF, QuestionType.SCP, QuestionType.SC, QuestionType.LT].includes(this.question.type);\n  }\n\n  @computedFrom('question.type', 'selectedAnswerMediaType')\n  private get isTrueOrFalseQuestionWithText(): boolean {\n    if(!this.question) return false;\n    return this.question.type === QuestionType.TF && this.selectedAnswerMediaType === MediaType.Text;\n  }\n\n  private createQuestionEditDto(question: IQuestionDto | null): void {\n    if(question?.type === QuestionType.LT) {\n      this.ltQuestionParts = QuestionUtils.parseFillInTheBlankQuestion(question.text);\n      this.ltHelpTextParts = QuestionUtils.parseFillInTheBlankHelpText(question.helpText ?? '');\n    }\n    this.question = {\n      type: question?.type ?? QuestionType.MC,\n      text: question?.text ?? '',\n      helpText: question?.helpText ?? '',\n      learningOnly: question?.learningOnly ?? true,\n      maxMediaSeenCount: 0,\n      pictureBlinkId: question?.pictureBlink?.id ?? null,\n      pictureId: question?.picture?.id ?? null,\n      videoId: question?.videoId ?? null,\n      audioId: question?.audioId ?? null,\n    };\n  }\n\n  private createAnswerEditDto(answer: IAnswerDto | null): IEditAnswerDto {\n    return {\n      questionId: this.questionId ?? 0,\n      text1: answer?.text1 ?? '',\n      text2: answer?.text2 ?? '',\n      text3: answer?.text3 ?? '',\n      text4: answer?.text4 ?? '',\n      correct1: answer?.correct1 ?? false,\n      correct2: answer?.correct2 ?? false,\n      correct3: answer?.correct3 ?? false,\n      correct4: answer?.correct4 ?? false,\n      audio1Id: answer?.audio1Id ?? null,\n      audio2Id: answer?.audio2Id ?? null,\n      audio3Id: answer?.audio3Id ?? null,\n      audio4Id: answer?.audio4Id ?? null,\n      picture1Id: answer?.picture1?.id ?? null,\n      picture2Id: answer?.picture2?.id ?? null,\n      picture3Id: answer?.picture3?.id ?? null,\n      picture4Id: answer?.picture4?.id ?? null,\n    };\n  }\n\n  private createAnswerCreateDto(answer: IEditAnswerDto, order: number): ICreateAnswerDto {\n    return {\n      text1: answer.text1,\n      text2: answer.text2,\n      text3: answer.text3,\n      text4: answer.text4,\n      correct1: answer.correct1,\n      correct2: answer.correct2,\n      correct3: answer.correct3,\n      correct4: answer.correct4,\n      audio1Id: answer.audio1Id,\n      audio2Id: answer.audio2Id,\n      audio3Id: answer.audio3Id,\n      audio4Id: answer.audio4Id,\n      picture1Id: answer.picture1Id,\n      picture2Id: answer.picture2Id,\n      picture3Id: answer.picture3Id,\n      picture4Id: answer.picture4Id,\n      order\n    };\n  }\n\n  private getSelectedAnswerMediaType(): void {\n    const sampleAnswer = this.originalAnswers?.[0] ?? null;\n    if(sampleAnswer?.audioUrl1 || sampleAnswer?.audioUrl2 || sampleAnswer?.audioUrl3 || sampleAnswer?.audioUrl4) {\n      this.selectedAnswerMediaType = MediaType.Audio;\n    } else if(sampleAnswer?.picture1 || sampleAnswer?.picture2 || sampleAnswer?.picture3 || sampleAnswer?.picture4) {\n      this.selectedAnswerMediaType = MediaType.Picture;\n    } else {\n      this.selectedAnswerMediaType = MediaType.Text;\n    }\n    if(this.question?.type === QuestionType.TF || this.question?.type === QuestionType.LT) {\n      this.selectedAnswerMediaType = MediaType.Text;\n    }\n    if(this.question?.type === QuestionType.MCP || this.question?.type === QuestionType.SCP) {\n      this.selectedAnswerMediaType = MediaType.PictureAndText;\n    }\n  }\n\n  private getSelectedQuestionMediaType(): void {\n    if(this.originalQuestion?.audioUrl) {\n      this.selectedQuestionMediaType = MediaType.Audio;\n    } else {\n      this.selectedQuestionMediaType = MediaType.Picture;\n    }\n    if(this.question?.type === QuestionType.LT) {\n      this.selectedQuestionMediaType = MediaType.Text;\n    }\n    if(this.question?.type === QuestionType.MCP || this.question?.type === QuestionType.SCP) {\n      this.selectedQuestionMediaType = MediaType.Picture;\n    }\n\n    if(this.allowedQuestionMediaTypes && !this.allowedQuestionMediaTypes.includes(this.selectedQuestionMediaType)) {\n      this.selectedQuestionMediaType = MediaType.Text;\n    }\n  }\n\n  private resetOrFillAnswers(): void {\n    if(this.question?.type === QuestionType.LT && this.originalAnswers.length !== 3) {\n      this.answers = [this.originalAnswers[0], this.originalAnswers[0], this.originalAnswers[0]].map(answer => this.createAnswerEditDto(answer));\n    } else if(this.question?.type === QuestionType.LT && this.originalAnswers.length === 3) {\n      this.answers = this.originalAnswers.map(answer => this.createAnswerEditDto(answer));\n    } else {\n      if(this.canOnlyHaveOneCorrectAnswer) { // reset all correct answers\n        this.originalAnswers[0].correct1 = false;\n        this.originalAnswers[0].correct2 = false;\n        this.originalAnswers[0].correct3 = false;\n        this.originalAnswers[0].correct4 = false;\n      }\n      this.answers = [this.createAnswerEditDto(this.originalAnswers[0])];\n    }\n  }\n\n  /**\n   * TF question types can either have \"true\" and \"false\" or text as answers\n   * if no text is set, \"true\" and \"false\" will be the default option\n   */\n  private resetAnswersAsText(): void {\n    this.answerAsText = !!(this.answers[0].text1.length && this.answers[0].text2.length);\n  }\n\n  private resetAnswerTexts(): void {\n    this.answers[0].text1 = '';\n    this.answers[0].text2 = '';\n    this.answers[0].text3 = '';\n    this.answers[0].text4 = '';\n  }\n\n  /**\n   * TF,SC,SCP question types can have only one correct answer\n   * at least one answer has to be correct\n   */\n  private checkQuestion(question: IQuestionEditDto, answers: IEditAnswerDto[]): boolean {\n    for(const answer of answers) {\n      if((question.type === QuestionType.TF && answer.correct1 && answer.correct2)\n        || ([QuestionType.SC, QuestionType.SCP].includes(question.type)\n          && [answer.correct1, answer.correct2, answer.correct3, answer.correct4].filter(a => a).length !== 1)\n      ) {\n        this.errorTk = 'pages.questions.moreThanOneCorrectAnswers';\n      }\n      const correctAnswers = question.type === QuestionType.TF\n        ? [answer.correct1, answer.correct2]\n        : [answer.correct1, answer.correct2, answer.correct3, answer.correct4];\n      if(!correctAnswers.some(a => a)) {\n        this.errorTk = 'pages.questions.oneAnswerHasToBeCorrect';\n      }\n    }\n    return !this.errorTk;\n  }\n\n  private async handleAnswerMediaTypeChange(): Promise<void> {\n    const confirmed = await this.showConfirmDialog();\n    if(confirmed) {\n      this.answers = [this.createAnswerEditDto(null)];\n    } else {\n      this.getSelectedAnswerMediaType();\n    }\n  }\n\n  private async handleQuestionMediaTypeChange(): Promise<void> {\n    const confirmed = await this.showConfirmDialog();\n    if(confirmed) {\n      this.createQuestionEditDto(null);\n    } else {\n      this.getSelectedQuestionMediaType();\n    }\n  }\n\n  private async handleQuestionTypeChange(): Promise<void> {\n    this.getSelectedAnswerMediaType();\n    this.getSelectedQuestionMediaType();\n    this.resetOrFillAnswers();\n    this.resetAnswersAsText();\n  }\n\n  private handleAnswerChecked(answerKey: 'correct1' | 'correct2' | 'correct3' | 'correct4', index: number, event: AnswerCheckedEvent): void {\n    const { checked } = event.detail;\n    /**\n     * some questionTypes can only have one correct answer\n     * checkboxes are treated like a radio in that case\n     */\n    if(this.canOnlyHaveOneCorrectAnswer) {\n      this.answers[index].correct1 = false;\n      this.answers[index].correct2 = false;\n      this.answers[index].correct3 = false;\n      this.answers[index].correct4 = false;\n    }\n    this.answers[index][answerKey] = checked;\n  }\n\n  private showPreview(): void {\n    assertNotNullOrUndefined(this.originalQuestion, 'originalQuestion cannot be null or undefined');\n    this.dialogService?.showQuestionPreview(this.originalQuestion);\n  }\n\n  private async showConfirmDialog(): Promise<boolean> {\n    return !!this.dialogService?.confirmAction({\n      icon: { name: 'alert-triangle', color: 'var(--warning)' },\n      titleTk: 'pages.questions.warningTitle',\n      textTk: 'pages.questions.resetContentWarning',\n      confirmTk: 'globals.ok',\n    });\n  }\n}\n\nexport interface IEditQuestionDialogModel {\n  courseId: number;\n  questionId?: number;\n  floatingDto?: IQuestionDto; // Launch the dialog with a question that has not been saved before\n  dialogService: CustomDialogService;\n  allowedQuestionTypes?: QuestionType[];\n  allowedQuestionMediaTypes?: MediaType[];\n  allowedAnswerMediaTypes?: MediaType[];\n}\n","import { autoinject, computedFrom, } from 'aurelia-framework';\nimport { IAssignSkillToStudentDto, IStudentSkillListItemDto, SkillAwardReason } from 'common/dto/skill/management/skill.management.interface';\nimport { SkillUtils } from 'common/utils/SkillUtils';\nimport { assertNotNullOrUndefined } from 'common/utils/assert';\nimport { CustomDialog } from '../../../services/CustomDialog';\nimport { ValidationUtils } from '../../../utils/ValidationUtils';\n\n@autoinject()\nexport class EditStudentSkillDialog extends CustomDialog {\n  public model!: IEditStudentSkillDialogModel;\n  public value: (IAssignSkillToStudentDto & { devalidate?: boolean }) | null = null;\n\n  private statusTk = 'globals.studentSkillStatus.pending';\n  private SkillAwardReason = SkillAwardReason;\n  private unlimitedValidity = false;\n  private devalidate = false;\n\n  private errorTks: IErrorTk = {\n    validUntil: null,\n    offerExtensionFrom: null\n  };\n\n  public initValue(model: IEditStudentSkillDialogModel): void {\n    this.value = {\n      overrideExpirationDate: model.skill.expiresAt,\n      overrideRenewabilityDate: model.skill.extensionPossibleFrom,\n    };\n    this.unlimitedValidity = model.skill.expiresAt === null;\n\n    if(model.skill.expiresAt == null || new Date(model.skill.expiresAt) > new Date()) {\n      if(model.skill.awardedReason === SkillAwardReason.Manual) {\n        this.statusTk = 'globals.studentSkillStatus.assigned';\n      } else {\n        this.statusTk = 'globals.studentSkillStatus.acquired';\n      }\n    }\n  }\n\n  protected async ok(): Promise<void> {\n    assertNotNullOrUndefined(this.value, 'value cannot be null or undefined');\n\n    if(!this.checkStudentSkill(this.value)) return;\n    this.controller.ok({\n      overrideExpirationDate: this.unlimitedValidity ? null : this.value.overrideExpirationDate,\n      overrideRenewabilityDate: this.unlimitedValidity ? null : this.value.overrideRenewabilityDate,\n      devalidate: this.devalidate,\n    });\n  }\n\n  private onUnassignSkillClick(): void {\n    assertNotNullOrUndefined(this.value, 'value cannot be null or undefined');\n\n    this.devalidate = true;\n    this.statusTk = 'globals.studentSkillStatus.invalid';\n  }\n\n  private resetErrorTkByKey(key: keyof IErrorTk): void {\n    this.errorTks[key] = null;\n  }\n\n  private resetErrorTks(): void {\n    this.errorTks = {\n      validUntil: null,\n      offerExtensionFrom: null\n    };\n  }\n\n  private checkStudentSkill(skill: IAssignSkillToStudentDto): boolean {\n    if(this.unlimitedValidity || this.devalidate) return true;\n\n    this.errorTks.validUntil = ValidationUtils.isDateInPast(skill.overrideExpirationDate);\n    this.errorTks.offerExtensionFrom = ValidationUtils.isEndDateBeforeStartDate(skill.overrideRenewabilityDate, skill.overrideExpirationDate, 'utils.validation.offerExtensionFromAfterValidUntil');\n    return Object.values(this.errorTks).every(value => !value);\n  }\n\n  @computedFrom('model.skill')\n  private get studentHasSkill(): boolean {\n    return SkillUtils.hasSkill(this.model.skill);\n  }\n}\n\nexport interface IEditStudentSkillDialogModel {\n  skill: IStudentSkillListItemDto;\n}\n\ninterface IErrorTk {\n  validUntil: string | null;\n  offerExtensionFrom: string | null;\n}\n","import { DialogController } from 'aurelia-dialog';\nimport { autoinject } from 'aurelia-framework';\nimport { IMpTileDto } from 'common/dto/course/content/block.interface';\nimport { IPictureDto } from 'common/dto/course/content/course.interface';\nimport { ITileInfo } from 'common/utils/MethodpoolUtils';\nimport { CustomDialog } from '../../../services/CustomDialog';\nimport { ToastService } from '../../../services/ToastService';\n\n@autoinject()\nexport class EditTileDialog extends CustomDialog {\n  public model!: IEditTileDialogModel;\n  public value!: IEditTileDialogValue;\n\n  private title: string = '';\n  private subtitle: string = '';\n\n  private tileInfo: ITileInfo | null = null;\n\n  private picture: IPictureDto | null = null;\n  private audioUrl: string | null = null;\n\n  constructor(\n    controller: DialogController,\n    private toastService: ToastService\n  ) {\n    super(controller);\n  }\n\n  public initValue(model: IEditTileDialogModel): void {\n    this.value = { tile: model.tile, tileInfo: model.tileInfo };\n\n    this.title = model.tile.title ?? '';\n    this.subtitle = model.tile.subtitle ?? '';\n\n    this.tileInfo = model.tileInfo;\n\n    this.picture = model.tile.picture ?? null;\n    this.audioUrl = model.tile.audioUrl ?? null;\n  }\n\n  protected ok(): void {\n    this.value.tile.title = this.title?.length ? this.title : null;\n    this.value.tile.subtitle = this.subtitle?.length ? this.subtitle : null;\n\n    this.value.tileInfo.title = this.value.tile.title || undefined;\n    this.value.tileInfo.subtitle = this.value.tile.subtitle || undefined;\n    this.value.tileInfo.audioId = this.tileInfo?.audioId || undefined;\n    this.value.tileInfo.pictureId = this.tileInfo?.pictureId || undefined;\n\n    this.value.tile.picture = this.picture;\n    this.value.tile.audioUrl = this.audioUrl;\n    if(this.checkTile()) {\n      this.controller.ok(this.value);\n    } else {\n      this.toastService.error('dialogs.editTileDialog.emptyTileError');\n    }\n  }\n\n  private checkTile(): boolean {\n    return !!(this.value.tile.title\n      || this.value.tile.subtitle\n      || this.value.tile.picture\n      || this.value.tile.audioUrl);\n  }\n}\n\nexport interface IEditTileDialogModel {\n  tile: IMpTileDto;\n  tileInfo: ITileInfo;\n}\n\nexport interface IEditTileDialogValue {\n  tile: IMpTileDto;\n  tileInfo: ITileInfo;\n}\n"],"names":[],"sourceRoot":""}