oip-common 0.10.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -411,6 +411,7 @@ class LayoutService {
411
411
  return ngDateFormat;
412
412
  }, ...(ngDevMode ? [{ debugName: "monthFormat" }] : []));
413
413
  this.timeZone = computed(() => this.layoutConfig().timeZone, ...(ngDevMode ? [{ debugName: "timeZone" }] : []));
414
+ this.firstDayOfWeek = computed(() => this.layoutConfig().firstDayOfWeek ?? 1, ...(ngDevMode ? [{ debugName: "firstDayOfWeek" }] : []));
414
415
  this.adminMode = computed(() => this.layoutConfig().adminMode ?? false, ...(ngDevMode ? [{ debugName: "adminMode" }] : []));
415
416
  this.transitionComplete = signal(false, ...(ngDevMode ? [{ debugName: "transitionComplete" }] : []));
416
417
  /**
@@ -443,6 +444,7 @@ class LayoutService {
443
444
  if (appConfigUiString != null) {
444
445
  const config = JSON.parse(appConfigUiString);
445
446
  config.timeZone ??= Intl.DateTimeFormat().resolvedOptions().timeZone;
447
+ config.firstDayOfWeek ??= 1;
446
448
  return config;
447
449
  }
448
450
  return {
@@ -456,6 +458,7 @@ class LayoutService {
456
458
  timeFormat: 'HH:mm:ss',
457
459
  dateTimeFormat: 'yyyy-MM-dd HH:mm:ss',
458
460
  timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
461
+ firstDayOfWeek: 1,
459
462
  adminMode: false
460
463
  };
461
464
  }
@@ -810,6 +813,8 @@ class L10nService {
810
813
  // Global dictionaries are available before the first `use()` call, so TranslateHttpLoader is never asked for them.
811
814
  L10nService.registerTranslations(globalTranslations);
812
815
  Object.entries(globalTranslations).forEach(([lang, translations]) => this.mergeTranslation(lang, translations));
816
+ // The first day of the week is a user setting, it overrides the value of the language dictionary.
817
+ effect(() => this.primeNg.setTranslation({ firstDayOfWeek: this.layoutService.firstDayOfWeek() }));
813
818
  // Static translations are merged once per language, so they have to be re-merged on every language change.
814
819
  this.translateService.onLangChange.subscribe((event) => {
815
820
  L10nService.staticTranslations.forEach((byLang) => {
@@ -904,7 +909,7 @@ class L10nService {
904
909
  if (key) {
905
910
  this.get(key);
906
911
  }
907
- this.translateService.use(selectedLanguage);
912
+ this.translateService.use(selectedLanguage).subscribe(() => this.applyPrimeNgTranslation());
908
913
  }
909
914
  init(languages) {
910
915
  this.availableLanguages = languages;
@@ -913,9 +918,17 @@ class L10nService {
913
918
  this.translateService.setDefaultLang(lang);
914
919
  this.translateService.use(lang).subscribe(() => {
915
920
  this.loadComponentTranslations('app-info');
916
- this.translateService.get('primeng').subscribe((res) => this.primeNg.setTranslation(res));
921
+ this.applyPrimeNgTranslation();
917
922
  });
918
923
  }
924
+ /**
925
+ * Passes the PrimeNG dictionary of the current language to PrimeNG, keeping the user's first day of the week
926
+ */
927
+ applyPrimeNgTranslation() {
928
+ this.translateService
929
+ .get('primeng')
930
+ .subscribe((res) => this.primeNg.setTranslation({ ...res, firstDayOfWeek: this.layoutService.firstDayOfWeek() }));
931
+ }
919
932
  instant(key, interpolateParams) {
920
933
  return this.translateService.instant(key, interpolateParams);
921
934
  }
@@ -1001,7 +1014,9 @@ class BffSecurityService {
1001
1014
  authorize(returnUrl) {
1002
1015
  const normalizedReturnUrl = this.normalizeReturnUrl(returnUrl);
1003
1016
  this.replaceWithUnauthorizedUrl(normalizedReturnUrl);
1004
- const form = this.createPostForm(this.buildUrl('api/security/create-auth-session'));
1017
+ // The return url travels with the request: when a guard rejects the initial navigation, the router
1018
+ // resets the address after this call, so the page address sent as Referer no longer carries it.
1019
+ const form = this.createPostForm(this.buildUrl(`api/security/create-auth-session?returnUrl=${encodeURIComponent(normalizedReturnUrl)}`));
1005
1020
  document.body.appendChild(form);
1006
1021
  form.submit();
1007
1022
  }
@@ -1891,9 +1906,10 @@ class SecurityApi extends HttpClient {
1891
1906
  format: "json",
1892
1907
  ...params,
1893
1908
  });
