{"version":3,"file":"app-24d02fed.f584a1446b2fc034d810.bundle.js","mappings":"AAGA","sources":["webpack://lernplattform-admin-frontend/./src/resources/pages/events-page/events-page.html","webpack://lernplattform-admin-frontend/./src/resources/pages/external-api-page/external-api-page.html","webpack://lernplattform-admin-frontend/./src/resources/pages/gallery-page/gallery-page.html","webpack://lernplattform-admin-frontend/./src/resources/pages/home-page/home-page.html","webpack://lernplattform-admin-frontend/./src/resources/pages/invites-page/invites-page.html","webpack://lernplattform-admin-frontend/./src/resources/pages/events-page/events-page.ts","webpack://lernplattform-admin-frontend/./src/resources/pages/external-api-page/external-api-page.ts","webpack://lernplattform-admin-frontend/./src/resources/pages/gallery-page/gallery-page.ts","webpack://lernplattform-admin-frontend/./src/resources/pages/home-page/home-page.ts","webpack://lernplattform-admin-frontend/./src/resources/pages/invites-page/invites-page.ts"],"sourcesContent":["// Module\nvar code = \"<template> <div class=\\\"page-header\\\"> <h1 class=\\\"page-title\\\" t=\\\"pages.events.title\\\"></h1> <div class=\\\"ml-auto\\\"> <button if.to-view=\\\"!isTenantSyncActive\\\" class=\\\"btn btn-primary ml-auto\\\" click.delegate=\\\"createEvent()\\\"> <i class=\\\"fe fe-calendar mr-2\\\"></i><span t=\\\"pages.events.createEvent\\\"></span> </button> </div> </div> <div class=\\\"row row-cards row-deck\\\"> <div class=\\\"col-12\\\"> <custom-table columns.to-view=\\\"tableColumns\\\" extendable=\\\"true\\\" table-id=\\\"events\\\" refresh-version.to-view=\\\"tableRefreshVersion\\\" get-shown-entries-data.call=\\\"fetchTableData(limit, offset)\\\"></custom-table> </div> </div> </template> \";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <h1 class=\\\"page-title mb-4\\\" t=\\\"pages.external-api.title\\\"></h1> <div class=\\\"external-api-page__tabs\\\"> <navigation-tabs tabs.to-view=\\\"navigationTabs\\\" wide-tabs.to-view=\\\"true\\\"></navigation-tabs> </div> <external-api-course-license if.to-view=\\\"activeTab === ExternalApiTabs.CourseLicense\\\" class=\\\"external-api-page__content\\\"></external-api-course-license> <external-api-overview if.to-view=\\\"activeTab === ExternalApiTabs.Overview\\\" class=\\\"external-api-page__content\\\"></external-api-overview> <external-api-log if.to-view=\\\"activeTab === ExternalApiTabs.Log\\\" class=\\\"external-api-page__content\\\"></external-api-log> <external-api-sync-queue if.to-view=\\\"activeTab === ExternalApiTabs.SyncQueue\\\" class=\\\"external-api-page__content\\\"></external-api-sync-queue> </template> \";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <button-standard if.to-view=\\\"courseId\\\" click.trigger=\\\"navigateToCourse()\\\" class=\\\"mb-4\\\" t=\\\"[btnText]globals.back\\\" icon-name=\\\"System/Tabler/Chevron-Left\\\" btn-size=\\\"btn-sm\\\" btn-color=\\\"secondary\\\"></button-standard> <gallery-widget course-id.to-view=\\\"courseId\\\" mode=\\\"picture\\\" show-media-type-filter.to-view=\\\"true\\\"></gallery-widget> <div if.to-view=\\\"isProvider\\\" style=\\\"display:flex;align-items:center;gap:8px\\\"> <h5 style=\\\"margin:0\\\">Bunny Migration</h5> <p style=\\\"margin:0\\\">Zu migrieren: ${migrationStatus.count}</p> <template if.to-view=\\\"migrationStatus.status !== 'running' && migrationStatus.count > 0\\\"> <button click.trigger=\\\"startMigration(10)\\\">10 Migrieren</button> <button click.trigger=\\\"startMigration()\\\">Alle Migrieren</button> </template> <div if.to-view=\\\"migrationStatus.status\\\">${migrationStatus.status} ${migrationStatus.message}</div> </div> </template> \";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <div class=\\\"page-header\\\"> <img src.to-view=\\\"bannerImage\\\" class=\\\"img-fluid\\\"> </div> <template if.to-view=\\\"!isFrp\\\"> <div class=\\\"home__title\\\" t=\\\"pages.home.welcome\\\" t-params.to-view=\\\"{ appName: appName }\\\"></div> <div class=\\\"home__subtitle\\\" t=\\\"pages.home.info\\\"></div> </template> <div if.to-view=\\\"showUpcomingEvents\\\" style=\\\"margin-top:20px\\\"> <div class=\\\"page-header\\\"> <h1 class=\\\"page-title\\\" t=\\\"pages.lecturerEvents.upcomingEvents\\\"></h1> </div> <lecturer-events-table fetch-table-data-delegate.call=\\\"fetchTableData()\\\" table-refresh-version.to-view=\\\"tableRefreshVersion\\\" hide-footer.to-view=\\\"true\\\"></lecturer-events-table> <a as-element=\\\"router-link\\\" to.to-view=\\\"{ name: 'lecturer-events' }\\\" t=\\\"pages.lecturerEvents.showAll\\\"></a> </div> <div class=\\\"home__statistic-cards mt-6\\\"> <div if.to-view=\\\"!isFrp && (isProvider || isOperator)\\\" class=\\\"home__statistic-card\\\"> <div class=\\\"home__statistic-card-title\\\"> ${onlineStudentStats.lastHour} </div> <div class=\\\"home__statistic-card-sub-title\\\" t=\\\"pages.home.onlineLastHour\\\"></div> </div> <div if.to-view=\\\"(isFrp && isOperator) || (!isFrp && (isProvider || isOperator))\\\" class=\\\"home__statistic-card\\\"> <div class=\\\"home__statistic-card-title\\\"> ${onlineStudentStats.last24Hours} </div> <div class=\\\"home__statistic-card-sub-title\\\" t=\\\"pages.home.${ isFrp ? 'studentOnlineLast24Hours': 'onlineLast24Hours' }\\\"></div> </div> <div if.to-view=\\\"isFrp && (isProvider || isOperator)\\\" class=\\\"home__statistic-card\\\"> <div class=\\\"home__statistic-card-title\\\"> ${pendingTeacherRegistrationsCount} </div> <div class=\\\"home__statistic-card-sub-title\\\" t=\\\"pages.home.pendingTeacherRegistration\\\" t-params.to-view=\\\"{ count: pendingTeacherRegistrationsCount }\\\"></div> </div> </div> <div if.to-view=\\\"isProvider && !isFrp\\\" class=\\\"row\\\"> <div class=\\\"col-sm-6\\\"> <div class=\\\"card\\\"> <div class=\\\"card-body text-center\\\"> <div class=\\\"display-4 font-weight-bold mb-2\\\">${formatNumberGrouped(createdInvitations)}</div> <div class=\\\"h5 mb-4\\\" t=\\\"pages.home.generatedInvitations\\\"></div> <div class=\\\"progress progress-sm\\\"> <div class=\\\"progress-bar home__progress_bar--created\\\" css=\\\"width: ${createdInvitations ? '100' : 0}%\\\"></div> </div> </div> </div> </div> <div class=\\\"col-sm-6\\\"> <div class=\\\"card\\\"> <div class=\\\"card-body text-center\\\"> <div class=\\\"display-4 font-weight-bold mb-2\\\">${formatNumberGrouped(usedInvitations)}</div> <div class=\\\"h5 mb-4\\\" t=\\\"pages.home.usedInvitations\\\"></div> <div class=\\\"progress progress-sm\\\"> <div class=\\\"progress-bar home__progress_bar--used\\\" css=\\\"width: ${createdInvitations ? usedInvitations / createdInvitations * 100 : 0}%\\\"></div> </div> </div> </div> </div> <div class=\\\"col-sm-12 home__download-statistics-container mb-4\\\"> <button-standard click.delegate=\\\"downloadStatistics()\\\" icon-name=\\\"System/Download\\\" t=\\\"[btn-text]pages.home.downloadInvitationStatistics\\\"></button-standard> </div> </div> <div if.to-view=\\\"isOperator && !isFrp && showEvents\\\" class=\\\"row\\\"> <div class=\\\"col-sm-12\\\"> <recent-student-activity-table></recent-student-activity-table> </div> </div> <div if.to-view=\\\"isProvider && recentlyRatedCourses.length\\\" class=\\\"row\\\"> <div class=\\\"col-sm-12\\\"> <course-rating-preview ratings.to-view=\\\"recentlyRatedCourses\\\" download-ratings.trigger=\\\"downloadRatings()\\\"></course-rating-preview> </div> </div> <teacher-tutorials if.to-view=\\\"isLecturer && isFrp\\\"></teacher-tutorials> </template> \";\n// Exports\nexport default code;","// Module\nvar code = \"<template> <div class=\\\"page-header\\\"> <h1 class=\\\"page-title\\\" t=\\\"pages.invites.title\\\"></h1> </div> <div class=\\\"row row-cards row-deck\\\"> <div class=\\\"card\\\" repeat.for=\\\"role of pendingRoles\\\"> <div class=\\\"card-header\\\"> <h3 class=\\\"card-title\\\">${role.tenantName}</h3> <div class=\\\"card-options\\\"> <button class=\\\"btn btn-primary\\\" click.delegate=\\\"acceptInvite(role.tenantId)\\\" t=\\\"globals.accept\\\"></button> </div> </div> </div> <div class=\\\"card\\\" if.to-view=\\\"pendingRoles.length === 0\\\"> <div class=\\\"card-header\\\"> <h3 class=\\\"card-title\\\" t=\\\"pages.invites.noPendingInvites\\\"></h3> </div> </div> </div></template> \";\n// Exports\nexport default code;","import { autoinject } from 'aurelia-framework';\nimport { Router } from 'aurelia-router';\nimport { StoreService } from 'common/aurelia-services/StoreService';\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, IEventDto } from 'common/dto/tenant/event/tenant.event.interface';\nimport { ITenantUserInfoDto } from 'common/dto/tenant/other/tenant.user.info.interface';\nimport { ErrorType } from 'common/enum/errortype.enum';\nimport { LogUtils } from 'common/utils/LogUtils';\nimport { CustomDialogService } from '../../../services/CustomDialogService';\nimport { DateService } from '../../../services/DateService';\nimport { StudentService } from '../../../services/StudentService';\nimport { EventsApiService } from '../../../services/api/EventsApiService';\nimport { TenantApiService } from '../../../services/api/TenantApiService';\nimport { IState } from '../../../store/state';\nimport { EditEventDialog } from '../../dialogs/edit-event-dialog/edit-event-dialog';\nimport { ActionsCell } from '../../elements/custom-table/actions-cell/actions-cell';\nimport { ButtonCell } from '../../elements/custom-table/button-cell/button-cell';\nimport { CustomTableCell } from '../../elements/custom-table/custom-table-cell/custom-table-cell';\nimport { VirtualFilterField, createPrepareServersideSortingFunction, prepareServersideFiltering, prepareServersideRangeFiltering } from '../../elements/custom-table/custom-table-helpers';\nimport { IColumnDefinition } from '../../elements/custom-table/custom-table-interfaces';\nimport { ENavigationTab } from '../student-invitations-page/student-invitations-page';\nimport { ToastService } from './../../../services/ToastService';\n\n@autoinject()\nexport class EventsPage {\n  private tenantId: number | null = null;\n  private isTenantSyncActive = false;\n\n  private courses: ICourseEditResponseDto[] = [];\n  private lecturers: ITenantUserInfoDto[] = [];\n  private addresses: ITenantAddressWithIdDto[] = [];\n\n  private didFetchInitialDialogData = false;\n\n  private tableRefreshVersion: number = 0;\n\n  private filter: IFilterDto<IEventDto<string>> = {\n    limit: 10,\n    offset: 0,\n    order: [{\n      field: 'plannedDateFrom',\n      dir: ColumnSortDirection.ASC,\n    }],\n    filter: []\n  };\n\n  private tableColumns: Array<IColumnDefinition<CustomTableCell<IEventDto<string>> | ActionsCell<IEventDto<string>> | ButtonCell<IEventDto<string>>>> = [\n    {\n      id: 'name',\n      nameTk: 'globals.name',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        property: 'title'\n      },\n      prepareSorting: createPrepareServersideSortingFunction(this.filter, 'title'),\n      filter: {\n        placeholderTk: 'globals.filter',\n        prepare: v => prepareServersideFiltering(this.filter, 'title', v),\n      },\n      wrap: true\n    },\n    {\n      id: 'type',\n      nameTk: 'globals.type',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        tk: event => `globals.eventTypes.${event.type}`\n      },\n      filter: {\n        placeholderTk: 'globals.filter',\n        prepare: v => this.filter ? prepareServersideFiltering(this.filter, 'type' as VirtualFilterField, v, FilterOperationType.Eq) : undefined,\n        options: Object.values(EventType).map(eT => ({ keyTk: `globals.eventTypes.${eT}`, value: eT }))\n      }\n    },\n    {\n      id: 'description',\n      nameTk: 'globals.description',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        property: 'description'\n      },\n      prepareSorting: createPrepareServersideSortingFunction(this.filter, 'description'),\n      filter: {\n        placeholderTk: 'globals.filter',\n        prepare: v => prepareServersideFiltering(this.filter, 'description', v),\n      },\n      wrap: true\n    },\n    {\n      id: 'date',\n      nameTk: 'globals.date',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        transformer: event => this.dateService.formatDateWithTimespan(event.plannedDateFrom, event.plannedDateTo)\n      },\n      prepareSorting: createPrepareServersideSortingFunction(this.filter, 'plannedDateFrom'),\n      filter: {\n        placeholderTk: 'globals.filter',\n        daterange: true,\n        prepare: v => prepareServersideRangeFiltering(this.filter, 'plannedDateFrom', v),\n        initialValue: new Date(),\n      },\n      wrap: true\n    },\n    {\n      id: 'course',\n      nameTk: 'globals.course',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        transformer: event => event.courses.map(course => course.title).join(', ')\n      },\n      prepareSorting: createPrepareServersideSortingFunction(this.filter, 'courseTitle' as VirtualFilterField),\n      filter: {\n        placeholderTk: 'globals.filter',\n        prepare: v => prepareServersideFiltering(this.filter, 'courseTitle' as VirtualFilterField, v),\n      },\n      wrap: true\n    },\n    {\n      id: 'group',\n      nameTk: 'globals.group',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        transformer: event => event.courseGroups.map(course => course.name).join(', ')\n      },\n      prepareSorting: createPrepareServersideSortingFunction(this.filter, 'courseGroupName' as VirtualFilterField),\n      filter: {\n        placeholderTk: 'globals.filter',\n        prepare: v => prepareServersideFiltering(this.filter, 'courseGroupName' as VirtualFilterField, v),\n      },\n      wrap: true\n    },\n    {\n      id: 'button',\n      nameTk: 'globals.students',\n      cellViewModel: ButtonCell,\n      shrink: true,\n      cellOptions: {\n        buttonText: () => ({\n          tk: 'globals.showStudentList'\n        }),\n        clickCallback: async e => this.displayStudentsDialog(e.id)\n      }\n    },\n    {\n      id: 'lecturer',\n      nameTk: 'globals.lecturer',\n      cellViewModel: CustomTableCell,\n      cellOptions: {\n        transformer: event => event.lecturer ? `${event.lecturer.firstname ?? ''} ${event.lecturer.lastname ?? ''}` : ''\n      },\n      prepareSorting: createPrepareServersideSortingFunction(this.filter, 'lecturerLastname' as VirtualFilterField),\n      filter: {\n        placeholderTk: 'globals.filter',\n        prepare: v => prepareServersideFiltering(this.filter, 'lecturerFullname' as VirtualFilterField, v),\n      },\n    },\n    {\n      id: 'actions',\n      nameTk: '',\n      shrink: true,\n      cellViewModel: ActionsCell,\n      cellOptions: {\n        entries: [\n          {\n            labelTk: 'globals.edit',\n            clickCallback: event => { this.editEvent(event); },\n            icon: 'fe-edit'\n          },\n          {\n            labelTk: 'globals.manageAttendees',\n            clickCallback: lecture => { this.router.navigateToRoute('event-attendants/id', { eventId: lecture.id }); },\n            showCallback: e => e.type === EventType.Lecture\n          },\n          'divider',\n          {\n            labelTk: 'globals.delete',\n            clickCallback: event => { this.deleteEvent(event); },\n            icon: 'fe-delete',\n            additionalClasses: 'text-danger'\n          },\n        ]\n      }\n    }\n  ];\n\n  constructor(\n    private api: EventsApiService,\n    private tenantApi: TenantApiService,\n    private dialogService: CustomDialogService,\n    private dateService: DateService,\n    private router: Router,\n    private toastService: ToastService,\n    private storeService: StoreService<IState>,\n    private studentService: StudentService\n  ) { }\n\n  protected attached(): void {\n    this.storeService.subscribeToDetailedChanges(this, [\n      state => state.currentRole?.tenantId,\n      state => state.currentRole?.isTenantSyncActive,\n    ], (_, tenantId, isTenantSyncActive) => {\n      this.tenantId = tenantId ?? null;\n      this.isTenantSyncActive = isTenantSyncActive ?? false;\n    });\n  }\n\n  protected detached(): void {\n    this.storeService.disposeAllSubscriptions(this);\n  }\n\n  private async fetchTableData(limit: number, offset: number): Promise<{ total: number; data: Array<IEventDto<string>> }> {\n    this.filter.limit = limit;\n    this.filter.offset = offset;\n\n    if(!this.tenantId) {\n      return { data: [], total: 0 };\n    }\n\n    const events = await this.api.getEvents(this.tenantId, this.filter);\n    return { data: events.rows, total: events.count };\n  }\n\n  private async fetchDialogData(): Promise<void> {\n    if(!this.tenantId) return;\n    this.courses = await this.tenantApi.getAvailableRegularCourses();\n    this.lecturers = await this.tenantApi.getAvailableLecturers(this.tenantId);\n    this.addresses = await this.tenantApi.getAvailableAddresses(this.tenantId);\n    this.didFetchInitialDialogData = true;\n  }\n\n  private async editEvent(event: IEventDto<string>): Promise<void> {\n    if(!this.tenantId) return;\n    if(!this.didFetchInitialDialogData) await this.fetchDialogData();\n    const modifiedEvent = await this.dialogService.getResultOrNull(EditEventDialog, {\n      createNew: false,\n      value: {\n        type: event.type,\n        plannedDateFrom: this.dateService.parseDateTime(event.plannedDateFrom).toISOString(),\n        plannedDateTo: this.dateService.parseDateTime(event.plannedDateTo).toISOString(),\n        courseIds: event.courses.map(course => course.id),\n        courseGroupIds: event.courseGroups.map(courseGroup => courseGroup.id),\n        userIds: event.userIds,\n        description: event.description,\n        title: event.title,\n        lecturerId: event.lecturer?.id,\n        addressId: event.address?.id,\n        mandatory: event.mandatory,\n      },\n      availableCourses: this.courses,\n      availableLecturers: this.lecturers,\n      availableAddresses: this.addresses,\n      updateCourseGroupsDelegate: async (selectedCourseIds: number[]): Promise<ICourseGroupResponseDto<string>[]> => {\n        return this.tenantApi.getAvailableCourseGroups(selectedCourseIds);\n      }\n    });\n\n    if(modifiedEvent && modifiedEvent.plannedDateFrom && modifiedEvent.plannedDateTo) {\n      try {\n        await this.api.updateEvent(this.tenantId, event.id, modifiedEvent);\n        this.toastService.success('pages.events.saveSuccess');\n        this.tableRefreshVersion++;\n        await this.fetchDialogData();\n      } catch(e) {\n        LogUtils.exception(e);\n        this.toastService.error('pages.events.saveError');\n      }\n    }\n  }\n\n  private async deleteEvent(event: IEventDto<string>): Promise<void> {\n    if(!this.tenantId) return;\n    const confirmation = await this.dialogService.confirmDeletion();\n    if(confirmation) {\n      try {\n        await this.api.deleteEvent(event.id);\n        this.toastService.success('pages.events.deleteSuccess');\n        this.tableRefreshVersion++;\n        await this.fetchDialogData();\n      } catch(e) {\n        if(e.message === ErrorType.CANNOT_DELETE_EVENT_WITH_ATTENDANCE) {\n          this.toastService.error('pages.events.deleteWithAttendanceError');\n        } else {\n          LogUtils.exception(e);\n          this.toastService.error('pages.events.deleteError');\n        }\n      }\n    }\n  }\n\n  private async createEvent(): Promise<void> {\n    if(!this.tenantId) return;\n    if(!this.didFetchInitialDialogData) await this.fetchDialogData();\n    const newEvent = await this.dialogService.getResultOrNull(EditEventDialog, {\n      createNew: true,\n      value: {\n        isQuickQuiz: false,\n        isQuickDistinar: false,\n        mandatory: false,\n        courseIds: [],\n        courseGroupIds: [],\n        userIds: [],\n      },\n      availableCourses: this.courses,\n      availableLecturers: this.lecturers,\n      availableAddresses: this.addresses,\n      updateCourseGroupsDelegate: async (selectedCourseIds: number[], date: string | null): Promise<ICourseGroupResponseDto<string>[]> => {\n        if(date == null) return this.tenantApi.getAvailableCourseGroups(selectedCourseIds);\n        return this.tenantApi.getAvailableCourseGroupsForDate(selectedCourseIds, date);\n      },\n    });\n\n    if(newEvent) {\n      try {\n        await this.api.createEvent(this.tenantId, newEvent as ICreateEventDto<string>);\n        this.toastService.success('pages.events.saveSuccess');\n        this.tableRefreshVersion++;\n        await this.fetchDialogData();\n      } catch(e) {\n        LogUtils.exception(e);\n        this.toastService.error('pages.events.saveError');\n      }\n    }\n  }\n\n  private async displayStudentsDialog(eventId: number): Promise<void> {\n    const userId = await this.dialogService.showStudentsDialog({ eventId });\n    if(!userId) return;\n    this.studentService.navigateToStudentInvitationsPage(userId, ENavigationTab.EVENTS);\n  }\n}\n","import { autoinject } from 'aurelia-framework';\nimport { StoreService } from 'common/aurelia-services/StoreService';\nimport { RoleName } from 'common/enum/role.enums';\nimport { IState } from '../../../store/state';\nimport { INavigationTab } from '../../elements/navigation-tabs/navigation-tabs';\n\nenum ExternalApiTabs {\n  Overview = 'overview',\n  Log = 'log',\n  CourseLicense = 'course-license',\n  SyncQueue = 'sync-queue',\n}\n\n@autoinject()\nexport class ExternalApiPage {\n  private ExternalApiTabs = ExternalApiTabs;\n\n  private activeTab: ExternalApiTabs | null = null;\n\n  constructor(\n    private storeService: StoreService<IState>,\n  ) { }\n\n  protected activate(): void {\n    this.storeService.subscribeToDetailedChanges(this, [\n      state => state.currentRole?.role,\n    ], (_, role) => {\n      this.updateTabs(role);\n    });\n  }\n\n  protected deactivate(): void {\n    this.storeService.disposeAllSubscriptions(this);\n  }\n\n  private navigationTabs: INavigationTab[] = [];\n\n  private updateTabs(role: RoleName | undefined): void {\n    const isProvider = role === RoleName.Provider;\n    const isPowerUser = role === RoleName.PowerUser || role === RoleName.Operator;\n\n    if(isProvider) this.activeTab = ExternalApiTabs.CourseLicense;\n    else if(isPowerUser) this.activeTab = ExternalApiTabs.Overview;\n    else this.activeTab = null;\n\n    this.navigationTabs = [\n      ...isProvider ? [\n        {\n          title: { titleTk: 'pages.external-api.courseLicense' },\n          active: isProvider,\n          callback: () => { this.activeTab = ExternalApiTabs.CourseLicense; },\n        }\n      ] : [],\n      ...isPowerUser ? [\n        {\n          title: { titleTk: 'pages.external-api.overview' },\n          active: isPowerUser,\n          callback: () => { this.activeTab = ExternalApiTabs.Overview; },\n        },\n        {\n          title: { titleTk: 'globals.log' },\n          active: false,\n          callback: () => { this.activeTab = ExternalApiTabs.Log; },\n        },\n        {\n          title: { titleTk: 'pages.external-api.syncQueue' },\n          active: false,\n          callback: () => { this.activeTab = ExternalApiTabs.SyncQueue; },\n        }\n      ] : [],\n    ];\n  }\n}\n","import { autoinject } from 'aurelia-framework';\nimport { Router } from 'aurelia-router';\nimport { ApiService } from 'common/aurelia-services/ApiService';\nimport { StoreService } from 'common/aurelia-services/StoreService';\nimport { OperationStatus } from 'common/dto/other/operation.response';\nimport { AppResponse } from 'common/types';\nimport { IState } from 'store/state';\nimport { SubPages } from '../course-page/course-page';\n\n@autoinject()\nexport class GalleryPage {\n  private courseId: number | null = null;\n\n  constructor(\n    private router: Router,\n    private storeService: StoreService<IState>,\n    private api: ApiService,\n  ) { }\n\n  protected activate(params: Record<string, string>): void {\n    this.courseId = parseInt(params.courseId) || null;\n  }\n\n  private navigateToCourse(): void {\n    this.router.navigateToRoute('courses/id', { id: this.courseId, subPage: SubPages.Structure });\n  }\n\n  // TODO KL-7771 remove everything after this after bunny migration\n  protected attached(): void {\n    this.storeService.subscribeToDetailedChanges(this, [\n      state => state.user.isProvider,\n    ], (_, isProvider) => {\n      if(!this.isProvider && isProvider) void this.updateMigrationStatus();\n      this.isProvider = isProvider;\n    });\n  }\n\n  protected detached(): void {\n    this.storeService.disposeAllSubscriptions(this);\n  }\n\n  private migrationStatus: { count: number; status?: OperationStatus; message?: string } = { count: 0 };\n  private isProvider = false;\n  private async updateMigrationStatus(): Promise<void> {\n    this.migrationStatus = await this.api.get<AppResponse<{ count: number; status?: OperationStatus; message?: string }>>('/videos/gallery-migration/status');\n  }\n  private async startMigration(limit?: number): Promise<void> {\n    if(this.migrationStatus.status === OperationStatus.Running) return;\n    this.migrationStatus.status = OperationStatus.Running;\n    await this.api.post(`/videos/gallery-migration/start-migration?limit=${limit ?? 0}`, {});\n    const interval = setInterval(async () => {\n      await this.updateMigrationStatus();\n      if(this.migrationStatus.status !== OperationStatus.Running) clearInterval(interval);\n    }, 500);\n  }\n  // \n}\n","import { autoinject, computedFrom, observable } from 'aurelia-framework';\nimport { StoreService } from 'common/aurelia-services/StoreService';\nimport { ICourseRatingDto } from 'common/dto/course/content/course.interface';\nimport { ColumnSortDirection, FilterOperationType } from 'common/dto/filter/filter.enum';\nimport { IFilterDto } from 'common/dto/filter/filter.interface';\nimport { IEventDto } from 'common/dto/tenant/event/tenant.event.interface';\nimport { RoleName } from 'common/enum/role.enums';\nimport { AppUtils as CommonAppUtils } from 'common/utils/AppUtils';\nimport { StringUtils } from 'common/utils/StringUtils';\nimport { CustomDialogService } from '../../../services/CustomDialogService';\nimport { DateService } from '../../../services/DateService';\nimport { CourseRatingApiService } from '../../../services/api/CourseRatingsApiService';\nimport { EventsApiService } from '../../../services/api/EventsApiService';\nimport { IOnlineStudentStats, StatisticsApiService } from '../../../services/api/StatisticsApiService';\nimport { TeacherRegistrationsApiService } from '../../../services/api/TeacherRegistrationsApiService';\nimport { setPendingTeacherRegistrationsCount } from '../../../store/actions';\nimport { IState } from '../../../store/state';\nimport { AppUtils } from '../../../utils/AppUtils';\nimport { VirtualFilterField } from '../../elements/custom-table/custom-table-helpers';\n\n@autoinject()\nexport class HomePage {\n  private isProvider: boolean = false;\n  private isOperator: boolean = false;\n  private isPowerUser: boolean = false;\n  private isLecturer: boolean = false;\n  private tenantId: number | null = null;\n  private userId: number | null = null;\n\n  private tableRefreshVersion: number = 0;\n  private appName = CommonAppUtils.getAppName();\n  private bannerImage = AppUtils.getHomeBanner();\n  private isFrp = CommonAppUtils.isFrp;\n  @observable() private hasEvents: boolean = false;\n  private showEvents: boolean = false;\n\n  private createdInvitations = 0;\n  private usedInvitations = 0;\n  private pendingTeacherRegistrationsCount = 0;\n\n  private onlineStudentStats?: IOnlineStudentStats;\n  private recentlyRatedCourses: ICourseRatingDto<string>[] | null = null;\n\n  private filter: IFilterDto<IEventDto<string>> = {\n    limit: 3,\n    offset: 0,\n    order: [\n      {\n        field: 'plannedDateFrom',\n        dir: ColumnSortDirection.ASC,\n      }\n    ],\n    filter: [\n      {\n        field: 'plannedDateFrom',\n        type: FilterOperationType.Gte,\n        value: this.dateService.formatDateForApiFilter(new Date())\n      }\n    ]\n  };\n\n  constructor(\n    private api: EventsApiService,\n    private dateService: DateService,\n    private statisticsApiService: StatisticsApiService,\n    private customDialogService: CustomDialogService,\n    private storeService: StoreService<IState>,\n    private courseRatingApiService: CourseRatingApiService,\n    private teacherRegistrationsApiService: TeacherRegistrationsApiService,\n  ) { }\n\n  protected attached(): void {\n    this.storeService.subscribeToDetailedChanges(this, [\n      state => state.currentRole?.role,\n    ], (_state, currentRole) => {\n      this.isProvider = currentRole === RoleName.Provider;\n      this.isOperator = currentRole === RoleName.Operator;\n      this.isPowerUser = currentRole === RoleName.PowerUser;\n      this.isLecturer = currentRole === RoleName.Lecturer;\n      if(currentRole) this.fetchStats(currentRole);\n    });\n\n    this.storeService.subscribeToDetailedChanges(this, [\n      state => state.currentRole?.tenantId ?? null,\n      state => state.user.id ?? null,\n    ], (_state, tenantId, userId) => {\n      this.tenantId = tenantId;\n      this.userId = userId;\n      if(this.tenantId && this.userId) {\n        this.hasEvents = false;\n        this.evaluateHasEvents(this.tenantId, this.userId);\n      }\n    });\n\n    this.storeService.subscribeToDetailedChanges(this, [\n      state => !!state.currentTenant?.showEvents\n    ], (_state, showEvents) => {\n      this.showEvents = showEvents;\n    });\n  }\n\n  protected detached(): void {\n    this.storeService.disposeAllSubscriptions(this);\n  }\n\n  @computedFrom(\n    'hasEvents',\n    'isOperator',\n    'isPowerUser',\n    'isLecturer',\n    'tenantId',\n    'isFrp'\n  )\n  private get showUpcomingEvents(): boolean {\n    return this.hasEvents && !this.isFrp && (this.isOperator || this.isPowerUser || this.isLecturer);\n  }\n\n  private async evaluateHasEvents(tenantId: number, userId: number): Promise<void> {\n    this.filter.filter = this.filter.filter?.filter(filter => filter.field !== 'lecturerId' as VirtualFilterField);\n    this.filter.filter?.push({\n      field: 'lecturerId' as VirtualFilterField,\n      type: FilterOperationType.Eq,\n      value: userId\n    });\n    this.hasEvents = (await this.api.getEvents(tenantId, { ...this.filter, limit: 1 })).rows.length > 0;\n  }\n\n  private async fetchTableData(): Promise<{ total: number; data: Array<IEventDto<string>> }> {\n    const results = await this.api.getEvents(this.tenantId!, this.filter);\n    return { data: results.rows, total: results.count };\n  }\n\n  private async fetchStats(role: RoleName): Promise<void> {\n    if(role === RoleName.Provider) {\n      this.recentlyRatedCourses = (await this.courseRatingApiService.getCourseRatings({\n        limit: 3,\n        order: [{\n          field: 'updatedAt',\n          dir: ColumnSortDirection.DESC,\n        }]\n      })).data;\n      const invitationStatsResponse = await this.statisticsApiService.getInvitationStats();\n      this.createdInvitations = invitationStatsResponse.created;\n      this.usedInvitations = invitationStatsResponse.used;\n    }\n\n    if([RoleName.Provider, RoleName.Operator].includes(role)) {\n      this.onlineStudentStats = await this.statisticsApiService.getOnlineStudentStats();\n    }\n\n    if([RoleName.Provider, RoleName.Operator, RoleName.PowerUser].includes(role)) {\n      this.pendingTeacherRegistrationsCount = await this.teacherRegistrationsApiService.getPendingTeacherRegistrationsCount();\n      await this.storeService.dispatch(setPendingTeacherRegistrationsCount, this.pendingTeacherRegistrationsCount);\n    }\n  }\n\n  private async downloadStatistics(): Promise<void> {\n    const csvType = await this.customDialogService.showChooseCsvTypeDialog();\n    if(!csvType) return;\n    this.statisticsApiService.exportInvitationStats(csvType);\n  }\n\n  private async downloadRatings(): Promise<void> {\n    const csvType = await this.customDialogService.showChooseCsvTypeDialog();\n    if(!csvType) return;\n    this.courseRatingApiService.exportRatingsCsv(csvType);\n  }\n\n  private formatNumberGrouped(value: number): string {\n    return StringUtils.formatNumberGrouped(value);\n  }\n}\n","import { autoinject, computedFrom } from 'aurelia-framework';\nimport { connectTo } from 'aurelia-store';\nimport { IRoleAssignment } from 'common/dto/auth/auth.interface';\nimport { LogUtils } from 'common/utils/LogUtils';\nimport { AuthApiService } from 'services/api/AuthApiService';\nimport { ToastService } from 'services/ToastService';\nimport { IState } from 'store/state';\n\n@connectTo()\n@autoinject\nexport class InvitesPage {\n  private state!: IState;\n\n  constructor(private authApiService: AuthApiService, private toastService: ToastService) {}\n\n  @computedFrom('state.roles')\n  protected get pendingRoles(): IRoleAssignment<string>[] {\n    return this.state.roles.filter(r => r.pending);\n  }\n\n  protected async acceptInvite(tenantId: number): Promise<void> {\n    try {\n      await this.authApiService.acceptInvite(tenantId);\n      this.toastService.success('pages.invites.acceptSuccess');\n    } catch(e) {\n      this.toastService.error('pages.invites.acceptError');\n      LogUtils.exception(e);\n    }\n  }\n}\n"],"names":[],"sourceRoot":""}