1894
- this.createAuthSession = (params = {}) => this.request({
1909
+ this.createAuthSession = (query, params = {}) => this.request({
1895
1910
  path: `/api/security/create-auth-session`,
1896
1911
  method: "POST",
1912
+ query: query,
1897
1913
  secure: true,
1898
1914
  ...params,
1899
1915
  });
@@ -2043,14 +2059,14 @@ class SecurityComponent {
2043
2059
  <div class="font-semibold text-xl">
2044
2060
  {{ 'securityComponent.security' | translate }}
2045
2061
  </div>
2046
- @for (item of securityData; track item.name) {
2062
+ @for (item of securityData; track item.code) {
2047
2063
  <div class="flex flex-col gap-2">
2048
- <label htmlFor="oip-security-multiselect-{{ item.name }}">
2064
+ <label htmlFor="oip-security-multiselect-{{ item.code }}">
2049
2065
  {{ item.name }}
2050
2066
  <span class="pi pi-question-circle" pTooltip="{{ item.description }}" tooltipPosition="right"></span>
2051
2067
  </label>
2052
2068
  <p-multiSelect
2053
- id="oip-security-multiselect-{{ item.name }}"
2069
+ id="oip-security-multiselect-{{ item.code }}"
2054
2070
  placeholder="{{ 'securityComponent.selectRoles' | translate }}"
2055
2071
  [maxSelectedLabels]="10"
2056
2072
  [options]="roles"
@@ -2081,14 +2097,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
2081
2097
  <div class="font-semibold text-xl">
2082
2098
  {{ 'securityComponent.security' | translate }}
2083
2099
  </div>
2084
- @for (item of securityData; track item.name) {
2100
+ @for (item of securityData; track item.code) {
2085
2101
  <div class="flex flex-col gap-2">
2086
- <label htmlFor="oip-security-multiselect-{{ item.name }}">
2102
+ <label htmlFor="oip-security-multiselect-{{ item.code }}">
2087
2103
  {{ item.name }}
2088
2104
  <span class="pi pi-question-circle" pTooltip="{{ item.description }}" tooltipPosition="right"></span>
2089
2105
  </label>
2090
2106
  <p-multiSelect
2091
- id="oip-security-multiselect-{{ item.name }}"
2107
+ id="oip-security-multiselect-{{ item.code }}"
2092
2108
  placeholder="{{ 'securityComponent.selectRoles' | translate }}"
2093
2109
  [maxSelectedLabels]="10"
2094
2110
  [options]="roles"
@@ -2905,11 +2921,18 @@ class UserService {
2905
2921
  }
2906
2922
  this.requestedPhotoKey = photoKey;
2907
2923
  this.userProfileApi.getUserPhoto().then((data) => {
2908
- this.createImageFromBlob(data);
2924
+ // 204 No Content comes back as an empty blob: the user hasn't uploaded a photo yet,
2925
+ // so the avatar falls back to initials.
2926
+ const photo = data;
2927
+ if (!photo?.size) {
2928
+ this.photo = null;
2929
+ this.photoLoaded = false;
2930
+ return;
2931
+ }
2932
+ this.createImageFromBlob(photo);
2909
2933
  this.photoLoaded = true;
2910
2934
  }, (error) => {
2911
- // A 404 just means the user hasn't uploaded a photo yet (e.g. on first login) —
2912
- // that's an expected state, not a failure, so fall back to initials silently.
2935
+ // Older backends answer 404 when there is no photo — that's an expected state, not a failure.
2913
2936
  if (error?.status !== 404) {
2914
2937
  this.msgService.errorFromException(error, this.translateService.instant('userService.failedToLoadPhoto'));
2915
2938
  }
@@ -3115,6 +3138,15 @@ class NotificationApi extends HttpClient {
3115
3138
  format: "json",
3116
3139
  ...params,
3117
3140
  });
3141
+ this.createTestNotification = (data, params = {}) => this.request({
3142
+ path: `/api/notification/create-test-notification`,
3143
+ method: "POST",
3144
+ body: data,
3145
+ secure: true,
3146
+ type: ContentType.Json,
3147
+ format: "json",
3148
+ ...params,
3149
+ });
3118
3150
  }
3119
3151
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: NotificationApi, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
3120
3152
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: NotificationApi }); }
@@ -3325,10 +3357,10 @@ class UserNotificationsComponent {
3325
3357
  } @else {
3326
3358
  <div class="flex flex-col gap-3 max-h-[26rem] overflow-y-auto pr-1">
3327
3359
  @for (notification of notifications; track notification.notificationUserId) {
3328
- <div class="border border-surface-200 dark:border-surface-700 rounded-md p-3">
3360
+ <div class="border border-surface-200 dark:border-surface-700 rounded-md p-3" qa-id="oip-user-notifications-item">
3329
3361
  <div class="flex items-start justify-between gap-3">
3330
3362
  <div class="min-w-0">
3331
- <div class="font-medium leading-snug break-words">
3363
+ <div class="font-medium leading-snug break-words" qa-id="oip-user-notifications-subject">
3332
3364
  {{ notification.subject }}
3333
3365
  </div>
3334
3366
  <div class="text-xs text-surface-500 mt-1">
@@ -3350,6 +3382,7 @@ class UserNotificationsComponent {
3350
3382
  <div class="flex justify-end mt-3">
3351
3383
  <p-button
3352
3384
  icon="pi pi-check"
3385
+ qa-id="oip-user-notifications-mark-as-read"
3353
3386
  size="small"
3354
3387
  [label]="'userNotifications.markAsRead' | translate"
3355
3388
  [loading]="readingNotificationId === notification.notificationUserId"
@@ -3361,6 +3394,7 @@ class UserNotificationsComponent {
3361
3394
 
3362
3395
  <p-paginator
3363
3396
  class="mt-3"
3397
+ qa-id="oip-user-notifications-paginator"
3364
3398
  [first]="skip"
3365
3399
  [rows]="take"
3366
3400
  [rowsPerPageOptions]="[5, 10, 20]"
@@ -3430,10 +3464,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
3430
3464
  } @else {
3431
3465
  <div class="flex flex-col gap-3 max-h-[26rem] overflow-y-auto pr-1">
3432
3466
  @for (notification of notifications; track notification.notificationUserId) {
3433
- <div class="border border-surface-200 dark:border-surface-700 rounded-md p-3">
3467
+ <div class="border border-surface-200 dark:border-surface-700 rounded-md p-3" qa-id="oip-user-notifications-item">
3434
3468
  <div class="flex items-start justify-between gap-3">
3435
3469
  <div class="min-w-0">
3436
- <div class="font-medium leading-snug break-words">
3470
+ <div class="font-medium leading-snug break-words" qa-id="oip-user-notifications-subject">
3437
3471
  {{ notification.subject }}
3438
3472
  </div>
3439
3473
  <div class="text-xs text-surface-500 mt-1">
@@ -3455,6 +3489,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
3455
3489
  <div class="flex justify-end mt-3">
3456
3490
  <p-button
3457
3491
  icon="pi pi-check"
3492
+ qa-id="oip-user-notifications-mark-as-read"
3458
3493
  size="small"
3459
3494
  [label]="'userNotifications.markAsRead' | translate"
3460
3495
  [loading]="readingNotificationId === notification.notificationUserId"
@@ -3466,6 +3501,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
3466
3501
 
3467
3502
  <p-paginator
3468
3503
  class="mt-3"
3504
+ qa-id="oip-user-notifications-paginator"
3469
3505
  [first]="skip"
3470
3506
  [rows]="take"
3471
3507
  [rowsPerPageOptions]="[5, 10, 20]"
@@ -3493,6 +3529,13 @@ class ApplicationsApi extends HttpClient {
3493
3529
  format: "json",
3494
3530
  ...params,
3495
3531
  });
3532
+ this.getAllApplicationRegistryItems = (params = {}) => this.request({
3533
+ path: `/api/applications/get-all-application-registry-items`,
3534
+ method: "GET",
3535
+ secure: true,
3536
+ format: "json",
3537
+ ...params,
3538
+ });
3496
3539
  this.getApplicationRegistryItemByCode = ({ code, ...query }, params = {}) => this.request({
3497
3540
  path: `/api/applications/get-application-registry-item-by-code/${code}`,
3498
3541
  method: "GET",
@@ -4037,6 +4080,13 @@ class MenuApi extends HttpClient {
4037
4080
  secure: true,
4038
4081
  ...params,
4039
4082
  });
4083
+ this.copyModuleInstance = ({ id, ...query }, params = {}) => this.request({
4084
+ path: `/api/menu/copy-module-instance/${id}`,
4085
+ method: "POST",
4086
+ secure: true,
4087
+ format: "json",
4088
+ ...params,
4089
+ });
4040
4090
  this.changeOrder = (query, params = {}) => this.request({
4041
4091
  path: `/api/menu/change-order`,
4042
4092
  method: "POST",
@@ -4191,9 +4241,9 @@ class StartPageService {
4191
4241
  if (item.separator) {
4192
4242
  continue;
4193
4243
  }
4194
- if (item.items?.length) {
4244
+ if (item.isFolder || item.items?.length) {
4195
4245
  // A folder is never a landing page itself, its children are.
4196
- result.push(...this.collectNavigableLeaves(item.items));
4246
+ result.push(...this.collectNavigableLeaves(item.items ?? []));
4197
4247
  continue;
4198
4248
  }
4199
4249
  // A module without a real route would resolve back to the empty path and loop.
@@ -4318,13 +4368,22 @@ class MenuItemComponent {
4318
4368
  {
4319
4369
  label: this.localization.new,
4320
4370
  icon: PrimeIcons.PLUS,
4321
- command: (event) => this.newClick(event)
4371
+ command: (event) => this.newClick(event),
4372
+ // Only a folder can have child items.
4373
+ visible: !!item.isFolder
4322
4374
  },
4323
4375
  {
4324
4376
  label: this.localization.edit,
4325
4377
  icon: PrimeIcons.FILE_EDIT,
4326
4378
  command: (event) => this.editClick(event)
4327
4379
  },
4380
+ {
4381
+ label: this.localization.copy,
4382
+ icon: PrimeIcons.COPY,
4383
+ command: () => this.copyItem(item),
4384
+ // Only a leaf instance can be copied, folders with children are not supported.
4385
+ visible: !item.items?.length
4386
+ },
4328
4387
  { separator: true },
4329
4388
  {
4330
4389
  label: this.localization.delete,
@@ -4356,10 +4415,10 @@ class MenuItemComponent {
4356
4415
  this.contextMenu.show($event);
4357
4416
  }
4358
4417
  /**
4359
- * Builds the start page entries of the context menu. Only a navigable leaf can be a start page.
4418
+ * Builds the start page entries of the context menu. Only a navigable non-folder item can be a start page.
4360
4419
  */
4361
4420
  getStartModuleItems(item) {
4362
- if (!item?.routerLink || item.items?.length) {
4421
+ if (!item?.routerLink || item.isFolder || item.items?.length) {
4363
4422
  return [];
4364
4423
  }
4365
4424
  return item.isStart
@@ -4388,6 +4447,11 @@ class MenuItemComponent {
4388
4447
  this.msgService.success(this.localization.unsetStartModuleSuccessMessage);
4389
4448
  await this.menuService.loadMenu();
4390
4449
  }
4450
+ async copyItem(item) {
4451
+ await this.menuDataService.copyModuleInstance({ id: item.moduleInstanceId });
4452
+ this.msgService.success(this.localization.copyItemSuccessMessage);
4453
+ await this.menuService.loadMenu();
4454
+ }
4391
4455
  deleteItem(event) {
4392
4456
  this.confirmationService.confirm({
4393
4457
  header: this.localization.deleteItemConfirmHeader,
@@ -4677,6 +4741,7 @@ class MenuItemCreateDialogComponent {
4677
4741
  this.msgService = inject(MsgService);
4678
4742
  this.visibleChange = new EventEmitter();
4679
4743
  this.modules = [];
4744
+ this.moduleOptions = [];
4680
4745
  this.iconOptions = Object.values(PrimeIcons)
4681
4746
  .filter((icon) => typeof icon === 'string')
4682
4747
  .map((icon) => ({
@@ -4684,14 +4749,35 @@ class MenuItemCreateDialogComponent {
4684
4749
  value: icon
4685
4750
  }))
4686
4751
  .sort((left, right) => left.label.localeCompare(right.label));
4687
- this.selectIcon = 'pi pi-box';
4752
+ this.selectIcon = MenuItemCreateDialogComponent.defaultIcon;
4688
4753
  this.saving = false;
4689
4754
  }
4755
+ static { this.defaultIcon = 'pi pi-box'; }
4690
4756
  get canSave() {
4691
4757
  return !this.saving && !!this.selectModule;
4692
4758
  }
4693
4759
  async ngOnInit() {
4694
4760
  this.modules = await this.menu.getModules();
4761
+ this.updateModuleOptions();
4762
+ }
4763
+ /**
4764
+ * Restricts the module list to the folder when the item is created at the menu root,
4765
+ * since only a folder can be placed there.
4766
+ */
4767
+ updateModuleOptions() {
4768
+ const isRoot = !this.menuService.contextMenuItem;
4769
+ this.moduleOptions = isRoot ? this.modules.filter((module) => module.isFolder) : this.modules;
4770
+ if (isRoot && this.moduleOptions.length === 1) {
4771
+ this.selectModule = this.moduleOptions[0].key;
4772
+ this.onModuleChange(this.selectModule);
4773
+ }
4774
+ else if (!this.moduleOptions.some((module) => module.key === this.selectModule)) {
4775
+ this.selectModule = undefined;
4776
+ }
4777
+ }
4778
+ onModuleChange(moduleId) {
4779
+ const module = this.modules.find((item) => item.key === moduleId);
4780
+ this.selectIcon = module?.icon || MenuItemCreateDialogComponent.defaultIcon;
4695
4781
  }
4696
4782
  changeVisible() {
4697
4783
  this.visible = !this.visible;
@@ -4728,6 +4814,7 @@ class MenuItemCreateDialogComponent {
4728
4814
  this.visibleChange.emit(this.visible);
4729
4815
  }
4730
4816
  showDialog() {
4817
+ this.updateModuleOptions();
4731
4818
  this.visible = true;
4732
4819
  this.visibleChange.emit(this.visible);
4733
4820
  }
@@ -4774,8 +4861,9 @@ class MenuItemCreateDialogComponent {
4774
4861
  placeholder="{{ 'menuItemCreateDialogComponent.selectModule' | translate }}"
4775
4862
  scrollHeight="18rem"
4776
4863
  [filter]="true"
4777
- [options]="modules"
4778
- [(ngModel)]="selectModule" />
4864
+ [options]="moduleOptions"
4865
+ [(ngModel)]="selectModule"
4866
+ (ngModelChange)="onModuleChange($event)" />
4779
4867
  </div>
4780
4868
  <div class="flex items-center gap-4 mb-4">
4781
4869
  <label class="font-semibold w-1/3" for="oip-menu-item-create-dialog-icon">
@@ -4871,8 +4959,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
4871
4959
  placeholder="{{ 'menuItemCreateDialogComponent.selectModule' | translate }}"
4872
4960
  scrollHeight="18rem"
4873
4961
  [filter]="true"
4874
- [options]="modules"
4875
- [(ngModel)]="selectModule" />
4962
+ [options]="moduleOptions"
4963
+ [(ngModel)]="selectModule"
4964
+ (ngModelChange)="onModuleChange($event)" />
4876
4965
  </div>
4877
4966
  <div class="flex items-center gap-4 mb-4">
4878
4967
  <label class="font-semibold w-1/3" for="oip-menu-item-create-dialog-icon">
@@ -5197,6 +5286,8 @@ var menuComponent$1 = {
5197
5286
  var menuItemComponent$1 = {
5198
5287
  "new": "Add",
5199
5288
  edit: "Edit",
5289
+ copy: "Copy",
5290
+ copyItemSuccessMessage: "Copied successfully",
5200
5291
  "delete": "Delete",
5201
5292
  deleteItemConfirmHeader: "Delete",
5202
5293
  deleteItemConfirmMessage: "Are you sure you want to delete the item?",
@@ -5246,6 +5337,8 @@ var menuComponent = {
5246
5337
  var menuItemComponent = {
5247
5338
  "new": "Добавить",
5248
5339
  edit: "Редактировать",
5340
+ copy: "Копировать",
5341
+ copyItemSuccessMessage: "Успешно скопировано",
5249
5342
  "delete": "Удалить",
5250
5343
  deleteItemConfirmHeader: "Удаление",
5251
5344
  deleteItemConfirmMessage: "Вы уверены что хотите удалить элемент?",
@@ -6534,16 +6627,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
6534
6627
  }]
6535
6628
  }] });
6536
6629
 
6537
- var userProfile_component = /*#__PURE__*/Object.freeze({
6538
- __proto__: null,
6539
- UserProfileComponent: UserProfileComponent
6540
- });
6541
-
6542
6630
  var config$1 = {
6543
6631
  all: "All",
6544
6632
  applicationManagement: "Application management",
6545
6633
  dateFormat: "Date format",
6546
6634
  dateTimeFormat: "Date and time format:",
6635
+ firstDayOfWeek: "First day of the week",
6547
6636
  goTo: "Go to",
6548
6637
  localization: "Localization",
6549
6638
  menu: "Menu",
@@ -6565,6 +6654,7 @@ var config = {
6565
6654
  applicationManagement: "Управление приложениями",
6566
6655
  dateFormat: "Формат даты",
6567
6656
  dateTimeFormat: "Формат даты и времени:",
6657
+ firstDayOfWeek: "Первый день недели",
6568
6658
  goTo: "Перейти",
6569
6659
  localization: "Локализация",
6570
6660
  menu: "Меню",
@@ -6593,10 +6683,20 @@ class ConfigComponent {
6593
6683
  this.timeFormats = ['HH:mm:ss', 'HH:mm'];
6594
6684
  // @ts-ignore
6595
6685
  this.allTimeZones = Intl.supportedValuesOf('timeZone');
6686
+ /** Days of the week starting from Monday, named in the current language. */
6687
+ this.weekDays = computed(() => {
6688
+ const format = new Intl.DateTimeFormat(this.layoutService.language() ?? 'en', { weekday: 'long' });
6689
+ // 1 January 2024 is a Monday, so day N of that week falls on N January (Sunday - on 7 January).
6690
+ return [1, 2, 3, 4, 5, 6, 0].map((day) => {
6691
+ const name = format.format(new Date(2024, 0, day || 7));
6692
+ return { value: day, label: name.charAt(0).toUpperCase() + name.slice(1) };
6693
+ });
6694
+ }, ...(ngDevMode ? [{ debugName: "weekDays" }] : []));
6596
6695
  this.selectedLanguage = this.layoutService.language();
6597
6696
  this.selectedDateFormat = this.layoutService.dateFormat();
6598
6697
  this.selectedTimeFormat = this.layoutService.timeFormat();
6599
6698
  this.selectedTimeZone = this.layoutService.timeZone();
6699
+ this.selectedFirstDayOfWeek = this.layoutService.firstDayOfWeek();
6600
6700
  }
6601
6701
  changeLanguage() {
6602
6702
  this.layoutService.layoutConfig.update((config) => ({
@@ -6611,6 +6711,9 @@ class ConfigComponent {
6611
6711
  changeTimeFormat() {
6612
6712
  this.layoutService.layoutConfig.update((config) => ({ ...config, timeFormat: this.selectedTimeFormat }));
6613
6713
  }
6714
+ changeFirstDayOfWeek() {
6715
+ this.layoutService.layoutConfig.update((config) => ({ ...config, firstDayOfWeek: this.selectedFirstDayOfWeek }));
6716
+ }
6614
6717
  changeTimeZone() {
6615
6718
  this.layoutService.layoutConfig.update((config) => ({ ...config, timeZone: this.selectedTimeZone }));
6616
6719
  }
@@ -6681,6 +6784,16 @@ class ConfigComponent {
6681
6784
  [placeholder]="'config.timeFormat' | translate"
6682
6785
  [(ngModel)]="selectedTimeFormat"
6683
6786
  (onChange)="changeTimeFormat()" />
6787
+ <div class="mt-5">{{ 'config.firstDayOfWeek' | translate }}</div>
6788
+ <p-select
6789
+ class="w-full md:w-56"
6790
+ optionLabel="label"
6791
+ optionValue="value"
6792
+ qa-id="oip-app-config-first-day-of-week-select"
6793
+ [options]="weekDays()"
6794
+ [placeholder]="'config.firstDayOfWeek' | translate"
6795
+ [(ngModel)]="selectedFirstDayOfWeek"
6796
+ (onChange)="changeFirstDayOfWeek()" />
6684
6797
  <div class="mt-5">{{ 'config.timeZone' | translate }}</div>
6685
6798
  <p-select
6686
6799
  class="w-full md:w-56"
@@ -6799,6 +6912,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
6799
6912
  [placeholder]="'config.timeFormat' | translate"
6800
6913
  [(ngModel)]="selectedTimeFormat"
6801
6914
  (onChange)="changeTimeFormat()" />
6915
+ <div class="mt-5">{{ 'config.firstDayOfWeek' | translate }}</div>
6916
+ <p-select
6917
+ class="w-full md:w-56"
6918
+ optionLabel="label"
6919
+ optionValue="value"
6920
+ qa-id="oip-app-config-first-day-of-week-select"
6921
+ [options]="weekDays()"
6922
+ [placeholder]="'config.firstDayOfWeek' | translate"
6923
+ [(ngModel)]="selectedFirstDayOfWeek"
6924
+ (onChange)="changeFirstDayOfWeek()" />
6802
6925
  <div class="mt-5">{{ 'config.timeZone' | translate }}</div>
6803
6926
  <p-select
6804
6927
  class="w-full md:w-56"
@@ -8379,7 +8502,7 @@ var applications$1 = {
8379
8502
  "delete": "Delete"
8380
8503
  },
8381
8504
  messages: {
8382
- requiredFields: "Fill code, name and Base URL",
8505
+ requiredFields: "Fill code, name, Base URL and Internal Base URL",
8383
8506
  createSuccess: "Application created",
8384
8507
  updateSuccess: "Application updated",
8385
8508
  deleteSuccess: "Application deleted"
@@ -8425,7 +8548,7 @@ var applications = {
8425
8548
  "delete": "Удалить"
8426
8549
  },
8427
8550
  messages: {
8428
- requiredFields: "Заполните код, название и Base URL",
8551
+ requiredFields: "Заполните код, название, Base URL и Internal Base URL",
8429
8552
  createSuccess: "Приложение создано",
8430
8553
  updateSuccess: "Приложение обновлено",
8431
8554
  deleteSuccess: "Приложение удалено"
@@ -8473,7 +8596,7 @@ class ApplicationsComponent {
8473
8596
  this.loading = true;
8474
8597
  this.cancelAllEdits();
8475
8598
  try {
8476
- this.applications = (await this.applicationsApi.getApplicationRegistryItems()).map((application) => ({ ...application }));
8599
+ this.applications = (await this.applicationsApi.getAllApplicationRegistryItems()).map((application) => ({ ...application }));
8477
8600
  this.syncVisibleApplications();
8478
8601
  }
8479
8602
  catch (error) {
@@ -8533,7 +8656,7 @@ class ApplicationsComponent {
8533
8656
  return;
8534
8657
  }
8535
8658
  const request = this.normalizeEditModel(editModel);
8536
- if (!request.code || !request.displayName || !request.baseUrl) {
8659
+ if (!request.code || !request.displayName || !request.baseUrl || !request.internalBaseUrl) {
8537
8660
  this.msgService.error(this.t('applications.messages.requiredFields'));
8538
8661
  return;
8539
8662
  }
@@ -9783,6 +9906,7 @@ class DiscussionComponent {
9783
9906
  <textarea
9784
9907
  class="min-h-28 w-full"
9785
9908
  pTextarea
9909
+ qa-id="oip-discussion-new-comment"
9786
9910
  rows="5"
9787
9911
  [autoResize]="true"
9788
9912
  [placeholder]="'discussionComponent.writeCommentPlaceholder' | translate"
@@ -9806,6 +9930,7 @@ class DiscussionComponent {
9806
9930
  <div class="flex flex-col gap-3">
9807
9931
  @for (candidate of mentionSuggestions; track candidate.userId) {
9808
9932
  <button
9933
+ qa-id="oip-discussion-mention-suggestion"
9809
9934
  class="flex w-full items-center justify-between gap-4 rounded-xl border border-transparent px-3 py-3 text-left transition hover:border-primary-200 hover:bg-primary-50 dark:hover:border-primary-800 dark:hover:bg-primary-950/30"
9810
9935
  type="button"
9811
9936
  (click)="insertMention(candidate)">
@@ -9824,7 +9949,8 @@ class DiscussionComponent {
9824
9949
  <div class="flex flex-col gap-3">
9825
9950
  @for (file of pendingFiles; track file.name + file.size) {
9826
9951
  <div
9827
- class="flex flex-col gap-3 rounded-2xl border border-surface-200 px-4 py-3 sm:flex-row sm:items-center sm:justify-between dark:border-surface-700">
9952
+ class="flex flex-col gap-3 rounded-2xl border border-surface-200 px-4 py-3 sm:flex-row sm:items-center sm:justify-between dark:border-surface-700"
9953
+ qa-id="oip-discussion-pending-file">
9828
9954
  <div class="inline-flex items-center gap-2">
9829
9955
  <i class="pi pi-file"></i>
9830
9956
  <span class="break-all">{{ file.name }}</span>
@@ -9857,10 +9983,11 @@ class DiscussionComponent {
9857
9983
  severity="secondary"
9858
9984
  [label]="'discussionComponent.attachFiles' | translate"
9859
9985
  [text]="true" />
9860
- <input multiple type="file" (change)="onFilesSelected($event)" />
9986
+ <input multiple qa-id="oip-discussion-attach-input" type="file" (change)="onFilesSelected($event)" />
9861
9987
  </label>
9862
9988
  <p-button
9863
9989
  icon="pi pi-send"
9990
+ qa-id="oip-discussion-send"
9864
9991
  [disabled]="submitting || !newComment.trim()"
9865
9992
  [label]="'discussionComponent.send' | translate"
9866
9993
  [loading]="submitting"
@@ -9884,7 +10011,7 @@ class DiscussionComponent {
9884
10011
  } @else {
9885
10012
  <div class="flex flex-col gap-4">
9886
10013
  @for (comment of comments; track comment.commentId) {
9887
- <p-card class="block">
10014
+ <p-card class="block" qa-id="oip-discussion-comment">
9888
10015
  <ng-template pTemplate="content">
9889
10016
  <div class="grid gap-4 md:grid-cols-[3.2rem_minmax(0,1fr)]">
9890
10017
  <div>
@@ -9905,6 +10032,7 @@ class DiscussionComponent {
9905
10032
  @if (comment.isEdited) {
9906
10033
  <p-tag
9907
10034
  icon="pi pi-pencil"
10035
+ qa-id="oip-discussion-comment-edited"
9908
10036
  severity="warn"
9909
10037
  [value]="'discussionComponent.edited' | translate" />
9910
10038
  }
@@ -9912,6 +10040,7 @@ class DiscussionComponent {
9912
10040
  @if (comment.canEdit) {
9913
10041
  <p-button
9914
10042
  icon="pi pi-pencil"
10043
+ qa-id="oip-discussion-comment-edit"
9915
10044
  severity="secondary"
9916
10045
  [rounded]="true"
9917
10046
  [text]="true"
@@ -9920,6 +10049,7 @@ class DiscussionComponent {
9920
10049
  @if (comment.historyCount > 0) {
9921
10050
  <p-button
9922
10051
  icon="pi pi-history"
10052
+ qa-id="oip-discussion-comment-history"
9923
10053
  severity="secondary"
9924
10054
  [rounded]="true"
9925
10055
  [text]="true"
@@ -9928,6 +10058,7 @@ class DiscussionComponent {
9928
10058
  @if (comment.canDelete) {
9929
10059
  <p-button
9930
10060
  icon="pi pi-trash"
10061
+ qa-id="oip-discussion-comment-delete"
9931
10062
  severity="danger"
9932
10063
  [rounded]="true"
9933
10064
  [text]="true"
@@ -9940,6 +10071,7 @@ class DiscussionComponent {
9940
10071
  <textarea
9941
10072
  class="min-h-24 w-full"
9942
10073
  pTextarea
10074
+ qa-id="oip-discussion-edit-content"
9943
10075
  rows="4"
9944
10076
  [autoResize]="true"
9945
10077
  [placeholder]="'discussionComponent.editCommentPlaceholder' | translate"
@@ -9958,6 +10090,7 @@ class DiscussionComponent {
9958
10090
 
9959
10091
  <p-button
9960
10092
  icon="pi pi-check"
10093
+ qa-id="oip-discussion-edit-save"
9961
10094
  [disabled]="!editContent.trim()"
9962
10095
  [label]="'discussionComponent.save' | translate"
9963
10096
  (onClick)="saveEdit(comment)" />
@@ -9970,17 +10103,23 @@ class DiscussionComponent {
9970
10103
  </div>
9971
10104
  </div>
9972
10105
  } @else {
9973
- <div class="markdown-preview" [innerHTML]="renderMarkdown(comment.content)"></div>
10106
+ <div
10107
+ class="markdown-preview"
10108
+ qa-id="oip-discussion-comment-content"
10109
+ [innerHTML]="renderMarkdown(comment.content)"></div>
9974
10110
  }
9975
10111
 
9976
10112
  @if (comment.attachments.length > 0) {
9977
10113
  <div class="flex flex-col gap-3">
9978
10114
  <div class="flex flex-col gap-3">
9979
10115
  @for (attachment of comment.attachments; track attachment.attachmentId) {
9980
- <div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-end">
10116
+ <div
10117
+ class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-end"
10118
+ qa-id="oip-discussion-attachment">
9981
10119
  <div class="inline-flex items-center gap-2">
9982
10120
  <i class="pi pi-file"></i>
9983
10121
  <button
10122
+ qa-id="oip-discussion-attachment-name"
9984
10123
  class="break-all bg-transparent p-0 text-left font-semibold text-primary-600 transition hover:text-primary-500"
9985
10124
  type="button"
9986
10125
  (click)="downloadAttachment(attachment)">
@@ -9992,6 +10131,7 @@ class DiscussionComponent {
9992
10131
  @if (comment.canEdit) {
9993
10132
  <p-button
9994
10133
  icon="pi pi-times"
10134
+ qa-id="oip-discussion-attachment-delete"
9995
10135
  severity="danger"
9996
10136
  [rounded]="true"
9997
10137
  [text]="true"
@@ -10006,6 +10146,7 @@ class DiscussionComponent {
10006
10146
  <div class="flex flex-wrap items-center gap-2">
10007
10147
  @for (reaction of getReactions(comment); track reaction.emojiCode) {
10008
10148
  <p-button
10149
+ qa-id="oip-discussion-reaction"
10009
10150
  severity="secondary"
10010
10151
  [label]="reaction.emojiCode + ' ' + reaction.count"
10011
10152
  [outlined]="false"
@@ -10015,6 +10156,7 @@ class DiscussionComponent {
10015
10156
  @if (!hasUserReaction(comment)) {
10016
10157
  <p-button
10017
10158
  icon="pi pi-face-smile"
10159
+ qa-id="oip-discussion-add-reaction"
10018
10160
  severity="secondary"
10019
10161
  [rounded]="true"
10020
10162
  [text]="true"
@@ -10024,6 +10166,7 @@ class DiscussionComponent {
10024
10166
  @for (emoji of emojiPalette; track emoji) {
10025
10167
  <button
10026
10168
  class="emoji-option"
10169
+ qa-id="oip-discussion-emoji"
10027
10170
  type="button"
10028
10171
  (click)="reactWithEmoji(comment, emoji, reactionPopover)">
10029
10172
  {{ emoji }}
@@ -10054,7 +10197,9 @@ class DiscussionComponent {
10054
10197
  item of historyByComment[comment.commentId]?.items ?? [];
10055
10198
  track item.commentEditHistoryId
10056
10199
  ) {
10057
- <div class="rounded-2xl border border-surface-200 px-4 py-4 dark:border-surface-700">
10200
+ <div
10201
+ class="rounded-2xl border border-surface-200 px-4 py-4 dark:border-surface-700"
10202
+ qa-id="oip-discussion-history-item">
10058
10203
  <div class="mb-3 inline-flex items-center gap-2 text-sm font-semibold">
10059
10204
  <i class="pi pi-clock"></i>
10060
10205
  <span>{{ item.editedByDisplayName }} · {{ item.editedAt | date: 'short' }}</span>
@@ -10064,12 +10209,14 @@ class DiscussionComponent {
10064
10209
  <p-tag severity="secondary" [value]="'discussionComponent.before' | translate" />
10065
10210
  <div
10066
10211
  class="history-markdown rounded-2xl bg-surface-50 px-4 py-3 leading-7 dark:bg-surface-900/40"
10212
+ qa-id="oip-discussion-history-old"
10067
10213
  [innerHTML]="renderMarkdown(item.oldContent)"></div>
10068
10214
  </div>
10069
10215
  <div class="flex flex-col gap-2">
10070
10216
  <p-tag severity="success" [value]="'discussionComponent.after' | translate" />
10071
10217
  <div
10072
10218
  class="history-markdown rounded-2xl bg-surface-50 px-4 py-3 leading-7 dark:bg-surface-900/40"
10219
+ qa-id="oip-discussion-history-new"
10073
10220
  [innerHTML]="renderMarkdown(item.newContent)"></div>
10074
10221
  </div>
10075
10222
  </div>
@@ -10116,6 +10263,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
10116
10263
  <textarea
10117
10264
  class="min-h-28 w-full"
10118
10265
  pTextarea
10266
+ qa-id="oip-discussion-new-comment"
10119
10267
  rows="5"
10120
10268
  [autoResize]="true"
10121
10269
  [placeholder]="'discussionComponent.writeCommentPlaceholder' | translate"
@@ -10139,6 +10287,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
10139
10287
  <div class="flex flex-col gap-3">
10140
10288
  @for (candidate of mentionSuggestions; track candidate.userId) {
10141
10289
  <button
10290
+ qa-id="oip-discussion-mention-suggestion"
10142
10291
  class="flex w-full items-center justify-between gap-4 rounded-xl border border-transparent px-3 py-3 text-left transition hover:border-primary-200 hover:bg-primary-50 dark:hover:border-primary-800 dark:hover:bg-primary-950/30"
10143
10292
  type="button"
10144
10293
  (click)="insertMention(candidate)">
@@ -10157,7 +10306,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
10157
10306
  <div class="flex flex-col gap-3">
10158
10307
  @for (file of pendingFiles; track file.name + file.size) {
10159
10308
  <div
10160
- class="flex flex-col gap-3 rounded-2xl border border-surface-200 px-4 py-3 sm:flex-row sm:items-center sm:justify-between dark:border-surface-700">
10309
+ class="flex flex-col gap-3 rounded-2xl border border-surface-200 px-4 py-3 sm:flex-row sm:items-center sm:justify-between dark:border-surface-700"
10310
+ qa-id="oip-discussion-pending-file">
10161
10311
  <div class="inline-flex items-center gap-2">
10162
10312
  <i class="pi pi-file"></i>
10163
10313
  <span class="break-all">{{ file.name }}</span>
@@ -10190,10 +10340,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
10190
10340
  severity="secondary"
10191
10341
  [label]="'discussionComponent.attachFiles' | translate"
10192
10342
  [text]="true" />
10193
- <input multiple type="file" (change)="onFilesSelected($event)" />
10343
+ <input multiple qa-id="oip-discussion-attach-input" type="file" (change)="onFilesSelected($event)" />
10194
10344
  </label>
10195
10345
  <p-button
10196
10346
  icon="pi pi-send"
10347
+ qa-id="oip-discussion-send"
10197
10348
  [disabled]="submitting || !newComment.trim()"
10198
10349
  [label]="'discussionComponent.send' | translate"
10199
10350
  [loading]="submitting"
@@ -10217,7 +10368,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
10217
10368
  } @else {
10218
10369
  <div class="flex flex-col gap-4">
10219
10370
  @for (comment of comments; track comment.commentId) {
10220
- <p-card class="block">
10371
+ <p-card class="block" qa-id="oip-discussion-comment">
10221
10372
  <ng-template pTemplate="content">
10222
10373
  <div class="grid gap-4 md:grid-cols-[3.2rem_minmax(0,1fr)]">
10223
10374
  <div>
@@ -10238,6 +10389,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
10238
10389
  @if (comment.isEdited) {
10239
10390
  <p-tag
10240
10391
  icon="pi pi-pencil"
10392
+ qa-id="oip-discussion-comment-edited"
10241
10393
  severity="warn"
10242
10394
  [value]="'discussionComponent.edited' | translate" />
10243
10395
  }
@@ -10245,6 +10397,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
10245
10397
  @if (comment.canEdit) {
10246
10398
  <p-button
10247
10399
  icon="pi pi-pencil"
10400
+ qa-id="oip-discussion-comment-edit"
10248
10401
  severity="secondary"
10249
10402
  [rounded]="true"
10250
10403
  [text]="true"
@@ -10253,6 +10406,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
10253
10406
  @if (comment.historyCount > 0) {
10254
10407
  <p-button
10255
10408
  icon="pi pi-history"
10409
+ qa-id="oip-discussion-comment-history"
10256
10410
  severity="secondary"
10257
10411
  [rounded]="true"
10258
10412
  [text]="true"
@@ -10261,6 +10415,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
10261
10415
  @if (comment.canDelete) {
10262
10416
  <p-button
10263
10417
  icon="pi pi-trash"
10418
+ qa-id="oip-discussion-comment-delete"
10264
10419
  severity="danger"
10265
10420
  [rounded]="true"
10266
10421
  [text]="true"
@@ -10273,6 +10428,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
10273
10428
  <textarea
10274
10429
  class="min-h-24 w-full"
10275
10430
  pTextarea
10431
+ qa-id="oip-discussion-edit-content"
10276
10432
  rows="4"
10277
10433
  [autoResize]="true"
10278
10434
  [placeholder]="'discussionComponent.editCommentPlaceholder' | translate"
@@ -10291,6 +10447,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
10291
10447
 
10292
10448
  <p-button
10293
10449
  icon="pi pi-check"
10450
+ qa-id="oip-discussion-edit-save"
10294
10451
  [disabled]="!editContent.trim()"
10295
10452
  [label]="'discussionComponent.save' | translate"
10296
10453
  (onClick)="saveEdit(comment)" />
@@ -10303,17 +10460,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
10303
10460
  </div>
10304
10461
  </div>
10305
10462
  } @else {
10306
- <div class="markdown-preview" [innerHTML]="renderMarkdown(comment.content)"></div>
10463
+ <div
10464
+ class="markdown-preview"
10465
+ qa-id="oip-discussion-comment-content"
10466
+ [innerHTML]="renderMarkdown(comment.content)"></div>
10307
10467
  }
10308
10468
 
10309
10469
  @if (comment.attachments.length > 0) {
10310
10470
  <div class="flex flex-col gap-3">
10311
10471
  <div class="flex flex-col gap-3">
10312
10472
  @for (attachment of comment.attachments; track attachment.attachmentId) {
10313
- <div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-end">
10473
+ <div
10474
+ class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-end"
10475
+ qa-id="oip-discussion-attachment">
10314
10476
  <div class="inline-flex items-center gap-2">
10315
10477
  <i class="pi pi-file"></i>
10316
10478
  <button
10479
+ qa-id="oip-discussion-attachment-name"
10317
10480
  class="break-all bg-transparent p-0 text-left font-semibold text-primary-600 transition hover:text-primary-500"
10318
10481
  type="button"
10319
10482
  (click)="downloadAttachment(attachment)">
@@ -10325,6 +10488,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
10325
10488
  @if (comment.canEdit) {
10326
10489
  <p-button
10327
10490
  icon="pi pi-times"
10491
+ qa-id="oip-discussion-attachment-delete"
10328
10492
  severity="danger"
10329
10493
  [rounded]="true"
10330
10494
  [text]="true"
@@ -10339,6 +10503,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
10339
10503
  <div class="flex flex-wrap items-center gap-2">
10340
10504
  @for (reaction of getReactions(comment); track reaction.emojiCode) {
10341
10505
  <p-button
10506
+ qa-id="oip-discussion-reaction"
10342
10507
  severity="secondary"
10343
10508
  [label]="reaction.emojiCode + ' ' + reaction.count"
10344
10509
  [outlined]="false"
@@ -10348,6 +10513,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
10348
10513
  @if (!hasUserReaction(comment)) {
10349
10514
  <p-button
10350
10515
  icon="pi pi-face-smile"
10516
+ qa-id="oip-discussion-add-reaction"
10351
10517
  severity="secondary"
10352
10518
  [rounded]="true"
10353
10519
  [text]="true"
@@ -10357,6 +10523,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
10357
10523
  @for (emoji of emojiPalette; track emoji) {
10358
10524
  <button
10359
10525
  class="emoji-option"
10526
+ qa-id="oip-discussion-emoji"
10360
10527
  type="button"
10361
10528
  (click)="reactWithEmoji(comment, emoji, reactionPopover)">
10362
10529
  {{ emoji }}
@@ -10387,7 +10554,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
10387
10554
  item of historyByComment[comment.commentId]?.items ?? [];
10388
10555
  track item.commentEditHistoryId
10389
10556
  ) {
10390
- <div class="rounded-2xl border border-surface-200 px-4 py-4 dark:border-surface-700">
10557
+ <div
10558
+ class="rounded-2xl border border-surface-200 px-4 py-4 dark:border-surface-700"
10559
+ qa-id="oip-discussion-history-item">
10391
10560
  <div class="mb-3 inline-flex items-center gap-2 text-sm font-semibold">
10392
10561
  <i class="pi pi-clock"></i>
10393
10562
  <span>{{ item.editedByDisplayName }} · {{ item.editedAt | date: 'short' }}</span>
@@ -10397,12 +10566,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
10397
10566
  <p-tag severity="secondary" [value]="'discussionComponent.before' | translate" />
10398
10567
  <div
10399
10568
  class="history-markdown rounded-2xl bg-surface-50 px-4 py-3 leading-7 dark:bg-surface-900/40"
10569
+ qa-id="oip-discussion-history-old"
10400
10570
  [innerHTML]="renderMarkdown(item.oldContent)"></div>
10401
10571
  </div>
10402
10572
  <div class="flex flex-col gap-2">
10403
10573
  <p-tag severity="success" [value]="'discussionComponent.after' | translate" />
10404
10574
  <div
10405
10575
  class="history-markdown rounded-2xl bg-surface-50 px-4 py-3 leading-7 dark:bg-surface-900/40"
10576
+ qa-id="oip-discussion-history-new"
10406
10577
  [innerHTML]="renderMarkdown(item.newContent)"></div>
10407
10578
  </div>
10408
10579
  </div>
@@ -11640,14 +11811,6 @@ function oipErrorRoute(path = 'error') {
11640
11811
  loadComponent: () => Promise.resolve().then(function () { return error_component; }).then((m) => m.ErrorComponent)
11641
11812
  };
11642
11813
  }
11643
- /** Current user profile. */
11644
- function oipProfileRoute(path = 'profile') {
11645
- return {
11646
- path,
11647
- loadComponent: () => Promise.resolve().then(function () { return userProfile_component; }).then((m) => m.UserProfileComponent),
11648
- canActivate: [oipAuthGuard]
11649
- };
11650
- }
11651
11814
  /** Application configuration. */
11652
11815
  function oipConfigRoute(path = 'config') {
11653
11816
  return {
@@ -11746,7 +11909,6 @@ function provideOipRoutes(options = {}) {
11746
11909
  const children = [...(options.children ?? [])];
11747
11910
  builtInRoute(features.access, oipAccessRoute, children);
11748
11911
  builtInRoute(features.error, oipErrorRoute, children);
11749
- builtInRoute(features.profile, oipProfileRoute, children);
11750
11912
  builtInRoute(features.config, oipConfigRoute, children);
11751
11913
  builtInRoute(features.applications, oipApplicationsRoute, children);
11752
11914
  builtInRoute(features.modules, oipModulesRoute, children);
@@ -11790,5 +11952,5 @@ function provideOipRoutes(options = {}) {
11790
11952
  * Generated bundle index. Do not edit.
11791
11953
  */
11792
11954
 
11793
- export { APP_INFO_TOKEN, APP_THEME_PRESETS, APP_THEME_PRESETS_MERGE_MODE, AccessComponent, AppConfiguratorComponent, AppFloatingConfiguratorComponent, AppInfoService, AppLayoutComponent, AppModulesComponent, AppTopbar, AppTopbarApplicationSwitcherComponent, ApplicationRegistryService, ApplicationsApi, ApplicationsComponent, AuthGuardService, AuthSessionsApi, AuthSessionsComponent, BaseModuleComponent, BffSecurityService, BlockLoaderComponent, ConfigComponent, ContentType, CustomElementExtensionModuleHostComponent, DbMigrationComponent, DefaultSecretsBannerComponent, DiscussionComponent, ErrorComponent, ExtensionLoaderService, ExtensionModuleHostComponent, FolderModuleApi, FooterComponent, HttpClient, IframeModuleApi, IframeModuleComponent, L10nService, LOGO_COMPONENT_TOKEN, LayoutService, LogoComponent, LogoService, MenuComponent, MenuService, ModuleInstanceRightsService, ModuleLoadingService, MsgService, NoModulesComponent, NotfoundComponent, NotificationApi, NotificationService, OIP_EXTENSION_EVENTS, SecurityApi, SecurityComponent, SecurityService, SidebarComponent, StartPageService, TableFilterService, TopBarService, UnauthorizedComponent, UserNotificationsComponent, UserProfileApi, UserProfileComponent, UserService, convertToPrimeNgDateFormat, defaultTheme, emitOipContextChange, emitOipError, emitOipNavigate, emitOipNotify, emitOipSettingsChange, emitOipTitleChange, langIntercept, mergeWithDefaults, moduleAccessGuard, oipAccessRoute, oipApplicationsRoute, oipAuthGuard, oipConfigRoute, oipDbMigrationRoute, oipDiscussionRoute, oipErrorRoute, oipExtensionsRoute, oipIframeModuleRoute, oipModulesRoute, oipNoModulesRoute, oipProfileRoute, oipSessionsRoute, oipStartRedirectGuard, oipStartRoute, provideAppInfo, provideAppThemes, provideLogoComponent, provideOip, provideOipRoutes, provideTranslations, replaceDefaults };
11955
+ export { APP_INFO_TOKEN, APP_THEME_PRESETS, APP_THEME_PRESETS_MERGE_MODE, AccessComponent, AppConfiguratorComponent, AppFloatingConfiguratorComponent, AppInfoService, AppLayoutComponent, AppModulesComponent, AppTopbar, AppTopbarApplicationSwitcherComponent, ApplicationRegistryService, ApplicationsApi, ApplicationsComponent, AuthGuardService, AuthSessionsApi, AuthSessionsComponent, BaseModuleComponent, BffSecurityService, BlockLoaderComponent, ConfigComponent, ContentType, CustomElementExtensionModuleHostComponent, DbMigrationComponent, DefaultSecretsBannerComponent, DiscussionComponent, ErrorComponent, ExtensionLoaderService, ExtensionModuleHostComponent, FolderModuleApi, FooterComponent, HttpClient, IframeModuleApi, IframeModuleComponent, L10nService, LOGO_COMPONENT_TOKEN, LayoutService, LogoComponent, LogoService, MenuComponent, MenuService, ModuleInstanceRightsService, ModuleLoadingService, MsgService, NoModulesComponent, NotfoundComponent, NotificationApi, NotificationService, OIP_EXTENSION_EVENTS, SecurityApi, SecurityComponent, SecurityService, SidebarComponent, StartPageService, TableFilterService, TopBarService, UnauthorizedComponent, UserNotificationsComponent, UserProfileApi, UserProfileComponent, UserService, convertToPrimeNgDateFormat, defaultTheme, emitOipContextChange, emitOipError, emitOipNavigate, emitOipNotify, emitOipSettingsChange, emitOipTitleChange, langIntercept, mergeWithDefaults, moduleAccessGuard, oipAccessRoute, oipApplicationsRoute, oipAuthGuard, oipConfigRoute, oipDbMigrationRoute, oipDiscussionRoute, oipErrorRoute, oipExtensionsRoute, oipIframeModuleRoute, oipModulesRoute, oipNoModulesRoute, oipSessionsRoute, oipStartRedirectGuard, oipStartRoute, provideAppInfo, provideAppThemes, provideLogoComponent, provideOip, provideOipRoutes, provideTranslations, replaceDefaults };
11794
11956
  //# sourceMappingURL=oip-common.mjs.map