@flusys/ng-localization 4.1.1 → 5.0.0

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.
@@ -5,31 +5,30 @@ import { PrimeNG } from 'primeng/config';
5
5
  import { HttpClient } from '@angular/common/http';
6
6
  import { APP_CONFIG, TRANSLATE_ADAPTER } from '@flusys/ng-core';
7
7
  import { firstValueFrom, from, fromEvent, finalize, forkJoin } from 'rxjs';
8
+ import { ApiResourceService, resolveTranslationModule, SHARED_MESSAGES, PermissionValidatorService, LANGUAGE_PERMISSIONS, TRANSLATION_KEY_PERMISSIONS, TranslatePipe, AngularModule, PrimeModule, HasPermissionDirective } from '@flusys/ng-shared';
8
9
  import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
9
10
  import * as i1 from 'primeng/styleclass';
10
11
  import { StyleClassModule } from 'primeng/styleclass';
11
12
  import { filter } from 'rxjs/operators';
12
13
  import { LAYOUT_LANGUAGE_SELECTOR, LAYOUT_LANGUAGE_SELECTOR_PANEL } from '@flusys/ng-layout';
13
- import { resolveTranslationModule, SHARED_MESSAGES, PermissionValidatorService, LANGUAGE_PERMISSIONS, TRANSLATION_KEY_PERMISSIONS, TRANSLATION_PERMISSIONS, TranslatePipe, AngularModule, PrimeModule, HasPermissionDirective } from '@flusys/ng-shared';
14
14
  import { RouterOutlet, RouterLink, RouterLinkActive, Router, ActivatedRoute } from '@angular/router';
15
15
  import { ConfirmationService, MessageService } from 'primeng/api';
16
16
  import * as i2 from 'primeng/button';
17
17
  import * as i2$1 from 'primeng/confirmdialog';
18
- import * as i8 from 'primeng/table';
18
+ import * as i3 from 'primeng/table';
19
19
  import * as i4 from 'primeng/tag';
20
- import * as i10 from 'primeng/toast';
20
+ import * as i5 from 'primeng/toast';
21
21
  import * as i6 from 'primeng/tooltip';
22
22
  import * as i1$1 from '@angular/forms';
23
23
  import { FormsModule } from '@angular/forms';
24
24
  import { form, required, maxLength, FormField } from '@angular/forms/signals';
25
- import * as i3 from 'primeng/card';
25
+ import * as i3$1 from 'primeng/card';
26
26
  import * as i4$1 from 'primeng/inputtext';
27
- import * as i5 from 'primeng/select';
27
+ import * as i5$1 from 'primeng/select';
28
28
  import * as i4$2 from 'primeng/iconfield';
29
- import * as i5$1 from 'primeng/inputicon';
29
+ import * as i5$2 from 'primeng/inputicon';
30
30
  import * as i7 from 'primeng/textarea';
31
31
  import * as i9 from 'primeng/progressspinner';
32
- import * as i3$1 from 'primeng/checkbox';
33
32
 
34
33
  const LOCALIZATION_CONFIG = new InjectionToken('LOCALIZATION_CONFIG');
35
34
  /** Get localization config with sensible defaults applied */
@@ -52,106 +51,17 @@ const STORAGE_KEYS = {
52
51
  };
53
52
 
54
53
  const LOCALIZATION_MESSAGES = {
55
- // Container
56
- 'localization.title': 'Localization',
57
- 'localization.subtitle': 'Manage languages and translations',
58
- // Tabs
59
- 'localization.tabs.languages': 'Languages',
60
- 'localization.tabs.keys': 'Keys',
61
- 'localization.tabs.translations': 'Translations',
62
- // Languages
63
- 'localization.language.title': 'Languages',
64
- 'localization.language.subtitle': 'Manage available languages for your application',
65
- 'localization.language.create': 'Add Language',
66
- 'localization.language.edit': 'Edit Language',
67
- 'localization.language.code': 'Language Code',
68
- 'localization.language.name': 'Language Name',
69
- 'localization.language.native.name': 'Native Name',
70
- 'localization.language.direction': 'Text Direction',
71
- 'localization.language.ltr': 'LTR',
72
- 'localization.language.rtl': 'RTL',
73
- 'localization.language.active': 'Active',
74
- 'localization.language.default': 'Default',
75
- 'localization.language.set.default': 'Set as Default',
76
- 'localization.language.confirm.set.default': 'Set "{{name}}" as the default language?',
77
- 'localization.language.set.default.success': '{{name}} is now the default language.',
78
- 'localization.language.delete.title': 'Delete Language',
79
- 'localization.language.empty': 'No languages configured. Add a language to get started.',
80
- 'localization.language.display.order': 'Display Order',
81
- 'localization.language.iso.code.hint': 'ISO 639-1 language code (e.g., en, ar, fr)',
82
- 'localization.language.native.name.hint': 'Name in the native language (e.g., العربية for Arabic)',
83
- 'localization.language.create.success': 'Language created successfully',
84
- 'localization.language.update.success': 'Language updated successfully',
85
- // Translation keys
86
- 'localization.key.title': 'Translation Keys',
87
- 'localization.key.subtitle': 'Manage translation keys for your application',
88
- 'localization.key.create': 'Add Key',
89
- 'localization.key.edit': 'Edit Key',
90
- 'localization.key.key': 'Key',
91
- 'localization.key.name': 'Key Name',
92
- 'localization.key.module': 'Module',
93
- 'localization.key.default.message': 'Default Message',
94
- 'localization.key.description': 'Description',
95
- 'localization.key.variables': 'Variables',
96
- 'localization.key.variables.hint': 'Enter variable names used in this translation',
97
- 'localization.key.variables.placeholder': 'e.g., name, count, date',
98
- 'localization.key.translations': 'Translations',
99
- 'localization.key.translations.section': 'Translations',
100
- 'localization.key.about': 'Key Information',
101
- 'localization.key.filter.module': 'Filter by module',
102
- 'localization.key.select.module': 'Select or enter module',
103
- 'localization.key.enter.translation': 'Enter translation',
104
- 'localization.key.no.languages': 'No active languages configured',
105
- 'localization.key.empty': 'No translation keys found',
106
- 'localization.key.delete.title': 'Delete Translation Key',
107
- 'localization.key.readonly': 'System Key',
108
- 'localization.key.readonly.hint': 'System keys cannot be deleted',
109
- // Translations
110
- 'localization.translation.title': 'Translations',
111
- 'localization.translation.manage': 'Manage Translations',
112
- 'localization.translation.value': 'Translation',
113
- 'localization.translation.verified': 'Verified',
114
- 'localization.translation.missing': 'Missing',
115
- 'localization.translation.save.changes': 'Save Changes',
116
- 'localization.translation.unsaved.changes': '{{count}} unsaved changes',
117
- 'localization.translation.no.keys': 'No translation keys found for this module',
118
- 'localization.translation.select.module': 'Select a module to view translations',
119
- 'localization.translation.select.language': 'Select a language to view translations',
120
- 'localization.translation.module': 'Module',
121
- 'localization.translation.language': 'Language',
122
- 'localization.translation.key': 'Key',
123
- 'localization.translation.default.message': 'Default Message',
124
- // Language selector
125
- 'localization.selector.language': 'Language',
126
- 'localization.selector.select.language': 'Select Language',
127
- // Translation list page
128
- 'localization.translation.save.success': 'Success',
129
- 'localization.translation.save.success.detail': 'Translations saved successfully',
130
- 'localization.translation.search.keys': 'Search translation keys...',
131
- 'localization.translation.select.module.hint': 'Please select a module to view its translations',
132
- 'localization.translation.select.language.hint': 'Please select a language to view translations',
133
- 'localization.translation.all.modules': 'All Modules',
134
- 'localization.translation.pagination': 'Showing {first} to {last} of {totalRecords} keys',
135
- // Language direction labels
136
- 'localization.language.direction.ltr': 'Left to Right (LTR)',
137
- 'localization.language.direction.rtl': 'Right to Left (RTL)',
138
- // Placeholders
139
- 'localization.placeholder.code': 'en',
140
- 'localization.placeholder.name': 'English',
141
- 'localization.placeholder.native.name': 'English',
142
- 'localization.placeholder.key.name': 'button.submit',
143
- 'localization.placeholder.default.message': 'Enter default value (English)',
144
- 'localization.placeholder.key.description': 'Optional description for translators',
145
54
  // Language CRUD (API keys)
146
55
  'language.create.success': 'Language created successfully',
147
56
  'language.create.many.success': '{{count}} languages created successfully',
148
57
  'language.get.success': 'Language retrieved successfully',
58
+ 'language.get.by.ids.success': 'Languages retrieved successfully by Ids',
59
+ 'language.get.by.filter.success': 'Languages retrieved successfully by filter',
149
60
  'language.get.all.success': 'Languages retrieved successfully',
150
61
  'language.update.success': 'Language updated successfully',
151
62
  'language.update.many.success': '{{count}} languages updated successfully',
152
63
  'language.delete.success': 'Language deleted successfully',
153
64
  'language.restore.success': 'Language restored successfully',
154
- 'language.not.found': 'Language not found',
155
65
  'language.active.success': 'Active languages retrieved successfully',
156
66
  'language.set.default.success': 'Default language updated successfully',
157
67
  'language.get.default.success': 'Default language retrieved successfully',
@@ -159,55 +69,90 @@ const LOCALIZATION_MESSAGES = {
159
69
  'translation.key.create.success': 'Translation key created successfully',
160
70
  'translation.key.create.many.success': '{{count}} translation keys created successfully',
161
71
  'translation.key.get.success': 'Translation key retrieved successfully',
72
+ 'translation.key.get.by.ids.success': 'Translation keys retrieved successfully by Ids',
73
+ 'translation.key.get.by.filter.success': 'Translation keys retrieved successfully by filter',
162
74
  'translation.key.get.all.success': 'Translation keys retrieved successfully',
163
75
  'translation.key.update.success': 'Translation key updated successfully',
164
76
  'translation.key.update.many.success': '{{count}} translation keys updated successfully',
165
77
  'translation.key.delete.success': 'Translation key deleted successfully',
166
78
  'translation.key.restore.success': 'Translation key restored successfully',
167
- 'translation.key.not.found': 'Translation key not found',
168
- 'translation.key.import.success': 'Translation keys imported successfully',
169
- 'translation.key.export.success': 'Translation keys exported successfully',
79
+ 'translation.key.modules.success': 'Modules retrieved successfully',
170
80
  'translation.key.readonly.delete.forbidden': 'Cannot delete readonly translation keys: {{keys}}',
171
81
  'translation.key.readonly.update.forbidden': 'Cannot modify key or module of system translation keys',
172
82
  'translation.key.duplicate.key.in.module': 'Translation key "{{key}}" already exists in module "{{module}}"',
173
- 'translation.key.modules.success': 'Modules retrieved successfully',
174
83
  // Translation CRUD (API keys)
175
84
  'translation.create.success': 'Translation created successfully',
176
85
  'translation.create.many.success': '{{count}} translations created successfully',
177
86
  'translation.get.success': 'Translation retrieved successfully',
87
+ 'translation.get.by.ids.success': 'Translations retrieved successfully by Ids',
88
+ 'translation.get.by.filter.success': 'Translations retrieved successfully by filter',
178
89
  'translation.get.all.success': 'Translations retrieved successfully',
179
90
  'translation.update.success': 'Translation updated successfully',
180
91
  'translation.update.many.success': '{{count}} translations updated successfully',
181
92
  'translation.delete.success': 'Translation deleted successfully',
182
93
  'translation.restore.success': 'Translation restored successfully',
183
- 'translation.not.found': 'Translation not found',
184
94
  'translation.by.language.success': 'Translations retrieved successfully',
185
- 'translation.by.module.success': 'Translations by module retrieved successfully',
186
- 'translation.missing.success': 'Missing translations retrieved successfully',
187
- 'translation.sync.success': 'Translations synced successfully',
188
- 'translation.update.values.success': 'Translation values updated successfully',
189
- // Validation messages
95
+ // pages/localization-container/localization-container.page.ts
96
+ 'localization.title': 'Localization',
97
+ 'localization.subtitle': 'Manage languages and translations',
98
+ 'localization.tabs.languages': 'Languages',
99
+ 'localization.tabs.keys': 'Keys',
100
+ // pages/language-list/language-list.page.ts
101
+ 'localization.language.title': 'Languages',
102
+ 'localization.language.subtitle': 'Manage available languages for your application',
103
+ 'localization.language.new': 'Add Language',
104
+ 'localization.language.code': 'Language Code',
105
+ 'localization.language.name': 'Language Name',
106
+ 'localization.language.native.name': 'Native Name',
107
+ 'localization.language.direction': 'Text Direction',
108
+ 'localization.language.rtl': 'RTL',
109
+ 'localization.language.ltr': 'LTR',
110
+ 'localization.language.default': 'Default',
111
+ 'localization.language.set.default': 'Set as Default',
112
+ 'localization.language.empty': 'No languages configured. Add a language to get started.',
113
+ 'localization.language.confirm.set.default': 'Set "{{name}}" as the default language?',
114
+ 'localization.language.delete.title': 'Delete Language',
115
+ // pages/language-form/language-form.page.ts
116
+ 'localization.language.edit': 'Edit Language',
117
+ 'localization.placeholder.code': 'en',
118
+ 'localization.language.iso.code.hint': 'ISO 639-1 language code (e.g., en, ar, fr)',
119
+ 'localization.placeholder.name': 'English',
120
+ 'localization.placeholder.native.name': 'English',
121
+ 'localization.language.native.name.hint': 'Name in the native language (e.g., العربية for Arabic)',
122
+ 'localization.language.display.order': 'Display Order',
123
+ 'localization.language.direction.ltr': 'Left to Right (LTR)',
124
+ 'localization.language.direction.rtl': 'Right to Left (RTL)',
190
125
  'localization.validation.code.required': 'Language code is required',
191
126
  'localization.validation.code.max.length': 'Language code must be at most 10 characters',
192
127
  'localization.validation.name.required': 'Language name is required',
193
128
  'localization.validation.name.max.length': 'Language name must be at most 100 characters',
129
+ // pages/translation-key-list/translation-key-list.page.ts
130
+ 'localization.key.title': 'Translation Keys',
131
+ 'localization.key.subtitle': 'Manage translation keys for your application',
132
+ 'localization.key.new': 'Add Key',
133
+ 'localization.key.filter.module': 'Filter by module',
134
+ 'localization.key.module': 'Module',
135
+ 'localization.key.name': 'Key Name',
136
+ 'localization.key.default.message': 'Default Message',
137
+ 'localization.key.translations': 'Translations',
138
+ 'localization.key.empty': 'No translation keys found',
139
+ 'localization.key.delete.title': 'Delete Translation Key',
140
+ // pages/translation-key-form/translation-key-form.page.ts
141
+ 'localization.key.edit': 'Edit Key',
142
+ 'localization.key.about': 'Key Information',
143
+ 'localization.placeholder.key.name': 'button.submit',
144
+ 'localization.key.select.module': 'Select or enter module',
145
+ 'localization.placeholder.default.message': 'Enter default value (English)',
146
+ 'localization.key.description': 'Description',
147
+ 'localization.placeholder.key.description': 'Optional description for translators',
148
+ 'localization.key.variables': 'Variables',
149
+ 'localization.key.variables.placeholder': 'e.g., name, count, date',
150
+ 'localization.key.variables.hint': 'Enter variable names used in this translation',
151
+ 'localization.key.translations.section': 'Translations',
152
+ 'localization.key.no.languages': 'No active languages configured',
153
+ 'localization.key.enter.translation': 'Enter translation',
194
154
  'localization.validation.key.required': 'Translation key is required',
195
- 'localization.validation.module.required': 'Module is required',
196
155
  'localization.validation.default.message.required': 'Default message is required',
197
- // Additional success messages
198
- 'localization.key.create.success': 'Translation key created successfully',
199
- 'localization.key.update.success': 'Translation key updated successfully',
200
- 'localization.key.delete.success': 'Translation key deleted successfully',
201
- 'localization.language.delete.success': 'Language deleted successfully',
202
- 'localization.language.load.failed': 'Failed to load languages',
203
- 'localization.key.load.failed': 'Failed to load translation keys',
204
- // Translation key operation keys (used by form components)
205
- 'localization.translation.key.create.success': 'Translation key created successfully',
206
- 'localization.translation.key.update.success': 'Translation key updated successfully',
207
- 'localization.translation.key.delete.success': 'Translation key deleted successfully',
208
- // Pagination templates
209
- 'localization.pagination.languages.template': 'Showing {first} to {last} of {totalRecords} languages',
210
- 'localization.pagination.keys.template': 'Showing {first} to {last} of {totalRecords} keys',
211
156
  };
212
157
 
213
158
  class LocalizationStateService {
@@ -255,8 +200,16 @@ class LocalizationStateService {
255
200
  dotToCamel(str) {
256
201
  return str.replace(/\.([a-z])/g, (_, c) => c.toUpperCase());
257
202
  }
203
+ PRIMENG_NUMERIC_KEYS = new Set(['firstDayOfWeek']);
204
+ PRIMENG_ARRAY_DEFAULTS = {
205
+ dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
206
+ dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
207
+ dayNamesMin: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
208
+ monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
209
+ monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
210
+ };
258
211
  syncPrimeNGTranslations(translations, fallbacks) {
259
- const primeNGTranslations = {};
212
+ const primeNGTranslations = { ...this.PRIMENG_ARRAY_DEFAULTS };
260
213
  const ariaTranslations = {};
261
214
  const prefix = 'primeng.';
262
215
  const ariaPrefix = 'primeng.aria.';
@@ -268,15 +221,13 @@ class LocalizationStateService {
268
221
  }
269
222
  else if (key.startsWith(prefix)) {
270
223
  const primeKey = this.dotToCamel(key.substring(prefix.length));
271
- primeNGTranslations[primeKey] = value;
224
+ primeNGTranslations[primeKey] = this.PRIMENG_NUMERIC_KEYS.has(primeKey) ? parseInt(value, 10) : value;
272
225
  }
273
226
  }
274
227
  if (Object.keys(ariaTranslations).length > 0) {
275
228
  primeNGTranslations['aria'] = ariaTranslations;
276
229
  }
277
- if (Object.keys(primeNGTranslations).length > 0) {
278
- this.primeNG.setTranslation(primeNGTranslations);
279
- }
230
+ this.primeNG.setTranslation(primeNGTranslations);
280
231
  }
281
232
  setCurrentLanguage(language) {
282
233
  this._currentLanguage.set(language);
@@ -393,7 +344,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
393
344
 
394
345
  class TranslateService {
395
346
  localizationState = inject(LocalizationStateService);
396
- localizationConfig = inject(LOCALIZATION_CONFIG, { optional: true });
347
+ localizationConfig = inject(LOCALIZATION_CONFIG, {
348
+ optional: true,
349
+ });
397
350
  languageCode = this.localizationState.currentLanguageCode;
398
351
  translate(key, params) {
399
352
  if (!this.localizationConfig) {
@@ -412,9 +365,6 @@ class TranslateService {
412
365
  return this.translate(key, params);
413
366
  });
414
367
  }
415
- instant(key, params) {
416
- return this.translate(key, params);
417
- }
418
368
  interpolate(template, params) {
419
369
  if (!params || !template)
420
370
  return template;
@@ -507,30 +457,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
507
457
  args: [{ providedIn: 'root' }]
508
458
  }] });
509
459
 
510
- class LanguageApiService {
511
- http = inject(HttpClient);
512
- appConfig = inject(APP_CONFIG);
513
- get baseUrl() {
514
- return `${this.appConfig.apiBaseUrl}/localization/language`;
515
- }
516
- getAll() {
517
- return this.http.post(`${this.baseUrl}/get-all`, {
518
- pagination: { currentPage: 0, pageSize: 100 },
519
- filter: {},
520
- sort: { serial: 'ASC', name: 'ASC' },
521
- });
522
- }
523
- getById(id) {
524
- return this.http.post(`${this.baseUrl}/get/${id}`, {});
525
- }
526
- create(dto) {
527
- return this.http.post(`${this.baseUrl}/insert`, dto);
528
- }
529
- update(dto) {
530
- return this.http.post(`${this.baseUrl}/update`, dto);
531
- }
532
- delete(id) {
533
- return this.http.post(`${this.baseUrl}/delete`, { id, type: 'delete' });
460
+ class LanguageApiService extends ApiResourceService {
461
+ constructor() {
462
+ super('language', inject(HttpClient), 'localization');
534
463
  }
535
464
  setDefault(id) {
536
465
  return this.http.post(`${this.baseUrl}/set-default`, { id });
@@ -541,57 +470,26 @@ class LanguageApiService {
541
470
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LanguageApiService, decorators: [{
542
471
  type: Injectable,
543
472
  args: [{ providedIn: 'root' }]
544
- }] });
473
+ }], ctorParameters: () => [] });
545
474
 
546
- class TranslationKeyApiService {
547
- http = inject(HttpClient);
548
- appConfig = inject(APP_CONFIG);
549
- get baseUrl() {
550
- return `${this.appConfig.apiBaseUrl}/localization/translation-key`;
551
- }
552
- getAll(filter = {}) {
553
- return this.http.post(`${this.baseUrl}/get-all`, {
554
- pagination: { currentPage: 0, pageSize: 1000 },
555
- filter,
556
- sort: { module: 'ASC', key: 'ASC' },
557
- });
558
- }
559
- getByModule(module) {
560
- return this.http.post(`${this.baseUrl}/get-by-module`, {
561
- module,
562
- });
475
+ class TranslationKeyApiService extends ApiResourceService {
476
+ constructor() {
477
+ super('translation-key', inject(HttpClient), 'localization');
563
478
  }
564
479
  getModules() {
565
480
  return this.http.post(`${this.baseUrl}/get-modules`, {});
566
481
  }
567
- getById(id) {
568
- return this.http.post(`${this.baseUrl}/get/${id}`, {});
569
- }
570
- create(dto) {
571
- return this.http.post(`${this.baseUrl}/insert`, dto);
572
- }
573
- createMany(dtos) {
574
- return this.http.post(`${this.baseUrl}/insert-many`, { items: dtos });
575
- }
576
- update(dto) {
577
- return this.http.post(`${this.baseUrl}/update`, dto);
578
- }
579
- delete(id) {
580
- return this.http.post(`${this.baseUrl}/delete`, { id, type: 'delete' });
581
- }
582
482
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationKeyApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
583
483
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationKeyApiService, providedIn: 'root' });
584
484
  }
585
485
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationKeyApiService, decorators: [{
586
486
  type: Injectable,
587
487
  args: [{ providedIn: 'root' }]
588
- }] });
488
+ }], ctorParameters: () => [] });
589
489
 
590
- class TranslationApiService {
591
- http = inject(HttpClient);
592
- appConfig = inject(APP_CONFIG);
593
- get baseUrl() {
594
- return `${this.appConfig.apiBaseUrl}/localization/translation`;
490
+ class TranslationApiService extends ApiResourceService {
491
+ constructor() {
492
+ super('translation', inject(HttpClient), 'localization');
595
493
  }
596
494
  getByLanguageId(languageId) {
597
495
  return this.http.post(`${this.baseUrl}/get-all`, {
@@ -607,21 +505,6 @@ class TranslationApiService {
607
505
  sort: {},
608
506
  });
609
507
  }
610
- getById(id) {
611
- return this.http.post(`${this.baseUrl}/get/${id}`, {});
612
- }
613
- create(dto) {
614
- return this.http.post(`${this.baseUrl}/insert`, dto);
615
- }
616
- update(dto) {
617
- return this.http.post(`${this.baseUrl}/update`, dto);
618
- }
619
- bulkUpdate(translations) {
620
- return this.http.post(`${this.baseUrl}/update-many-values`, { translations });
621
- }
622
- delete(id) {
623
- return this.http.post(`${this.baseUrl}/delete`, { id, type: 'delete' });
624
- }
625
508
  getByKeyId(translationKeyId) {
626
509
  return this.http.post(`${this.baseUrl}/get-all`, {
627
510
  pagination: { currentPage: 0, pageSize: 1000 },
@@ -632,13 +515,16 @@ class TranslationApiService {
632
515
  getCounts() {
633
516
  return this.http.post(`${this.baseUrl}/get-counts`, {});
634
517
  }
518
+ bulkUpdate(translations) {
519
+ return this.http.post(`${this.baseUrl}/update-many-values`, { translations });
520
+ }
635
521
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
636
522
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationApiService, providedIn: 'root' });
637
523
  }
638
524
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationApiService, decorators: [{
639
525
  type: Injectable,
640
526
  args: [{ providedIn: 'root' }]
641
- }] });
527
+ }], ctorParameters: () => [] });
642
528
 
643
529
  class LanguageSelectorComponent {
644
530
  localizationState = inject(LocalizationStateService);
@@ -885,10 +771,6 @@ const LOCALIZATION_ROUTES = [
885
771
  path: 'keys/:id',
886
772
  loadComponent: () => Promise.resolve().then(function () { return translationKeyForm_page; }).then((m) => m.TranslationKeyFormPage),
887
773
  },
888
- {
889
- path: 'translations',
890
- loadComponent: () => Promise.resolve().then(function () { return translationList_page; }).then((m) => m.TranslationListPage),
891
- },
892
774
  {
893
775
  path: '',
894
776
  redirectTo: 'languages',
@@ -915,13 +797,6 @@ class LocalizationContainerPage {
915
797
  route: 'keys',
916
798
  permission: TRANSLATION_KEY_PERMISSIONS.READ,
917
799
  },
918
- {
919
- id: 'translations',
920
- labelKey: 'localization.tabs.translations',
921
- icon: 'pi pi-language',
922
- route: 'translations',
923
- permission: TRANSLATION_PERMISSIONS.READ,
924
- },
925
800
  ];
926
801
  visibleTabs = computed(() => {
927
802
  // Explicitly track permissions signal for reactivity
@@ -995,10 +870,12 @@ class LanguageListPage {
995
870
  messageService = inject(MessageService);
996
871
  translateService = inject(TranslateService);
997
872
  destroyRef = inject(DestroyRef);
998
- isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
999
- languages = signal([], ...(ngDevMode ? [{ debugName: "languages" }] : []));
1000
873
  ngOnInit() {
1001
- this.loadLanguages();
874
+ this.languageService.fetchList('', {
875
+ pagination: { currentPage: 0, pageSize: 100 },
876
+ filter: {},
877
+ sort: { serial: 'ASC', name: 'ASC' },
878
+ });
1002
879
  }
1003
880
  onCreate() {
1004
881
  this.router.navigate(['/localization/languages/new']);
@@ -1006,29 +883,14 @@ class LanguageListPage {
1006
883
  onEdit(id) {
1007
884
  this.router.navigate(['/localization/languages', id]);
1008
885
  }
1009
- loadLanguages() {
1010
- this.isLoading.set(true);
1011
- this.languageService.getAll()
1012
- .pipe(takeUntilDestroyed(this.destroyRef))
1013
- .subscribe({
1014
- next: (response) => {
1015
- if (response.success) {
1016
- this.languages.set(response.data ?? []);
1017
- }
1018
- this.isLoading.set(false);
1019
- },
1020
- error: () => {
1021
- this.isLoading.set(false);
1022
- },
1023
- });
1024
- }
1025
886
  onSetDefault(lang) {
1026
887
  this.confirmationService.confirm({
1027
888
  message: this.translateService.translate('localization.language.confirm.set.default', { name: lang.name }),
1028
889
  header: this.translateService.translate('localization.language.set.default'),
1029
890
  icon: 'pi pi-star',
1030
891
  accept: () => {
1031
- this.languageService.setDefault(lang.id)
892
+ this.languageService
893
+ .setDefault(lang.id)
1032
894
  .pipe(takeUntilDestroyed(this.destroyRef))
1033
895
  .subscribe({
1034
896
  next: (response) => {
@@ -1036,9 +898,11 @@ class LanguageListPage {
1036
898
  this.messageService.add({
1037
899
  severity: 'success',
1038
900
  summary: this.translateService.translate('shared.success'),
1039
- detail: this.translateService.translate('localization.language.set.default.success', { name: lang.name }),
901
+ detail: response.messageKey
902
+ ? this.translateService.translate(response.messageKey, response.messageVariables)
903
+ : (response.message ?? ''),
1040
904
  });
1041
- this.loadLanguages();
905
+ this.languageService.reload();
1042
906
  }
1043
907
  },
1044
908
  });
@@ -1052,7 +916,7 @@ class LanguageListPage {
1052
916
  icon: 'pi pi-exclamation-triangle',
1053
917
  acceptButtonStyleClass: 'p-button-danger',
1054
918
  accept: () => {
1055
- this.languageService.delete(lang.id)
919
+ from(this.languageService.delete({ id: lang.id, type: 'delete' }))
1056
920
  .pipe(takeUntilDestroyed(this.destroyRef))
1057
921
  .subscribe({
1058
922
  next: (response) => {
@@ -1060,9 +924,11 @@ class LanguageListPage {
1060
924
  this.messageService.add({
1061
925
  severity: 'success',
1062
926
  summary: this.translateService.translate('shared.success'),
1063
- detail: this.translateService.translate('localization.language.delete.success'),
927
+ detail: response.messageKey
928
+ ? this.translateService.translate(response.messageKey, response.messageVariables)
929
+ : (response.message ?? ''),
1064
930
  });
1065
- this.loadLanguages();
931
+ this.languageService.reload();
1066
932
  }
1067
933
  },
1068
934
  });
@@ -1072,16 +938,20 @@ class LanguageListPage {
1072
938
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LanguageListPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
1073
939
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: LanguageListPage, isStandalone: true, selector: "lib-language-list-page", providers: [ConfirmationService, MessageService], ngImport: i0, template: `
1074
940
  <div class="card">
1075
- <div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4">
941
+ <div
942
+ class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4"
943
+ >
1076
944
  <div>
1077
- <h3 class="text-lg sm:text-xl font-semibold m-0">{{ 'localization.language.title' | translate }}</h3>
945
+ <h3 class="text-lg sm:text-xl font-semibold m-0">
946
+ {{ 'localization.language.title' | translate }}
947
+ </h3>
1078
948
  <p class="text-sm text-muted-color mt-1">
1079
949
  {{ 'localization.language.subtitle' | translate }}
1080
950
  </p>
1081
951
  </div>
1082
952
  <p-button
1083
953
  *hasPermission="LANGUAGE_PERMISSIONS.CREATE"
1084
- [label]="'localization.language.create' | translate"
954
+ [label]="'localization.language.new' | translate"
1085
955
  icon="pi pi-plus"
1086
956
  (onClick)="onCreate()"
1087
957
  styleClass="w-full sm:w-auto"
@@ -1090,8 +960,8 @@ class LanguageListPage {
1090
960
 
1091
961
  <div class="overflow-x-auto -mx-4 sm:mx-0">
1092
962
  <p-table
1093
- [value]="languages()"
1094
- [loading]="isLoading()"
963
+ [value]="languageService.data()"
964
+ [loading]="languageService.isLoading()"
1095
965
  styleClass="p-datatable-sm"
1096
966
  [tableStyle]="{ 'min-width': '40rem' }"
1097
967
  >
@@ -1115,18 +985,29 @@ class LanguageListPage {
1115
985
  <td>{{ lang.nativeName || ('shared.na' | translate) }}</td>
1116
986
  <td>
1117
987
  <p-tag
1118
- [value]="lang.direction === 'rtl' ? ('localization.language.rtl' | translate) : ('localization.language.ltr' | translate)"
988
+ [value]="
989
+ lang.direction === 'rtl'
990
+ ? ('localization.language.rtl' | translate)
991
+ : ('localization.language.ltr' | translate)
992
+ "
1119
993
  [severity]="lang.direction === 'rtl' ? 'warn' : 'info'"
1120
994
  />
1121
995
  </td>
1122
996
  <td>
1123
997
  <div class="flex gap-1 items-center">
1124
998
  <p-tag
1125
- [value]="lang.isActive ? ('shared.active' | translate) : ('shared.inactive' | translate)"
999
+ [value]="
1000
+ lang.isActive
1001
+ ? ('shared.active' | translate)
1002
+ : ('shared.inactive' | translate)
1003
+ "
1126
1004
  [severity]="lang.isActive ? 'success' : 'secondary'"
1127
1005
  />
1128
1006
  @if (lang.isDefault) {
1129
- <p-tag [value]="'localization.language.default' | translate" severity="contrast" />
1007
+ <p-tag
1008
+ [value]="'localization.language.default' | translate"
1009
+ severity="contrast"
1010
+ />
1130
1011
  }
1131
1012
  </div>
1132
1013
  </td>
@@ -1138,7 +1019,9 @@ class LanguageListPage {
1138
1019
  icon="pi pi-star"
1139
1020
  [text]="true"
1140
1021
  size="small"
1141
- [pTooltip]="'localization.language.set.default' | translate"
1022
+ [pTooltip]="
1023
+ 'localization.language.set.default' | translate
1024
+ "
1142
1025
  (onClick)="onSetDefault(lang)"
1143
1026
  />
1144
1027
  }
@@ -1179,7 +1062,7 @@ class LanguageListPage {
1179
1062
 
1180
1063
  <p-confirmDialog />
1181
1064
  <p-toast />
1182
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i2$1.ConfirmDialog, selector: "p-confirmDialog, p-confirmdialog, p-confirm-dialog", inputs: ["header", "icon", "message", "style", "styleClass", "maskStyleClass", "acceptIcon", "acceptLabel", "closeAriaLabel", "acceptAriaLabel", "acceptVisible", "rejectIcon", "rejectLabel", "rejectAriaLabel", "rejectVisible", "acceptButtonStyleClass", "rejectButtonStyleClass", "closeOnEscape", "dismissableMask", "blockScroll", "rtl", "closable", "appendTo", "key", "autoZIndex", "baseZIndex", "transitionOptions", "focusTrap", "defaultFocus", "breakpoints", "modal", "visible", "position", "draggable"], outputs: ["onHide"] }, { kind: "component", type: i8.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "component", type: i4.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "component", type: i10.Toast, selector: "p-toast", inputs: ["key", "autoZIndex", "baseZIndex", "life", "styleClass", "position", "preventOpenDuplicates", "preventDuplicates", "showTransformOptions", "hideTransformOptions", "showTransitionOptions", "hideTransitionOptions", "motionOptions", "breakpoints"], outputs: ["onClose"] }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "directive", type: HasPermissionDirective, selector: "[hasPermission]", inputs: ["hasPermission"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1065
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i2$1.ConfirmDialog, selector: "p-confirmDialog, p-confirmdialog, p-confirm-dialog", inputs: ["header", "icon", "message", "style", "styleClass", "maskStyleClass", "acceptIcon", "acceptLabel", "closeAriaLabel", "acceptAriaLabel", "acceptVisible", "rejectIcon", "rejectLabel", "rejectAriaLabel", "rejectVisible", "acceptButtonStyleClass", "rejectButtonStyleClass", "closeOnEscape", "dismissableMask", "blockScroll", "rtl", "closable", "appendTo", "key", "autoZIndex", "baseZIndex", "transitionOptions", "focusTrap", "defaultFocus", "breakpoints", "modal", "visible", "position", "draggable"], outputs: ["onHide"] }, { kind: "component", type: i3.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "component", type: i4.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "component", type: i5.Toast, selector: "p-toast", inputs: ["key", "autoZIndex", "baseZIndex", "life", "styleClass", "position", "preventOpenDuplicates", "preventDuplicates", "showTransformOptions", "hideTransformOptions", "showTransitionOptions", "hideTransitionOptions", "motionOptions", "breakpoints"], outputs: ["onClose"] }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "directive", type: HasPermissionDirective, selector: "[hasPermission]", inputs: ["hasPermission"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1183
1066
  }
1184
1067
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LanguageListPage, decorators: [{
1185
1068
  type: Component,
@@ -1189,16 +1072,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1189
1072
  providers: [ConfirmationService, MessageService],
1190
1073
  template: `
1191
1074
  <div class="card">
1192
- <div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4">
1075
+ <div
1076
+ class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4"
1077
+ >
1193
1078
  <div>
1194
- <h3 class="text-lg sm:text-xl font-semibold m-0">{{ 'localization.language.title' | translate }}</h3>
1079
+ <h3 class="text-lg sm:text-xl font-semibold m-0">
1080
+ {{ 'localization.language.title' | translate }}
1081
+ </h3>
1195
1082
  <p class="text-sm text-muted-color mt-1">
1196
1083
  {{ 'localization.language.subtitle' | translate }}
1197
1084
  </p>
1198
1085
  </div>
1199
1086
  <p-button
1200
1087
  *hasPermission="LANGUAGE_PERMISSIONS.CREATE"
1201
- [label]="'localization.language.create' | translate"
1088
+ [label]="'localization.language.new' | translate"
1202
1089
  icon="pi pi-plus"
1203
1090
  (onClick)="onCreate()"
1204
1091
  styleClass="w-full sm:w-auto"
@@ -1207,8 +1094,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1207
1094
 
1208
1095
  <div class="overflow-x-auto -mx-4 sm:mx-0">
1209
1096
  <p-table
1210
- [value]="languages()"
1211
- [loading]="isLoading()"
1097
+ [value]="languageService.data()"
1098
+ [loading]="languageService.isLoading()"
1212
1099
  styleClass="p-datatable-sm"
1213
1100
  [tableStyle]="{ 'min-width': '40rem' }"
1214
1101
  >
@@ -1232,18 +1119,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1232
1119
  <td>{{ lang.nativeName || ('shared.na' | translate) }}</td>
1233
1120
  <td>
1234
1121
  <p-tag
1235
- [value]="lang.direction === 'rtl' ? ('localization.language.rtl' | translate) : ('localization.language.ltr' | translate)"
1122
+ [value]="
1123
+ lang.direction === 'rtl'
1124
+ ? ('localization.language.rtl' | translate)
1125
+ : ('localization.language.ltr' | translate)
1126
+ "
1236
1127
  [severity]="lang.direction === 'rtl' ? 'warn' : 'info'"
1237
1128
  />
1238
1129
  </td>
1239
1130
  <td>
1240
1131
  <div class="flex gap-1 items-center">
1241
1132
  <p-tag
1242
- [value]="lang.isActive ? ('shared.active' | translate) : ('shared.inactive' | translate)"
1133
+ [value]="
1134
+ lang.isActive
1135
+ ? ('shared.active' | translate)
1136
+ : ('shared.inactive' | translate)
1137
+ "
1243
1138
  [severity]="lang.isActive ? 'success' : 'secondary'"
1244
1139
  />
1245
1140
  @if (lang.isDefault) {
1246
- <p-tag [value]="'localization.language.default' | translate" severity="contrast" />
1141
+ <p-tag
1142
+ [value]="'localization.language.default' | translate"
1143
+ severity="contrast"
1144
+ />
1247
1145
  }
1248
1146
  </div>
1249
1147
  </td>
@@ -1255,7 +1153,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1255
1153
  icon="pi pi-star"
1256
1154
  [text]="true"
1257
1155
  size="small"
1258
- [pTooltip]="'localization.language.set.default' | translate"
1156
+ [pTooltip]="
1157
+ 'localization.language.set.default' | translate
1158
+ "
1259
1159
  (onClick)="onSetDefault(lang)"
1260
1160
  />
1261
1161
  }
@@ -1310,19 +1210,27 @@ class LanguageFormPage {
1310
1210
  route = inject(ActivatedRoute);
1311
1211
  languageService = inject(LanguageApiService);
1312
1212
  messageService = inject(MessageService);
1313
- translateAdapter = inject(TRANSLATE_ADAPTER, { optional: true });
1213
+ translateAdapter = inject(TRANSLATE_ADAPTER, {
1214
+ optional: true,
1215
+ });
1314
1216
  destroyRef = inject(DestroyRef);
1315
1217
  routeParams = toSignal(this.route.paramMap);
1316
1218
  loadTriggered = false;
1317
1219
  isSaving = signal(false, ...(ngDevMode ? [{ debugName: "isSaving" }] : []));
1318
1220
  existingLanguage = signal(null, ...(ngDevMode ? [{ debugName: "existingLanguage" }] : []));
1319
1221
  isEditMode = computed(() => !!this.existingLanguage(), ...(ngDevMode ? [{ debugName: "isEditMode" }] : []));
1320
- translate(key) {
1321
- return this.translateAdapter?.translate(key) ?? key;
1222
+ translate(key, vars) {
1223
+ return this.translateAdapter?.translate(key, vars) ?? key;
1322
1224
  }
1323
1225
  directionOptions = computed(() => [
1324
- { label: this.translate('localization.language.direction.ltr'), value: 'ltr' },
1325
- { label: this.translate('localization.language.direction.rtl'), value: 'rtl' },
1226
+ {
1227
+ label: this.translate('localization.language.direction.ltr'),
1228
+ value: 'ltr',
1229
+ },
1230
+ {
1231
+ label: this.translate('localization.language.direction.rtl'),
1232
+ value: 'rtl',
1233
+ },
1326
1234
  ], ...(ngDevMode ? [{ debugName: "directionOptions" }] : []));
1327
1235
  formModel = signal({
1328
1236
  code: '',
@@ -1334,10 +1242,18 @@ class LanguageFormPage {
1334
1242
  isDefault: false,
1335
1243
  }, ...(ngDevMode ? [{ debugName: "formModel" }] : []));
1336
1244
  formTree = form(this.formModel, (f) => {
1337
- required(f.code, { message: this.translate('localization.validation.code.required') });
1338
- maxLength(f.code, 10, { message: this.translate('localization.validation.code.max.length') });
1339
- required(f.name, { message: this.translate('localization.validation.name.required') });
1340
- maxLength(f.name, 100, { message: this.translate('localization.validation.name.max.length') });
1245
+ required(f.code, {
1246
+ message: this.translate('localization.validation.code.required'),
1247
+ });
1248
+ maxLength(f.code, 10, {
1249
+ message: this.translate('localization.validation.code.max.length'),
1250
+ });
1251
+ required(f.name, {
1252
+ message: this.translate('localization.validation.name.required'),
1253
+ });
1254
+ maxLength(f.name, 100, {
1255
+ message: this.translate('localization.validation.name.max.length'),
1256
+ });
1341
1257
  });
1342
1258
  isFormValid = computed(() => {
1343
1259
  const model = this.formModel();
@@ -1360,7 +1276,7 @@ class LanguageFormPage {
1360
1276
  }
1361
1277
  loadLanguage(id) {
1362
1278
  this.isSaving.set(true);
1363
- this.languageService.getById(id)
1279
+ from(this.languageService.findById(id))
1364
1280
  .pipe(takeUntilDestroyed(this.destroyRef), finalize(() => this.isSaving.set(false)))
1365
1281
  .subscribe({
1366
1282
  next: (response) => {
@@ -1398,8 +1314,11 @@ class LanguageFormPage {
1398
1314
  isDefault: model.isDefault,
1399
1315
  };
1400
1316
  const operation$ = this.isEditMode()
1401
- ? this.languageService.update({ id: this.existingLanguage().id, ...dto })
1402
- : this.languageService.create(dto);
1317
+ ? from(this.languageService.update({
1318
+ id: this.existingLanguage().id,
1319
+ ...dto,
1320
+ }))
1321
+ : from(this.languageService.insert(dto));
1403
1322
  operation$
1404
1323
  .pipe(takeUntilDestroyed(this.destroyRef), finalize(() => this.isSaving.set(false)))
1405
1324
  .subscribe({
@@ -1408,7 +1327,9 @@ class LanguageFormPage {
1408
1327
  this.messageService.add({
1409
1328
  severity: 'success',
1410
1329
  summary: this.translate('shared.success'),
1411
- detail: this.translate(this.isEditMode() ? 'localization.language.update.success' : 'localization.language.create.success'),
1330
+ detail: response.messageKey
1331
+ ? this.translate(response.messageKey, response.messageVariables)
1332
+ : (response.message ?? ''),
1412
1333
  });
1413
1334
  this.router.navigate(['/localization/languages']);
1414
1335
  }
@@ -1424,67 +1345,93 @@ class LanguageFormPage {
1424
1345
  <ng-template #header>
1425
1346
  <div class="flex justify-between items-center p-4">
1426
1347
  <h2 class="text-xl font-semibold m-0">
1427
- {{ isEditMode() ? ('localization.language.edit' | translate) : ('localization.language.create' | translate) }}
1348
+ {{
1349
+ isEditMode()
1350
+ ? ('localization.language.edit' | translate)
1351
+ : ('localization.language.new' | translate)
1352
+ }}
1428
1353
  </h2>
1429
1354
  </div>
1430
1355
  </ng-template>
1431
1356
 
1432
- <form (ngSubmit)="onSubmit()" class="grid grid-cols-1 md:grid-cols-2 gap-4">
1357
+ <form
1358
+ (ngSubmit)="onSubmit()"
1359
+ class="grid grid-cols-1 md:grid-cols-2 gap-4"
1360
+ >
1433
1361
  <!-- Language Code -->
1434
1362
  <div class="flex flex-col gap-2">
1435
- <label for="code" class="font-medium">{{ 'localization.language.code' | translate }} *</label>
1363
+ <label for="code" class="font-medium"
1364
+ >{{ 'localization.language.code' | translate }} *</label
1365
+ >
1436
1366
  <input
1437
1367
  pInputText
1438
1368
  id="code"
1439
1369
  [formField]="formTree.code"
1440
- [placeholder]="'localization.placeholder.code' | translate" />
1441
- <small class="text-muted-color">{{ 'localization.language.iso.code.hint' | translate }}</small>
1370
+ [placeholder]="'localization.placeholder.code' | translate"
1371
+ />
1372
+ <small class="text-muted-color">{{
1373
+ 'localization.language.iso.code.hint' | translate
1374
+ }}</small>
1442
1375
  </div>
1443
1376
 
1444
1377
  <!-- Language Name -->
1445
1378
  <div class="flex flex-col gap-2">
1446
- <label for="name" class="font-medium">{{ 'localization.language.name' | translate }} *</label>
1379
+ <label for="name" class="font-medium"
1380
+ >{{ 'localization.language.name' | translate }} *</label
1381
+ >
1447
1382
  <input
1448
1383
  pInputText
1449
1384
  id="name"
1450
1385
  [formField]="formTree.name"
1451
- [placeholder]="'localization.placeholder.name' | translate" />
1386
+ [placeholder]="'localization.placeholder.name' | translate"
1387
+ />
1452
1388
  </div>
1453
1389
 
1454
1390
  <!-- Native Name -->
1455
1391
  <div class="flex flex-col gap-2">
1456
- <label for="nativeName" class="font-medium">{{ 'localization.language.native.name' | translate }}</label>
1392
+ <label for="nativeName" class="font-medium">{{
1393
+ 'localization.language.native.name' | translate
1394
+ }}</label>
1457
1395
  <input
1458
1396
  pInputText
1459
1397
  id="nativeName"
1460
1398
  [formField]="formTree.nativeName"
1461
- [placeholder]="'localization.placeholder.native.name' | translate" />
1462
- <small class="text-muted-color">{{ 'localization.language.native.name.hint' | translate }}</small>
1399
+ [placeholder]="'localization.placeholder.native.name' | translate"
1400
+ />
1401
+ <small class="text-muted-color">{{
1402
+ 'localization.language.native.name.hint' | translate
1403
+ }}</small>
1463
1404
  </div>
1464
1405
 
1465
1406
  <!-- Text Direction -->
1466
1407
  <div class="flex flex-col gap-2">
1467
- <label for="direction" class="font-medium">{{ 'localization.language.direction' | translate }} *</label>
1408
+ <label for="direction" class="font-medium"
1409
+ >{{ 'localization.language.direction' | translate }} *</label
1410
+ >
1468
1411
  <p-select
1469
1412
  id="direction"
1470
1413
  [ngModel]="formModel().direction"
1471
1414
  (ngModelChange)="updateDirection($event)"
1472
- [ngModelOptions]="{standalone: true}"
1415
+ [ngModelOptions]="{ standalone: true }"
1473
1416
  [options]="directionOptions()"
1474
1417
  optionLabel="label"
1475
1418
  optionValue="value"
1476
- class="w-full" />
1419
+ class="w-full"
1420
+ />
1477
1421
  </div>
1478
1422
 
1479
1423
  <!-- Display Order -->
1480
1424
  <div class="flex flex-col gap-2">
1481
- <label for="serial" class="font-medium">{{ 'localization.language.display.order' | translate }}</label>
1425
+ <label for="serial" class="font-medium">{{
1426
+ 'localization.language.display.order' | translate
1427
+ }}</label>
1482
1428
  <input
1483
1429
  pInputText
1484
1430
  id="serial"
1485
1431
  type="number"
1486
1432
  [formField]="formTree.serial"
1487
- [placeholder]="'shared.display.order.placeholder' | translate" />
1433
+ [placeholder]="'shared.display.order.placeholder' | translate"
1434
+ />
1488
1435
  </div>
1489
1436
 
1490
1437
  <!-- Empty cell for alignment -->
@@ -1508,18 +1455,24 @@ class LanguageFormPage {
1508
1455
  [label]="'shared.cancel' | translate"
1509
1456
  severity="secondary"
1510
1457
  [outlined]="true"
1511
- (onClick)="onCancel()" />
1458
+ (onClick)="onCancel()"
1459
+ />
1512
1460
  <p-button
1513
- [label]="isEditMode() ? ('shared.update' | translate) : ('shared.create' | translate)"
1461
+ [label]="
1462
+ isEditMode()
1463
+ ? ('shared.update' | translate)
1464
+ : ('shared.create' | translate)
1465
+ "
1514
1466
  type="submit"
1515
1467
  [loading]="isSaving()"
1516
- [disabled]="!isFormValid() || isSaving()" />
1468
+ [disabled]="!isFormValid() || isSaving()"
1469
+ />
1517
1470
  </div>
1518
1471
  </form>
1519
1472
  </p-card>
1520
1473
 
1521
1474
  <p-toast />
1522
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "component", type: i5.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i10.Toast, selector: "p-toast", inputs: ["key", "autoZIndex", "baseZIndex", "life", "styleClass", "position", "preventOpenDuplicates", "preventDuplicates", "showTransformOptions", "hideTransformOptions", "showTransitionOptions", "hideTransitionOptions", "motionOptions", "breakpoints"], outputs: ["onClose"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1475
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3$1.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "component", type: i5$1.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i5.Toast, selector: "p-toast", inputs: ["key", "autoZIndex", "baseZIndex", "life", "styleClass", "position", "preventOpenDuplicates", "preventDuplicates", "showTransformOptions", "hideTransformOptions", "showTransitionOptions", "hideTransitionOptions", "motionOptions", "breakpoints"], outputs: ["onClose"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1523
1476
  }
1524
1477
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LanguageFormPage, decorators: [{
1525
1478
  type: Component,
@@ -1532,67 +1485,93 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1532
1485
  <ng-template #header>
1533
1486
  <div class="flex justify-between items-center p-4">
1534
1487
  <h2 class="text-xl font-semibold m-0">
1535
- {{ isEditMode() ? ('localization.language.edit' | translate) : ('localization.language.create' | translate) }}
1488
+ {{
1489
+ isEditMode()
1490
+ ? ('localization.language.edit' | translate)
1491
+ : ('localization.language.new' | translate)
1492
+ }}
1536
1493
  </h2>
1537
1494
  </div>
1538
1495
  </ng-template>
1539
1496
 
1540
- <form (ngSubmit)="onSubmit()" class="grid grid-cols-1 md:grid-cols-2 gap-4">
1497
+ <form
1498
+ (ngSubmit)="onSubmit()"
1499
+ class="grid grid-cols-1 md:grid-cols-2 gap-4"
1500
+ >
1541
1501
  <!-- Language Code -->
1542
1502
  <div class="flex flex-col gap-2">
1543
- <label for="code" class="font-medium">{{ 'localization.language.code' | translate }} *</label>
1503
+ <label for="code" class="font-medium"
1504
+ >{{ 'localization.language.code' | translate }} *</label
1505
+ >
1544
1506
  <input
1545
1507
  pInputText
1546
1508
  id="code"
1547
1509
  [formField]="formTree.code"
1548
- [placeholder]="'localization.placeholder.code' | translate" />
1549
- <small class="text-muted-color">{{ 'localization.language.iso.code.hint' | translate }}</small>
1510
+ [placeholder]="'localization.placeholder.code' | translate"
1511
+ />
1512
+ <small class="text-muted-color">{{
1513
+ 'localization.language.iso.code.hint' | translate
1514
+ }}</small>
1550
1515
  </div>
1551
1516
 
1552
1517
  <!-- Language Name -->
1553
1518
  <div class="flex flex-col gap-2">
1554
- <label for="name" class="font-medium">{{ 'localization.language.name' | translate }} *</label>
1519
+ <label for="name" class="font-medium"
1520
+ >{{ 'localization.language.name' | translate }} *</label
1521
+ >
1555
1522
  <input
1556
1523
  pInputText
1557
1524
  id="name"
1558
1525
  [formField]="formTree.name"
1559
- [placeholder]="'localization.placeholder.name' | translate" />
1526
+ [placeholder]="'localization.placeholder.name' | translate"
1527
+ />
1560
1528
  </div>
1561
1529
 
1562
1530
  <!-- Native Name -->
1563
1531
  <div class="flex flex-col gap-2">
1564
- <label for="nativeName" class="font-medium">{{ 'localization.language.native.name' | translate }}</label>
1532
+ <label for="nativeName" class="font-medium">{{
1533
+ 'localization.language.native.name' | translate
1534
+ }}</label>
1565
1535
  <input
1566
1536
  pInputText
1567
1537
  id="nativeName"
1568
1538
  [formField]="formTree.nativeName"
1569
- [placeholder]="'localization.placeholder.native.name' | translate" />
1570
- <small class="text-muted-color">{{ 'localization.language.native.name.hint' | translate }}</small>
1539
+ [placeholder]="'localization.placeholder.native.name' | translate"
1540
+ />
1541
+ <small class="text-muted-color">{{
1542
+ 'localization.language.native.name.hint' | translate
1543
+ }}</small>
1571
1544
  </div>
1572
1545
 
1573
1546
  <!-- Text Direction -->
1574
1547
  <div class="flex flex-col gap-2">
1575
- <label for="direction" class="font-medium">{{ 'localization.language.direction' | translate }} *</label>
1548
+ <label for="direction" class="font-medium"
1549
+ >{{ 'localization.language.direction' | translate }} *</label
1550
+ >
1576
1551
  <p-select
1577
1552
  id="direction"
1578
1553
  [ngModel]="formModel().direction"
1579
1554
  (ngModelChange)="updateDirection($event)"
1580
- [ngModelOptions]="{standalone: true}"
1555
+ [ngModelOptions]="{ standalone: true }"
1581
1556
  [options]="directionOptions()"
1582
1557
  optionLabel="label"
1583
1558
  optionValue="value"
1584
- class="w-full" />
1559
+ class="w-full"
1560
+ />
1585
1561
  </div>
1586
1562
 
1587
1563
  <!-- Display Order -->
1588
1564
  <div class="flex flex-col gap-2">
1589
- <label for="serial" class="font-medium">{{ 'localization.language.display.order' | translate }}</label>
1565
+ <label for="serial" class="font-medium">{{
1566
+ 'localization.language.display.order' | translate
1567
+ }}</label>
1590
1568
  <input
1591
1569
  pInputText
1592
1570
  id="serial"
1593
1571
  type="number"
1594
1572
  [formField]="formTree.serial"
1595
- [placeholder]="'shared.display.order.placeholder' | translate" />
1573
+ [placeholder]="'shared.display.order.placeholder' | translate"
1574
+ />
1596
1575
  </div>
1597
1576
 
1598
1577
  <!-- Empty cell for alignment -->
@@ -1616,12 +1595,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1616
1595
  [label]="'shared.cancel' | translate"
1617
1596
  severity="secondary"
1618
1597
  [outlined]="true"
1619
- (onClick)="onCancel()" />
1598
+ (onClick)="onCancel()"
1599
+ />
1620
1600
  <p-button
1621
- [label]="isEditMode() ? ('shared.update' | translate) : ('shared.create' | translate)"
1601
+ [label]="
1602
+ isEditMode()
1603
+ ? ('shared.update' | translate)
1604
+ : ('shared.create' | translate)
1605
+ "
1622
1606
  type="submit"
1623
1607
  [loading]="isSaving()"
1624
- [disabled]="!isFormValid() || isSaving()" />
1608
+ [disabled]="!isFormValid() || isSaving()"
1609
+ />
1625
1610
  </div>
1626
1611
  </form>
1627
1612
  </p-card>
@@ -1654,7 +1639,7 @@ class TranslationKeyListPage {
1654
1639
  selectedModule = signal(null, ...(ngDevMode ? [{ debugName: "selectedModule" }] : []));
1655
1640
  searchTerm = signal('', ...(ngDevMode ? [{ debugName: "searchTerm" }] : []));
1656
1641
  moduleOptions = computed(() => [
1657
- { label: this.translateService.instant('shared.all'), value: null },
1642
+ { label: this.translateService.translate('shared.all'), value: null },
1658
1643
  ...this.modules().map((m) => ({ label: m, value: m })),
1659
1644
  ], ...(ngDevMode ? [{ debugName: "moduleOptions" }] : []));
1660
1645
  filteredKeys = computed(() => {
@@ -1677,9 +1662,17 @@ class TranslationKeyListPage {
1677
1662
  loadData() {
1678
1663
  this.isLoading.set(true);
1679
1664
  forkJoin({
1680
- languages: this.languageService.getAll(),
1665
+ languages: from(this.languageService.getAll({
1666
+ pagination: { currentPage: 0, pageSize: 100 },
1667
+ filter: {},
1668
+ sort: { serial: 'ASC', name: 'ASC' },
1669
+ })),
1681
1670
  modules: this.translationKeyService.getModules(),
1682
- keys: this.translationKeyService.getAll(),
1671
+ keys: from(this.translationKeyService.getAll({
1672
+ pagination: { currentPage: 0, pageSize: 1000 },
1673
+ filter: {},
1674
+ sort: { module: 'ASC', key: 'ASC' },
1675
+ })),
1683
1676
  })
1684
1677
  .pipe(takeUntilDestroyed(this.destroyRef))
1685
1678
  .subscribe({
@@ -1702,7 +1695,8 @@ class TranslationKeyListPage {
1702
1695
  });
1703
1696
  }
1704
1697
  loadTranslationCounts() {
1705
- this.translationService.getCounts()
1698
+ this.translationService
1699
+ .getCounts()
1706
1700
  .pipe(takeUntilDestroyed(this.destroyRef))
1707
1701
  .subscribe({
1708
1702
  next: (response) => {
@@ -1731,15 +1725,17 @@ class TranslationKeyListPage {
1731
1725
  icon: 'pi pi-exclamation-triangle',
1732
1726
  acceptButtonStyleClass: 'p-button-danger',
1733
1727
  accept: () => {
1734
- this.translationKeyService.delete(key.id)
1728
+ from(this.translationKeyService.delete({ id: key.id, type: 'delete' }))
1735
1729
  .pipe(takeUntilDestroyed(this.destroyRef))
1736
1730
  .subscribe({
1737
1731
  next: (response) => {
1738
1732
  if (response.success) {
1739
1733
  this.messageService.add({
1740
1734
  severity: 'success',
1741
- summary: this.translateService.instant('shared.success'),
1742
- detail: this.translateService.instant('localization.translation.key.delete.success'),
1735
+ summary: this.translateService.translate('shared.success'),
1736
+ detail: response.messageKey
1737
+ ? this.translateService.translate(response.messageKey, response.messageVariables)
1738
+ : (response.message ?? ''),
1743
1739
  });
1744
1740
  this.loadData();
1745
1741
  }
@@ -1751,29 +1747,34 @@ class TranslationKeyListPage {
1751
1747
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationKeyListPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
1752
1748
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: TranslationKeyListPage, isStandalone: true, selector: "lib-translation-key-list-page", providers: [ConfirmationService, MessageService], ngImport: i0, template: `
1753
1749
  <div class="card">
1754
- <div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4">
1750
+ <div
1751
+ class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4"
1752
+ >
1755
1753
  <div>
1756
- <h3 class="text-lg sm:text-xl font-semibold m-0">{{ 'localization.key.title' | translate }}</h3>
1757
- <p class="text-sm text-muted-color mt-1">{{ 'localization.key.subtitle' | translate }}</p>
1754
+ <h3 class="text-lg sm:text-xl font-semibold m-0">
1755
+ {{ 'localization.key.title' | translate }}
1756
+ </h3>
1757
+ <p class="text-sm text-muted-color mt-1">
1758
+ {{ 'localization.key.subtitle' | translate }}
1759
+ </p>
1758
1760
  </div>
1759
1761
  <div class="flex gap-2">
1760
1762
  <p-button
1761
1763
  *hasPermission="TRANSLATION_KEY_PERMISSIONS.CREATE"
1762
- [label]="'localization.key.create' | translate"
1764
+ [label]="'localization.key.new' | translate"
1763
1765
  icon="pi pi-plus"
1764
1766
  (onClick)="onCreate()"
1765
1767
  />
1766
1768
  </div>
1767
1769
  </div>
1768
1770
 
1769
- <!-- Filters -->
1770
1771
  <div class="flex flex-col md:flex-row gap-4 mb-4">
1771
1772
  <div class="flex-1">
1772
1773
  <p-select
1773
1774
  [options]="moduleOptions()"
1774
1775
  [ngModel]="selectedModule()"
1775
1776
  (ngModelChange)="onModuleChange($event)"
1776
- [ngModelOptions]="{standalone: true}"
1777
+ [ngModelOptions]="{ standalone: true }"
1777
1778
  optionLabel="label"
1778
1779
  optionValue="value"
1779
1780
  [placeholder]="'localization.key.filter.module' | translate"
@@ -1788,7 +1789,7 @@ class TranslationKeyListPage {
1788
1789
  pInputText
1789
1790
  [ngModel]="searchTerm()"
1790
1791
  (ngModelChange)="searchTerm.set($event)"
1791
- [ngModelOptions]="{standalone: true}"
1792
+ [ngModelOptions]="{ standalone: true }"
1792
1793
  [placeholder]="'shared.search' | translate"
1793
1794
  class="w-full"
1794
1795
  />
@@ -1803,18 +1804,28 @@ class TranslationKeyListPage {
1803
1804
  [paginator]="true"
1804
1805
  [rows]="15"
1805
1806
  [rowsPerPageOptions]="[10, 15, 25, 50]"
1806
- styleClass="p-datatable-sm"
1807
+ class="p-datatable-sm"
1807
1808
  >
1808
1809
  <ng-template #header>
1809
1810
  <tr>
1810
- <th style="width: 15%">{{ 'localization.key.module' | translate }}</th>
1811
- <th style="width: 25%">{{ 'localization.key.name' | translate }}</th>
1812
- <th style="width: 35%">{{ 'localization.key.default.message' | translate }}</th>
1813
- <th style="width: 10%">{{ 'localization.key.translations' | translate }}</th>
1811
+ <th style="width: 15%">
1812
+ {{ 'localization.key.module' | translate }}
1813
+ </th>
1814
+ <th style="width: 25%">
1815
+ {{ 'localization.key.name' | translate }}
1816
+ </th>
1817
+ <th style="width: 35%">
1818
+ {{ 'localization.key.default.message' | translate }}
1819
+ </th>
1820
+ <th style="width: 10%">
1821
+ {{ 'localization.key.translations' | translate }}
1822
+ </th>
1814
1823
  <th style="width: 15%">{{ 'shared.actions' | translate }}</th>
1815
1824
  </tr>
1816
1825
  </ng-template>
1817
1826
  <ng-template #body let-key>
1827
+ @let translationCount = getTranslationCount(key.id);
1828
+ @let languageCount = languages().length;
1818
1829
  <tr>
1819
1830
  <td>
1820
1831
  <p-tag [value]="key.module" severity="secondary" />
@@ -1827,8 +1838,13 @@ class TranslationKeyListPage {
1827
1838
  </td>
1828
1839
  <td>{{ key.defaultMessage }}</td>
1829
1840
  <td class="text-center">
1830
- <span class="text-sm" [class.text-green-500]="getTranslationCount(key.id) === languages().length" [class.text-orange-500]="getTranslationCount(key.id) < languages().length && getTranslationCount(key.id) > 0" [class.text-muted-color]="getTranslationCount(key.id) === 0">
1831
- {{ getTranslationCount(key.id) }}/{{ languages().length }}
1841
+ <span
1842
+ class="text-sm"
1843
+ [class.text-green-500]="translationCount === languageCount"
1844
+ [class.text-orange-500]="translationCount < languageCount && translationCount > 0"
1845
+ [class.text-muted-color]="translationCount === 0"
1846
+ >
1847
+ {{ translationCount }}/{{ languageCount }}
1832
1848
  </span>
1833
1849
  </td>
1834
1850
  <td>
@@ -1867,39 +1883,50 @@ class TranslationKeyListPage {
1867
1883
 
1868
1884
  <p-confirmDialog />
1869
1885
  <p-toast />
1870
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i2$1.ConfirmDialog, selector: "p-confirmDialog, p-confirmdialog, p-confirm-dialog", inputs: ["header", "icon", "message", "style", "styleClass", "maskStyleClass", "acceptIcon", "acceptLabel", "closeAriaLabel", "acceptAriaLabel", "acceptVisible", "rejectIcon", "rejectLabel", "rejectAriaLabel", "rejectVisible", "acceptButtonStyleClass", "rejectButtonStyleClass", "closeOnEscape", "dismissableMask", "blockScroll", "rtl", "closable", "appendTo", "key", "autoZIndex", "baseZIndex", "transitionOptions", "focusTrap", "defaultFocus", "breakpoints", "modal", "visible", "position", "draggable"], outputs: ["onHide"] }, { kind: "component", type: i4$2.IconField, selector: "p-iconfield, p-iconField, p-icon-field", inputs: ["hostName", "iconPosition", "styleClass"] }, { kind: "component", type: i5$1.InputIcon, selector: "p-inputicon, p-inputIcon", inputs: ["hostName", "styleClass"] }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "component", type: i5.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i8.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "component", type: i4.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "component", type: i10.Toast, selector: "p-toast", inputs: ["key", "autoZIndex", "baseZIndex", "life", "styleClass", "position", "preventOpenDuplicates", "preventDuplicates", "showTransformOptions", "hideTransformOptions", "showTransitionOptions", "hideTransitionOptions", "motionOptions", "breakpoints"], outputs: ["onClose"] }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: HasPermissionDirective, selector: "[hasPermission]", inputs: ["hasPermission"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1886
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i2$1.ConfirmDialog, selector: "p-confirmDialog, p-confirmdialog, p-confirm-dialog", inputs: ["header", "icon", "message", "style", "styleClass", "maskStyleClass", "acceptIcon", "acceptLabel", "closeAriaLabel", "acceptAriaLabel", "acceptVisible", "rejectIcon", "rejectLabel", "rejectAriaLabel", "rejectVisible", "acceptButtonStyleClass", "rejectButtonStyleClass", "closeOnEscape", "dismissableMask", "blockScroll", "rtl", "closable", "appendTo", "key", "autoZIndex", "baseZIndex", "transitionOptions", "focusTrap", "defaultFocus", "breakpoints", "modal", "visible", "position", "draggable"], outputs: ["onHide"] }, { kind: "component", type: i4$2.IconField, selector: "p-iconfield, p-iconField, p-icon-field", inputs: ["hostName", "iconPosition", "styleClass"] }, { kind: "component", type: i5$2.InputIcon, selector: "p-inputicon, p-inputIcon", inputs: ["hostName", "styleClass"] }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "component", type: i5$1.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i3.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "component", type: i4.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "component", type: i5.Toast, selector: "p-toast", inputs: ["key", "autoZIndex", "baseZIndex", "life", "styleClass", "position", "preventOpenDuplicates", "preventDuplicates", "showTransformOptions", "hideTransformOptions", "showTransitionOptions", "hideTransitionOptions", "motionOptions", "breakpoints"], outputs: ["onClose"] }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: HasPermissionDirective, selector: "[hasPermission]", inputs: ["hasPermission"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1871
1887
  }
1872
1888
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationKeyListPage, decorators: [{
1873
1889
  type: Component,
1874
1890
  args: [{
1875
1891
  selector: 'lib-translation-key-list-page',
1876
- imports: [AngularModule, PrimeModule, FormsModule, HasPermissionDirective, TranslatePipe],
1892
+ imports: [
1893
+ AngularModule,
1894
+ PrimeModule,
1895
+ FormsModule,
1896
+ HasPermissionDirective,
1897
+ TranslatePipe,
1898
+ ],
1877
1899
  providers: [ConfirmationService, MessageService],
1878
1900
  template: `
1879
1901
  <div class="card">
1880
- <div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4">
1902
+ <div
1903
+ class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4"
1904
+ >
1881
1905
  <div>
1882
- <h3 class="text-lg sm:text-xl font-semibold m-0">{{ 'localization.key.title' | translate }}</h3>
1883
- <p class="text-sm text-muted-color mt-1">{{ 'localization.key.subtitle' | translate }}</p>
1906
+ <h3 class="text-lg sm:text-xl font-semibold m-0">
1907
+ {{ 'localization.key.title' | translate }}
1908
+ </h3>
1909
+ <p class="text-sm text-muted-color mt-1">
1910
+ {{ 'localization.key.subtitle' | translate }}
1911
+ </p>
1884
1912
  </div>
1885
1913
  <div class="flex gap-2">
1886
1914
  <p-button
1887
1915
  *hasPermission="TRANSLATION_KEY_PERMISSIONS.CREATE"
1888
- [label]="'localization.key.create' | translate"
1916
+ [label]="'localization.key.new' | translate"
1889
1917
  icon="pi pi-plus"
1890
1918
  (onClick)="onCreate()"
1891
1919
  />
1892
1920
  </div>
1893
1921
  </div>
1894
1922
 
1895
- <!-- Filters -->
1896
1923
  <div class="flex flex-col md:flex-row gap-4 mb-4">
1897
1924
  <div class="flex-1">
1898
1925
  <p-select
1899
1926
  [options]="moduleOptions()"
1900
1927
  [ngModel]="selectedModule()"
1901
1928
  (ngModelChange)="onModuleChange($event)"
1902
- [ngModelOptions]="{standalone: true}"
1929
+ [ngModelOptions]="{ standalone: true }"
1903
1930
  optionLabel="label"
1904
1931
  optionValue="value"
1905
1932
  [placeholder]="'localization.key.filter.module' | translate"
@@ -1914,7 +1941,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1914
1941
  pInputText
1915
1942
  [ngModel]="searchTerm()"
1916
1943
  (ngModelChange)="searchTerm.set($event)"
1917
- [ngModelOptions]="{standalone: true}"
1944
+ [ngModelOptions]="{ standalone: true }"
1918
1945
  [placeholder]="'shared.search' | translate"
1919
1946
  class="w-full"
1920
1947
  />
@@ -1929,18 +1956,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1929
1956
  [paginator]="true"
1930
1957
  [rows]="15"
1931
1958
  [rowsPerPageOptions]="[10, 15, 25, 50]"
1932
- styleClass="p-datatable-sm"
1959
+ class="p-datatable-sm"
1933
1960
  >
1934
1961
  <ng-template #header>
1935
1962
  <tr>
1936
- <th style="width: 15%">{{ 'localization.key.module' | translate }}</th>
1937
- <th style="width: 25%">{{ 'localization.key.name' | translate }}</th>
1938
- <th style="width: 35%">{{ 'localization.key.default.message' | translate }}</th>
1939
- <th style="width: 10%">{{ 'localization.key.translations' | translate }}</th>
1963
+ <th style="width: 15%">
1964
+ {{ 'localization.key.module' | translate }}
1965
+ </th>
1966
+ <th style="width: 25%">
1967
+ {{ 'localization.key.name' | translate }}
1968
+ </th>
1969
+ <th style="width: 35%">
1970
+ {{ 'localization.key.default.message' | translate }}
1971
+ </th>
1972
+ <th style="width: 10%">
1973
+ {{ 'localization.key.translations' | translate }}
1974
+ </th>
1940
1975
  <th style="width: 15%">{{ 'shared.actions' | translate }}</th>
1941
1976
  </tr>
1942
1977
  </ng-template>
1943
1978
  <ng-template #body let-key>
1979
+ @let translationCount = getTranslationCount(key.id);
1980
+ @let languageCount = languages().length;
1944
1981
  <tr>
1945
1982
  <td>
1946
1983
  <p-tag [value]="key.module" severity="secondary" />
@@ -1953,8 +1990,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1953
1990
  </td>
1954
1991
  <td>{{ key.defaultMessage }}</td>
1955
1992
  <td class="text-center">
1956
- <span class="text-sm" [class.text-green-500]="getTranslationCount(key.id) === languages().length" [class.text-orange-500]="getTranslationCount(key.id) < languages().length && getTranslationCount(key.id) > 0" [class.text-muted-color]="getTranslationCount(key.id) === 0">
1957
- {{ getTranslationCount(key.id) }}/{{ languages().length }}
1993
+ <span
1994
+ class="text-sm"
1995
+ [class.text-green-500]="translationCount === languageCount"
1996
+ [class.text-orange-500]="translationCount < languageCount && translationCount > 0"
1997
+ [class.text-muted-color]="translationCount === 0"
1998
+ >
1999
+ {{ translationCount }}/{{ languageCount }}
1958
2000
  </span>
1959
2001
  </td>
1960
2002
  <td>
@@ -2009,7 +2051,9 @@ class TranslationKeyFormPage {
2009
2051
  translationKeyService = inject(TranslationKeyApiService);
2010
2052
  translationService = inject(TranslationApiService);
2011
2053
  messageService = inject(MessageService);
2012
- translateAdapter = inject(TRANSLATE_ADAPTER, { optional: true });
2054
+ translateAdapter = inject(TRANSLATE_ADAPTER, {
2055
+ optional: true,
2056
+ });
2013
2057
  destroyRef = inject(DestroyRef);
2014
2058
  routeParams = toSignal(this.route.paramMap);
2015
2059
  loadTriggered = false;
@@ -2020,8 +2064,8 @@ class TranslationKeyFormPage {
2020
2064
  languages = signal([], ...(ngDevMode ? [{ debugName: "languages" }] : []));
2021
2065
  modules = signal([], ...(ngDevMode ? [{ debugName: "modules" }] : []));
2022
2066
  translations = signal([], ...(ngDevMode ? [{ debugName: "translations" }] : []));
2023
- translate(key) {
2024
- return this.translateAdapter?.translate(key) ?? key;
2067
+ translate(key, vars) {
2068
+ return this.translateAdapter?.translate(key, vars) ?? key;
2025
2069
  }
2026
2070
  moduleOptions = computed(() => this.modules().map((m) => ({ label: m, value: m })), ...(ngDevMode ? [{ debugName: "moduleOptions" }] : []));
2027
2071
  formModel = signal({
@@ -2032,12 +2076,18 @@ class TranslationKeyFormPage {
2032
2076
  variables: '',
2033
2077
  }, ...(ngDevMode ? [{ debugName: "formModel" }] : []));
2034
2078
  formTree = form(this.formModel, (f) => {
2035
- required(f.key, { message: this.translate('localization.validation.key.required') });
2036
- required(f.defaultMessage, { message: this.translate('localization.validation.default.message.required') });
2079
+ required(f.key, {
2080
+ message: this.translate('localization.validation.key.required'),
2081
+ });
2082
+ required(f.defaultMessage, {
2083
+ message: this.translate('localization.validation.default.message.required'),
2084
+ });
2037
2085
  });
2038
2086
  isFormValid = computed(() => {
2039
2087
  const model = this.formModel();
2040
- return model.key.trim().length > 0 && model.module.trim().length > 0 && model.defaultMessage.trim().length > 0;
2088
+ return (model.key.trim().length > 0 &&
2089
+ model.module.trim().length > 0 &&
2090
+ model.defaultMessage.trim().length > 0);
2041
2091
  }, ...(ngDevMode ? [{ debugName: "isFormValid" }] : []));
2042
2092
  constructor() {
2043
2093
  effect(() => {
@@ -2057,7 +2107,11 @@ class TranslationKeyFormPage {
2057
2107
  loadInitialData() {
2058
2108
  this.isLoadingLanguages.set(true);
2059
2109
  forkJoin({
2060
- languages: this.languageService.getAll(),
2110
+ languages: from(this.languageService.getAll({
2111
+ pagination: { currentPage: 0, pageSize: 100 },
2112
+ filter: {},
2113
+ sort: { serial: 'ASC', name: 'ASC' },
2114
+ })),
2061
2115
  modules: this.translationKeyService.getModules(),
2062
2116
  })
2063
2117
  .pipe(takeUntilDestroyed(this.destroyRef), finalize(() => this.isLoadingLanguages.set(false)))
@@ -2084,20 +2138,20 @@ class TranslationKeyFormPage {
2084
2138
  this.formModel.update((m) => ({ ...m, module: value ?? '' }));
2085
2139
  }
2086
2140
  getTranslationValue(languageId) {
2087
- return this.translations().find((t) => t.languageId === languageId)?.value ?? '';
2141
+ return (this.translations().find((t) => t.languageId === languageId)?.value ?? '');
2088
2142
  }
2089
2143
  setTranslationValue(languageId, value) {
2090
2144
  this.translations.update((list) => {
2091
2145
  const existing = list.find((t) => t.languageId === languageId);
2092
2146
  if (existing) {
2093
- return list.map((t) => (t.languageId === languageId ? { ...t, value } : t));
2147
+ return list.map((t) => t.languageId === languageId ? { ...t, value } : t);
2094
2148
  }
2095
2149
  return [...list, { languageId, value }];
2096
2150
  });
2097
2151
  }
2098
2152
  loadTranslationKey(id) {
2099
2153
  this.isSaving.set(true);
2100
- this.translationKeyService.getById(id)
2154
+ from(this.translationKeyService.findById(id))
2101
2155
  .pipe(takeUntilDestroyed(this.destroyRef), finalize(() => this.isSaving.set(false)))
2102
2156
  .subscribe({
2103
2157
  next: (response) => {
@@ -2111,7 +2165,8 @@ class TranslationKeyFormPage {
2111
2165
  });
2112
2166
  }
2113
2167
  loadExistingTranslations(keyId) {
2114
- this.translationService.getByKeyId(keyId)
2168
+ this.translationService
2169
+ .getByKeyId(keyId)
2115
2170
  .pipe(takeUntilDestroyed(this.destroyRef))
2116
2171
  .subscribe({
2117
2172
  next: (response) => {
@@ -2125,7 +2180,11 @@ class TranslationKeyFormPage {
2125
2180
  existing.translationId = t.id;
2126
2181
  }
2127
2182
  else {
2128
- updated.push({ languageId: t.languageId, value: t.value, translationId: t.id });
2183
+ updated.push({
2184
+ languageId: t.languageId,
2185
+ value: t.value,
2186
+ translationId: t.id,
2187
+ });
2129
2188
  }
2130
2189
  });
2131
2190
  return updated;
@@ -2167,12 +2226,12 @@ class TranslationKeyFormPage {
2167
2226
  description: model.description || undefined,
2168
2227
  variables: variables.length > 0 ? variables : undefined,
2169
2228
  };
2170
- this.translationKeyService.create(createDto)
2229
+ from(this.translationKeyService.insert(createDto))
2171
2230
  .pipe(takeUntilDestroyed(this.destroyRef))
2172
2231
  .subscribe({
2173
2232
  next: (response) => {
2174
2233
  if (response.success && response.data) {
2175
- this.saveTranslations(response.data.id);
2234
+ this.saveTranslations(response.data.id, response.messageKey, response.messageVariables);
2176
2235
  }
2177
2236
  else {
2178
2237
  this.isSaving.set(false);
@@ -2190,12 +2249,12 @@ class TranslationKeyFormPage {
2190
2249
  description: model.description || undefined,
2191
2250
  variables: variables.length > 0 ? variables : undefined,
2192
2251
  };
2193
- this.translationKeyService.update(updateDto)
2252
+ from(this.translationKeyService.update(updateDto))
2194
2253
  .pipe(takeUntilDestroyed(this.destroyRef))
2195
2254
  .subscribe({
2196
2255
  next: (response) => {
2197
2256
  if (response.success) {
2198
- this.saveTranslations(this.existingKey().id);
2257
+ this.saveTranslations(this.existingKey().id, response.messageKey, response.messageVariables);
2199
2258
  }
2200
2259
  else {
2201
2260
  this.isSaving.set(false);
@@ -2204,7 +2263,7 @@ class TranslationKeyFormPage {
2204
2263
  error: () => this.isSaving.set(false),
2205
2264
  });
2206
2265
  }
2207
- saveTranslations(keyId) {
2266
+ saveTranslations(keyId, messageKey, messageVariables) {
2208
2267
  const translationsToSave = this.translations()
2209
2268
  .filter((t) => t.value?.trim())
2210
2269
  .map((t) => ({
@@ -2215,21 +2274,22 @@ class TranslationKeyFormPage {
2215
2274
  isVerified: false,
2216
2275
  }));
2217
2276
  if (translationsToSave.length === 0) {
2218
- this.onSaveSuccess();
2277
+ this.onSaveSuccess(messageKey, messageVariables);
2219
2278
  return;
2220
2279
  }
2221
- this.translationService.bulkUpdate(translationsToSave)
2280
+ this.translationService
2281
+ .bulkUpdate(translationsToSave)
2222
2282
  .pipe(takeUntilDestroyed(this.destroyRef))
2223
2283
  .subscribe({
2224
- next: () => this.onSaveSuccess(),
2225
- error: () => this.onSaveSuccess(),
2284
+ next: () => this.onSaveSuccess(messageKey, messageVariables),
2285
+ error: () => this.onSaveSuccess(messageKey, messageVariables),
2226
2286
  });
2227
2287
  }
2228
- onSaveSuccess() {
2288
+ onSaveSuccess(messageKey, messageVariables) {
2229
2289
  this.messageService.add({
2230
2290
  severity: 'success',
2231
2291
  summary: this.translate('shared.success'),
2232
- detail: this.translate(this.isEditMode() ? 'localization.translation.key.update.success' : 'localization.translation.key.create.success'),
2292
+ detail: messageKey ? this.translate(messageKey, messageVariables) : '',
2233
2293
  });
2234
2294
  this.isSaving.set(false);
2235
2295
  this.router.navigate(['/localization/keys']);
@@ -2243,7 +2303,11 @@ class TranslationKeyFormPage {
2243
2303
  <ng-template #header>
2244
2304
  <div class="flex justify-between items-center p-4">
2245
2305
  <h2 class="text-xl font-semibold m-0">
2246
- {{ isEditMode() ? ('localization.key.edit' | translate) : ('localization.key.create' | translate) }}
2306
+ {{
2307
+ isEditMode()
2308
+ ? ('localization.key.edit' | translate)
2309
+ : ('localization.key.new' | translate)
2310
+ }}
2247
2311
  </h2>
2248
2312
  </div>
2249
2313
  </ng-template>
@@ -2251,71 +2315,98 @@ class TranslationKeyFormPage {
2251
2315
  <form (ngSubmit)="onSubmit()" class="flex flex-col gap-6">
2252
2316
  <!-- Key Info Section -->
2253
2317
  <div class="p-4 surface-ground rounded-lg">
2254
- <h4 class="font-semibold mt-0 mb-4">{{ 'localization.key.about' | translate }}</h4>
2318
+ <h4 class="font-semibold mt-0 mb-4">
2319
+ {{ 'localization.key.about' | translate }}
2320
+ </h4>
2255
2321
  <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
2256
2322
  <!-- Key Name -->
2257
2323
  <div class="flex flex-col gap-2">
2258
- <label for="key" class="font-medium">{{ 'localization.key.name' | translate }} *</label>
2324
+ <label for="key" class="font-medium"
2325
+ >{{ 'localization.key.name' | translate }} *</label
2326
+ >
2259
2327
  <input
2260
2328
  pInputText
2261
2329
  id="key"
2262
2330
  [formField]="formTree.key"
2263
- [placeholder]="'localization.placeholder.key.name' | translate" />
2331
+ [placeholder]="'localization.placeholder.key.name' | translate"
2332
+ />
2264
2333
  </div>
2265
2334
 
2266
2335
  <!-- Module -->
2267
2336
  <div class="flex flex-col gap-2">
2268
- <label for="module" class="font-medium">{{ 'localization.key.module' | translate }} *</label>
2337
+ <label for="module" class="font-medium"
2338
+ >{{ 'localization.key.module' | translate }} *</label
2339
+ >
2269
2340
  <p-select
2270
2341
  id="module"
2271
2342
  [ngModel]="formModel().module"
2272
2343
  (ngModelChange)="updateModule($event)"
2273
- [ngModelOptions]="{standalone: true}"
2344
+ [ngModelOptions]="{ standalone: true }"
2274
2345
  [options]="moduleOptions()"
2275
2346
  optionLabel="label"
2276
2347
  optionValue="value"
2277
2348
  [editable]="true"
2278
2349
  [placeholder]="'localization.key.select.module' | translate"
2279
- class="w-full" />
2350
+ class="w-full"
2351
+ />
2280
2352
  </div>
2281
2353
 
2282
2354
  <!-- Default Message -->
2283
2355
  <div class="flex flex-col gap-2 md:col-span-2">
2284
- <label for="defaultMessage" class="font-medium">{{ 'localization.key.default.message' | translate }} *</label>
2356
+ <label for="defaultMessage" class="font-medium"
2357
+ >{{ 'localization.key.default.message' | translate }} *</label
2358
+ >
2285
2359
  <textarea
2286
2360
  pInputTextarea
2287
2361
  id="defaultMessage"
2288
2362
  [formField]="formTree.defaultMessage"
2289
2363
  [rows]="2"
2290
- [placeholder]="'localization.placeholder.default.message' | translate"></textarea>
2364
+ [placeholder]="
2365
+ 'localization.placeholder.default.message' | translate
2366
+ "
2367
+ ></textarea>
2291
2368
  </div>
2292
2369
 
2293
2370
  <!-- Description -->
2294
2371
  <div class="flex flex-col gap-2 md:col-span-2">
2295
- <label for="description" class="font-medium">{{ 'localization.key.description' | translate }}</label>
2372
+ <label for="description" class="font-medium">{{
2373
+ 'localization.key.description' | translate
2374
+ }}</label>
2296
2375
  <input
2297
2376
  pInputText
2298
2377
  id="description"
2299
2378
  [formField]="formTree.description"
2300
- [placeholder]="'localization.placeholder.key.description' | translate" />
2379
+ [placeholder]="
2380
+ 'localization.placeholder.key.description' | translate
2381
+ "
2382
+ />
2301
2383
  </div>
2302
2384
 
2303
2385
  <!-- Variables -->
2304
2386
  <div class="flex flex-col gap-2 md:col-span-2">
2305
- <label for="variables" class="font-medium">{{ 'localization.key.variables' | translate }}</label>
2387
+ <label for="variables" class="font-medium">{{
2388
+ 'localization.key.variables' | translate
2389
+ }}</label>
2306
2390
  <input
2307
2391
  pInputText
2308
2392
  id="variables"
2309
2393
  [formField]="formTree.variables"
2310
- [placeholder]="'localization.key.variables.placeholder' | translate" />
2311
- <small class="text-muted-color">{{ 'localization.key.variables.hint' | translate }}</small>
2394
+ [placeholder]="
2395
+ 'localization.key.variables.placeholder' | translate
2396
+ "
2397
+ />
2398
+ <small class="text-muted-color">{{
2399
+ 'localization.key.variables.hint' | translate
2400
+ }}</small>
2312
2401
  </div>
2313
2402
  </div>
2314
2403
  </div>
2315
2404
 
2316
2405
  <!-- Translations Section -->
2317
2406
  <div class="p-4 surface-ground rounded-lg">
2318
- <h4 class="font-semibold mt-0 mb-4">{{ 'localization.key.translations.section' | translate }}</h4>
2407
+ <h4 class="font-semibold mt-0 mb-4">
2408
+ {{ 'localization.key.translations.section' | translate }}
2409
+ </h4>
2319
2410
  @if (isLoadingLanguages()) {
2320
2411
  <div class="flex justify-center py-4">
2321
2412
  <p-progressSpinner strokeWidth="4" />
@@ -2332,18 +2423,28 @@ class TranslationKeyFormPage {
2332
2423
  <label class="font-medium">
2333
2424
  {{ lang.name }}
2334
2425
  @if (lang.nativeName && lang.nativeName !== lang.name) {
2335
- <span class="text-muted-color">({{ lang.nativeName }})</span>
2426
+ <span class="text-muted-color"
2427
+ >({{ lang.nativeName }})</span
2428
+ >
2336
2429
  }
2337
2430
  </label>
2338
- <p-tag [value]="lang.code" severity="secondary" class="uppercase" />
2431
+ <p-tag
2432
+ [value]="lang.code"
2433
+ severity="secondary"
2434
+ class="uppercase"
2435
+ />
2339
2436
  </div>
2340
2437
  <textarea
2341
2438
  pInputTextarea
2342
2439
  [ngModel]="getTranslationValue(lang.id)"
2343
2440
  (ngModelChange)="setTranslationValue(lang.id, $event)"
2344
- [ngModelOptions]="{standalone: true}"
2441
+ [ngModelOptions]="{ standalone: true }"
2345
2442
  [rows]="2"
2346
- [placeholder]="formModel().defaultMessage || ('localization.key.enter.translation' | translate)"></textarea>
2443
+ [placeholder]="
2444
+ formModel().defaultMessage ||
2445
+ ('localization.key.enter.translation' | translate)
2446
+ "
2447
+ ></textarea>
2347
2448
  </div>
2348
2449
  }
2349
2450
  </div>
@@ -2356,18 +2457,24 @@ class TranslationKeyFormPage {
2356
2457
  [label]="'shared.cancel' | translate"
2357
2458
  severity="secondary"
2358
2459
  [outlined]="true"
2359
- (onClick)="onCancel()" />
2460
+ (onClick)="onCancel()"
2461
+ />
2360
2462
  <p-button
2361
- [label]="isEditMode() ? ('shared.update' | translate) : ('shared.create' | translate)"
2463
+ [label]="
2464
+ isEditMode()
2465
+ ? ('shared.update' | translate)
2466
+ : ('shared.create' | translate)
2467
+ "
2362
2468
  type="submit"
2363
2469
  [loading]="isSaving()"
2364
- [disabled]="!isFormValid() || isSaving()" />
2470
+ [disabled]="!isFormValid() || isSaving()"
2471
+ />
2365
2472
  </div>
2366
2473
  </form>
2367
2474
  </p-card>
2368
2475
 
2369
2476
  <p-toast />
2370
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "component", type: i5.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i4.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "directive", type: i7.Textarea, selector: "[pTextarea], [pInputTextarea]", inputs: ["pTextareaPT", "pTextareaUnstyled", "autoResize", "pSize", "variant", "fluid", "invalid"], outputs: ["onResize"] }, { kind: "component", type: i10.Toast, selector: "p-toast", inputs: ["key", "autoZIndex", "baseZIndex", "life", "styleClass", "position", "preventOpenDuplicates", "preventDuplicates", "showTransformOptions", "hideTransformOptions", "showTransitionOptions", "hideTransitionOptions", "motionOptions", "breakpoints"], outputs: ["onClose"] }, { kind: "component", type: i9.ProgressSpinner, selector: "p-progressSpinner, p-progress-spinner, p-progressspinner", inputs: ["styleClass", "strokeWidth", "fill", "animationDuration", "ariaLabel"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
2477
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3$1.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "component", type: i5$1.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i4.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "directive", type: i7.Textarea, selector: "[pTextarea], [pInputTextarea]", inputs: ["pTextareaPT", "pTextareaUnstyled", "autoResize", "pSize", "variant", "fluid", "invalid"], outputs: ["onResize"] }, { kind: "component", type: i5.Toast, selector: "p-toast", inputs: ["key", "autoZIndex", "baseZIndex", "life", "styleClass", "position", "preventOpenDuplicates", "preventDuplicates", "showTransformOptions", "hideTransformOptions", "showTransitionOptions", "hideTransitionOptions", "motionOptions", "breakpoints"], outputs: ["onClose"] }, { kind: "component", type: i9.ProgressSpinner, selector: "p-progressSpinner, p-progress-spinner, p-progressspinner", inputs: ["styleClass", "strokeWidth", "fill", "animationDuration", "ariaLabel"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
2371
2478
  }
2372
2479
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationKeyFormPage, decorators: [{
2373
2480
  type: Component,
@@ -2380,7 +2487,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
2380
2487
  <ng-template #header>
2381
2488
  <div class="flex justify-between items-center p-4">
2382
2489
  <h2 class="text-xl font-semibold m-0">
2383
- {{ isEditMode() ? ('localization.key.edit' | translate) : ('localization.key.create' | translate) }}
2490
+ {{
2491
+ isEditMode()
2492
+ ? ('localization.key.edit' | translate)
2493
+ : ('localization.key.new' | translate)
2494
+ }}
2384
2495
  </h2>
2385
2496
  </div>
2386
2497
  </ng-template>
@@ -2388,71 +2499,98 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
2388
2499
  <form (ngSubmit)="onSubmit()" class="flex flex-col gap-6">
2389
2500
  <!-- Key Info Section -->
2390
2501
  <div class="p-4 surface-ground rounded-lg">
2391
- <h4 class="font-semibold mt-0 mb-4">{{ 'localization.key.about' | translate }}</h4>
2502
+ <h4 class="font-semibold mt-0 mb-4">
2503
+ {{ 'localization.key.about' | translate }}
2504
+ </h4>
2392
2505
  <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
2393
2506
  <!-- Key Name -->
2394
2507
  <div class="flex flex-col gap-2">
2395
- <label for="key" class="font-medium">{{ 'localization.key.name' | translate }} *</label>
2508
+ <label for="key" class="font-medium"
2509
+ >{{ 'localization.key.name' | translate }} *</label
2510
+ >
2396
2511
  <input
2397
2512
  pInputText
2398
2513
  id="key"
2399
2514
  [formField]="formTree.key"
2400
- [placeholder]="'localization.placeholder.key.name' | translate" />
2515
+ [placeholder]="'localization.placeholder.key.name' | translate"
2516
+ />
2401
2517
  </div>
2402
2518
 
2403
2519
  <!-- Module -->
2404
2520
  <div class="flex flex-col gap-2">
2405
- <label for="module" class="font-medium">{{ 'localization.key.module' | translate }} *</label>
2521
+ <label for="module" class="font-medium"
2522
+ >{{ 'localization.key.module' | translate }} *</label
2523
+ >
2406
2524
  <p-select
2407
2525
  id="module"
2408
2526
  [ngModel]="formModel().module"
2409
2527
  (ngModelChange)="updateModule($event)"
2410
- [ngModelOptions]="{standalone: true}"
2528
+ [ngModelOptions]="{ standalone: true }"
2411
2529
  [options]="moduleOptions()"
2412
2530
  optionLabel="label"
2413
2531
  optionValue="value"
2414
2532
  [editable]="true"
2415
2533
  [placeholder]="'localization.key.select.module' | translate"
2416
- class="w-full" />
2534
+ class="w-full"
2535
+ />
2417
2536
  </div>
2418
2537
 
2419
2538
  <!-- Default Message -->
2420
2539
  <div class="flex flex-col gap-2 md:col-span-2">
2421
- <label for="defaultMessage" class="font-medium">{{ 'localization.key.default.message' | translate }} *</label>
2540
+ <label for="defaultMessage" class="font-medium"
2541
+ >{{ 'localization.key.default.message' | translate }} *</label
2542
+ >
2422
2543
  <textarea
2423
2544
  pInputTextarea
2424
2545
  id="defaultMessage"
2425
2546
  [formField]="formTree.defaultMessage"
2426
2547
  [rows]="2"
2427
- [placeholder]="'localization.placeholder.default.message' | translate"></textarea>
2548
+ [placeholder]="
2549
+ 'localization.placeholder.default.message' | translate
2550
+ "
2551
+ ></textarea>
2428
2552
  </div>
2429
2553
 
2430
2554
  <!-- Description -->
2431
2555
  <div class="flex flex-col gap-2 md:col-span-2">
2432
- <label for="description" class="font-medium">{{ 'localization.key.description' | translate }}</label>
2556
+ <label for="description" class="font-medium">{{
2557
+ 'localization.key.description' | translate
2558
+ }}</label>
2433
2559
  <input
2434
2560
  pInputText
2435
2561
  id="description"
2436
2562
  [formField]="formTree.description"
2437
- [placeholder]="'localization.placeholder.key.description' | translate" />
2563
+ [placeholder]="
2564
+ 'localization.placeholder.key.description' | translate
2565
+ "
2566
+ />
2438
2567
  </div>
2439
2568
 
2440
2569
  <!-- Variables -->
2441
2570
  <div class="flex flex-col gap-2 md:col-span-2">
2442
- <label for="variables" class="font-medium">{{ 'localization.key.variables' | translate }}</label>
2571
+ <label for="variables" class="font-medium">{{
2572
+ 'localization.key.variables' | translate
2573
+ }}</label>
2443
2574
  <input
2444
2575
  pInputText
2445
2576
  id="variables"
2446
2577
  [formField]="formTree.variables"
2447
- [placeholder]="'localization.key.variables.placeholder' | translate" />
2448
- <small class="text-muted-color">{{ 'localization.key.variables.hint' | translate }}</small>
2578
+ [placeholder]="
2579
+ 'localization.key.variables.placeholder' | translate
2580
+ "
2581
+ />
2582
+ <small class="text-muted-color">{{
2583
+ 'localization.key.variables.hint' | translate
2584
+ }}</small>
2449
2585
  </div>
2450
2586
  </div>
2451
2587
  </div>
2452
2588
 
2453
2589
  <!-- Translations Section -->
2454
2590
  <div class="p-4 surface-ground rounded-lg">
2455
- <h4 class="font-semibold mt-0 mb-4">{{ 'localization.key.translations.section' | translate }}</h4>
2591
+ <h4 class="font-semibold mt-0 mb-4">
2592
+ {{ 'localization.key.translations.section' | translate }}
2593
+ </h4>
2456
2594
  @if (isLoadingLanguages()) {
2457
2595
  <div class="flex justify-center py-4">
2458
2596
  <p-progressSpinner strokeWidth="4" />
@@ -2469,18 +2607,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
2469
2607
  <label class="font-medium">
2470
2608
  {{ lang.name }}
2471
2609
  @if (lang.nativeName && lang.nativeName !== lang.name) {
2472
- <span class="text-muted-color">({{ lang.nativeName }})</span>
2610
+ <span class="text-muted-color"
2611
+ >({{ lang.nativeName }})</span
2612
+ >
2473
2613
  }
2474
2614
  </label>
2475
- <p-tag [value]="lang.code" severity="secondary" class="uppercase" />
2615
+ <p-tag
2616
+ [value]="lang.code"
2617
+ severity="secondary"
2618
+ class="uppercase"
2619
+ />
2476
2620
  </div>
2477
2621
  <textarea
2478
2622
  pInputTextarea
2479
2623
  [ngModel]="getTranslationValue(lang.id)"
2480
2624
  (ngModelChange)="setTranslationValue(lang.id, $event)"
2481
- [ngModelOptions]="{standalone: true}"
2625
+ [ngModelOptions]="{ standalone: true }"
2482
2626
  [rows]="2"
2483
- [placeholder]="formModel().defaultMessage || ('localization.key.enter.translation' | translate)"></textarea>
2627
+ [placeholder]="
2628
+ formModel().defaultMessage ||
2629
+ ('localization.key.enter.translation' | translate)
2630
+ "
2631
+ ></textarea>
2484
2632
  </div>
2485
2633
  }
2486
2634
  </div>
@@ -2493,12 +2641,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
2493
2641
  [label]="'shared.cancel' | translate"
2494
2642
  severity="secondary"
2495
2643
  [outlined]="true"
2496
- (onClick)="onCancel()" />
2644
+ (onClick)="onCancel()"
2645
+ />
2497
2646
  <p-button
2498
- [label]="isEditMode() ? ('shared.update' | translate) : ('shared.create' | translate)"
2647
+ [label]="
2648
+ isEditMode()
2649
+ ? ('shared.update' | translate)
2650
+ : ('shared.create' | translate)
2651
+ "
2499
2652
  type="submit"
2500
2653
  [loading]="isSaving()"
2501
- [disabled]="!isFormValid() || isSaving()" />
2654
+ [disabled]="!isFormValid() || isSaving()"
2655
+ />
2502
2656
  </div>
2503
2657
  </form>
2504
2658
  </p-card>
@@ -2513,511 +2667,11 @@ var translationKeyForm_page = /*#__PURE__*/Object.freeze({
2513
2667
  TranslationKeyFormPage: TranslationKeyFormPage
2514
2668
  });
2515
2669
 
2516
- class TranslationListPage {
2517
- languageService = inject(LanguageApiService);
2518
- translationKeyService = inject(TranslationKeyApiService);
2519
- translationService = inject(TranslationApiService);
2520
- messageService = inject(MessageService);
2521
- translateService = inject(TranslateService);
2522
- destroyRef = inject(DestroyRef);
2523
- isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
2524
- isSaving = signal(false, ...(ngDevMode ? [{ debugName: "isSaving" }] : []));
2525
- languages = signal([], ...(ngDevMode ? [{ debugName: "languages" }] : []));
2526
- modules = signal([], ...(ngDevMode ? [{ debugName: "modules" }] : []));
2527
- translationRows = signal([], ...(ngDevMode ? [{ debugName: "translationRows" }] : []));
2528
- selectedModule = signal(null, ...(ngDevMode ? [{ debugName: "selectedModule" }] : []));
2529
- selectedLanguageId = signal(null, ...(ngDevMode ? [{ debugName: "selectedLanguageId" }] : []));
2530
- searchTerm = signal('', ...(ngDevMode ? [{ debugName: "searchTerm" }] : []));
2531
- moduleOptions = computed(() => [
2532
- { label: this.translateService.instant('localization.translation.all.modules'), value: null },
2533
- ...this.modules().map((m) => ({ label: m, value: m })),
2534
- ], ...(ngDevMode ? [{ debugName: "moduleOptions" }] : []));
2535
- paginationTemplate = computed(() => this.translateService.instant('localization.translation.pagination'), ...(ngDevMode ? [{ debugName: "paginationTemplate" }] : []));
2536
- languageOptions = computed(() => this.languages().map((l) => ({
2537
- label: `${l.name} (${l.code})`,
2538
- value: l.id,
2539
- })), ...(ngDevMode ? [{ debugName: "languageOptions" }] : []));
2540
- filteredRows = computed(() => {
2541
- const rows = this.translationRows();
2542
- const search = this.searchTerm().toLowerCase().trim();
2543
- if (!search)
2544
- return rows;
2545
- return rows.filter((r) => r.translationKey.key.toLowerCase().includes(search) ||
2546
- r.translationKey.defaultMessage.toLowerCase().includes(search) ||
2547
- r.editValue.toLowerCase().includes(search));
2548
- }, ...(ngDevMode ? [{ debugName: "filteredRows" }] : []));
2549
- hasChanges = computed(() => this.translationRows().some((r) => r.isDirty), ...(ngDevMode ? [{ debugName: "hasChanges" }] : []));
2550
- changedCount = computed(() => this.translationRows().filter((r) => r.isDirty).length, ...(ngDevMode ? [{ debugName: "changedCount" }] : []));
2551
- formatVariable(v) {
2552
- return `{{${v}}}`;
2553
- }
2554
- ngOnInit() {
2555
- this.loadInitialData();
2556
- }
2557
- loadInitialData() {
2558
- this.isLoading.set(true);
2559
- forkJoin({
2560
- languages: this.languageService.getAll(),
2561
- modules: this.translationKeyService.getModules(),
2562
- })
2563
- .pipe(takeUntilDestroyed(this.destroyRef))
2564
- .subscribe({
2565
- next: ({ languages, modules }) => {
2566
- if (languages.success && languages.data) {
2567
- this.languages.set(languages.data.filter((l) => l.isActive));
2568
- const defaultLang = languages.data.find((l) => l.isDefault);
2569
- if (defaultLang) {
2570
- this.selectedLanguageId.set(defaultLang.id);
2571
- }
2572
- else if (languages.data.length > 0) {
2573
- this.selectedLanguageId.set(languages.data[0].id);
2574
- }
2575
- }
2576
- if (modules.success && modules.data) {
2577
- this.modules.set(modules.data);
2578
- }
2579
- this.isLoading.set(false);
2580
- },
2581
- error: () => {
2582
- this.isLoading.set(false);
2583
- },
2584
- });
2585
- }
2586
- onModuleChange(value) {
2587
- this.selectedModule.set(value);
2588
- this.loadTranslations();
2589
- }
2590
- onLanguageChange(value) {
2591
- this.selectedLanguageId.set(value);
2592
- this.loadTranslations();
2593
- }
2594
- loadTranslations() {
2595
- const languageId = this.selectedLanguageId();
2596
- if (!languageId) {
2597
- this.translationRows.set([]);
2598
- return;
2599
- }
2600
- this.isLoading.set(true);
2601
- const module = this.selectedModule();
2602
- const keysRequest = module
2603
- ? this.translationKeyService.getByModule(module)
2604
- : this.translationKeyService.getAll();
2605
- const translationsRequest = module
2606
- ? this.translationService.getByModuleAndLanguageId(module, languageId)
2607
- : this.translationService.getByLanguageId(languageId);
2608
- forkJoin({
2609
- keys: keysRequest,
2610
- translations: translationsRequest,
2611
- })
2612
- .pipe(takeUntilDestroyed(this.destroyRef))
2613
- .subscribe({
2614
- next: ({ keys, translations }) => {
2615
- const translationMap = new Map();
2616
- if (translations.success && translations.data) {
2617
- translations.data.forEach((t) => {
2618
- translationMap.set(t.translationKeyId, t);
2619
- });
2620
- }
2621
- const rows = [];
2622
- if (keys.success && keys.data) {
2623
- keys.data.forEach((key) => {
2624
- const translation = translationMap.get(key.id) ?? null;
2625
- rows.push({
2626
- translationKey: key,
2627
- translation,
2628
- editValue: translation?.value ?? '',
2629
- isVerified: translation?.isVerified ?? false,
2630
- isDirty: false,
2631
- });
2632
- });
2633
- }
2634
- this.translationRows.set(rows);
2635
- this.isLoading.set(false);
2636
- },
2637
- error: () => {
2638
- this.isLoading.set(false);
2639
- },
2640
- });
2641
- }
2642
- onValueChange(row) {
2643
- const originalValue = row.translation?.value ?? '';
2644
- const originalVerified = row.translation?.isVerified ?? false;
2645
- row.isDirty = row.editValue !== originalValue || row.isVerified !== originalVerified;
2646
- this.translationRows.update((rows) => [...rows]);
2647
- }
2648
- onVerifiedChange(row) {
2649
- this.onValueChange(row);
2650
- }
2651
- saveChanges() {
2652
- const changedRows = this.translationRows().filter((r) => r.isDirty);
2653
- const languageId = this.selectedLanguageId();
2654
- if (changedRows.length === 0 || !languageId)
2655
- return;
2656
- this.isSaving.set(true);
2657
- const items = changedRows.map((row) => ({
2658
- id: row.translation?.id,
2659
- languageId,
2660
- translationKeyId: row.translationKey.id,
2661
- value: row.editValue,
2662
- isVerified: row.isVerified,
2663
- }));
2664
- this.translationService
2665
- .bulkUpdate(items)
2666
- .pipe(takeUntilDestroyed(this.destroyRef))
2667
- .subscribe({
2668
- next: (response) => {
2669
- if (response.success) {
2670
- this.messageService.add({
2671
- severity: 'success',
2672
- summary: this.translateService.instant('localization.translation.save.success'),
2673
- detail: this.translateService.instant('localization.translation.save.success.detail'),
2674
- });
2675
- this.loadTranslations();
2676
- }
2677
- this.isSaving.set(false);
2678
- },
2679
- error: () => {
2680
- this.isSaving.set(false);
2681
- },
2682
- });
2683
- }
2684
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationListPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
2685
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: TranslationListPage, isStandalone: true, selector: "lib-translation-list-page", providers: [MessageService], ngImport: i0, template: `
2686
- <div class="card">
2687
- <div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 mb-4">
2688
- <h2 class="text-xl font-semibold m-0">{{ 'localization.translation.title' | translate }}</h2>
2689
- <div class="flex gap-2">
2690
- <p-button
2691
- [label]="'shared.save' | translate"
2692
- icon="pi pi-save"
2693
- [disabled]="!hasChanges()"
2694
- [loading]="isSaving()"
2695
- (onClick)="saveChanges()"
2696
- />
2697
- </div>
2698
- </div>
2699
-
2700
- <div class="flex flex-col md:flex-row gap-4 mb-4">
2701
- <div class="flex-1">
2702
- <label class="block font-medium mb-2">{{ 'localization.translation.module' | translate }}</label>
2703
- <p-select
2704
- [options]="moduleOptions()"
2705
- [ngModel]="selectedModule()"
2706
- (ngModelChange)="onModuleChange($event)"
2707
- [ngModelOptions]="{standalone: true}"
2708
- optionLabel="label"
2709
- optionValue="value"
2710
- [placeholder]="'localization.translation.select.module' | translate"
2711
- class="w-full"
2712
- />
2713
- </div>
2714
- <div class="flex-1">
2715
- <label class="block font-medium mb-2">{{ 'localization.translation.language' | translate }}</label>
2716
- <p-select
2717
- [options]="languageOptions()"
2718
- [ngModel]="selectedLanguageId()"
2719
- (ngModelChange)="onLanguageChange($event)"
2720
- [ngModelOptions]="{standalone: true}"
2721
- optionLabel="label"
2722
- optionValue="value"
2723
- [placeholder]="'localization.translation.select.language' | translate"
2724
- class="w-full"
2725
- />
2726
- </div>
2727
- <div class="flex-1">
2728
- <label class="block font-medium mb-2">{{ 'shared.search' | translate }}</label>
2729
- <p-iconfield class="w-full">
2730
- <p-inputicon class="pi pi-search" />
2731
- <input
2732
- pInputText
2733
- [ngModel]="searchTerm()"
2734
- (ngModelChange)="searchTerm.set($event)"
2735
- [ngModelOptions]="{standalone: true}"
2736
- [placeholder]="'localization.translation.search.keys' | translate"
2737
- class="w-full"
2738
- />
2739
- </p-iconfield>
2740
- </div>
2741
- </div>
2742
-
2743
- @if (isLoading()) {
2744
- <div class="flex justify-center py-8">
2745
- <p-progressSpinner strokeWidth="4" />
2746
- </div>
2747
- } @else if (!selectedLanguageId()) {
2748
- <div class="text-center py-8 text-muted-color">
2749
- <p>{{ 'localization.translation.select.language.hint' | translate }}</p>
2750
- </div>
2751
- } @else if (filteredRows().length === 0) {
2752
- <div class="text-center py-8 text-muted-color">
2753
- <p>{{ 'localization.translation.no.keys' | translate }}</p>
2754
- </div>
2755
- } @else {
2756
- <p-table
2757
- [value]="filteredRows()"
2758
- [paginator]="true"
2759
- [rows]="20"
2760
- [rowsPerPageOptions]="[10, 20, 50, 100]"
2761
- [showCurrentPageReport]="true"
2762
- [currentPageReportTemplate]="paginationTemplate()"
2763
- styleClass="p-datatable-sm"
2764
- >
2765
- <ng-template #header>
2766
- <tr>
2767
- <th style="width: 30%">{{ 'localization.translation.key' | translate }}</th>
2768
- <th style="width: 25%">{{ 'localization.translation.default.message' | translate }}</th>
2769
- <th style="width: 35%">{{ 'localization.translation.value' | translate }}</th>
2770
- <th style="width: 10%">{{ 'localization.translation.verified' | translate }}</th>
2771
- </tr>
2772
- </ng-template>
2773
- <ng-template #body let-row>
2774
- <tr [class.bg-yellow-50]="row.isDirty">
2775
- <td>
2776
- <div class="font-medium text-sm">{{ row.translationKey.key }}</div>
2777
- @if (row.translationKey.description) {
2778
- <small class="text-muted-color">{{ row.translationKey.description }}</small>
2779
- }
2780
- @if (row.translationKey.variables?.length) {
2781
- <div class="mt-1">
2782
- @for (v of row.translationKey.variables; track v) {
2783
- <span class="text-xs bg-surface-100 px-2 py-1 rounded mr-1">{{ formatVariable(v) }}</span>
2784
- }
2785
- </div>
2786
- }
2787
- </td>
2788
- <td>
2789
- <span class="text-sm">{{ row.translationKey.defaultMessage }}</span>
2790
- </td>
2791
- <td>
2792
- <textarea
2793
- pInputTextarea
2794
- [(ngModel)]="row.editValue"
2795
- [ngModelOptions]="{standalone: true}"
2796
- [rows]="2"
2797
- class="w-full"
2798
- [placeholder]="row.translationKey.defaultMessage"
2799
- (input)="onValueChange(row)"
2800
- ></textarea>
2801
- </td>
2802
- <td class="text-center">
2803
- <p-checkbox
2804
- [(ngModel)]="row.isVerified"
2805
- [ngModelOptions]="{standalone: true}"
2806
- [binary]="true"
2807
- (onChange)="onVerifiedChange(row)"
2808
- />
2809
- </td>
2810
- </tr>
2811
- </ng-template>
2812
- <ng-template #emptymessage>
2813
- <tr>
2814
- <td colspan="4" class="text-center py-4 text-muted-color">
2815
- {{ 'shared.no.results' | translate }}
2816
- </td>
2817
- </tr>
2818
- </ng-template>
2819
- </p-table>
2820
- }
2821
-
2822
- <div class="flex justify-between items-center mt-4">
2823
- <div class="text-muted-color">
2824
- @if (hasChanges()) {
2825
- <span class="text-orange-500">
2826
- <i class="pi pi-exclamation-circle mr-1"></i>
2827
- {{ 'localization.translation.unsaved.changes' | translate: { count: changedCount() } }}
2828
- </span>
2829
- }
2830
- </div>
2831
- <p-button
2832
- [label]="'shared.save' | translate"
2833
- icon="pi pi-save"
2834
- [disabled]="!hasChanges()"
2835
- [loading]="isSaving()"
2836
- (onClick)="saveChanges()"
2837
- />
2838
- </div>
2839
- </div>
2840
-
2841
- <p-toast />
2842
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3$1.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["hostName", "value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "component", type: i4$2.IconField, selector: "p-iconfield, p-iconField, p-icon-field", inputs: ["hostName", "iconPosition", "styleClass"] }, { kind: "component", type: i5$1.InputIcon, selector: "p-inputicon, p-inputIcon", inputs: ["hostName", "styleClass"] }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "component", type: i5.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i8.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i7.Textarea, selector: "[pTextarea], [pInputTextarea]", inputs: ["pTextareaPT", "pTextareaUnstyled", "autoResize", "pSize", "variant", "fluid", "invalid"], outputs: ["onResize"] }, { kind: "component", type: i10.Toast, selector: "p-toast", inputs: ["key", "autoZIndex", "baseZIndex", "life", "styleClass", "position", "preventOpenDuplicates", "preventDuplicates", "showTransformOptions", "hideTransformOptions", "showTransitionOptions", "hideTransitionOptions", "motionOptions", "breakpoints"], outputs: ["onClose"] }, { kind: "component", type: i9.ProgressSpinner, selector: "p-progressSpinner, p-progress-spinner, p-progressspinner", inputs: ["styleClass", "strokeWidth", "fill", "animationDuration", "ariaLabel"] }, { kind: "ngmodule", type: FormsModule }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
2843
- }
2844
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationListPage, decorators: [{
2845
- type: Component,
2846
- args: [{
2847
- selector: 'lib-translation-list-page',
2848
- imports: [AngularModule, PrimeModule, FormsModule, TranslatePipe],
2849
- providers: [MessageService],
2850
- template: `
2851
- <div class="card">
2852
- <div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 mb-4">
2853
- <h2 class="text-xl font-semibold m-0">{{ 'localization.translation.title' | translate }}</h2>
2854
- <div class="flex gap-2">
2855
- <p-button
2856
- [label]="'shared.save' | translate"
2857
- icon="pi pi-save"
2858
- [disabled]="!hasChanges()"
2859
- [loading]="isSaving()"
2860
- (onClick)="saveChanges()"
2861
- />
2862
- </div>
2863
- </div>
2864
-
2865
- <div class="flex flex-col md:flex-row gap-4 mb-4">
2866
- <div class="flex-1">
2867
- <label class="block font-medium mb-2">{{ 'localization.translation.module' | translate }}</label>
2868
- <p-select
2869
- [options]="moduleOptions()"
2870
- [ngModel]="selectedModule()"
2871
- (ngModelChange)="onModuleChange($event)"
2872
- [ngModelOptions]="{standalone: true}"
2873
- optionLabel="label"
2874
- optionValue="value"
2875
- [placeholder]="'localization.translation.select.module' | translate"
2876
- class="w-full"
2877
- />
2878
- </div>
2879
- <div class="flex-1">
2880
- <label class="block font-medium mb-2">{{ 'localization.translation.language' | translate }}</label>
2881
- <p-select
2882
- [options]="languageOptions()"
2883
- [ngModel]="selectedLanguageId()"
2884
- (ngModelChange)="onLanguageChange($event)"
2885
- [ngModelOptions]="{standalone: true}"
2886
- optionLabel="label"
2887
- optionValue="value"
2888
- [placeholder]="'localization.translation.select.language' | translate"
2889
- class="w-full"
2890
- />
2891
- </div>
2892
- <div class="flex-1">
2893
- <label class="block font-medium mb-2">{{ 'shared.search' | translate }}</label>
2894
- <p-iconfield class="w-full">
2895
- <p-inputicon class="pi pi-search" />
2896
- <input
2897
- pInputText
2898
- [ngModel]="searchTerm()"
2899
- (ngModelChange)="searchTerm.set($event)"
2900
- [ngModelOptions]="{standalone: true}"
2901
- [placeholder]="'localization.translation.search.keys' | translate"
2902
- class="w-full"
2903
- />
2904
- </p-iconfield>
2905
- </div>
2906
- </div>
2907
-
2908
- @if (isLoading()) {
2909
- <div class="flex justify-center py-8">
2910
- <p-progressSpinner strokeWidth="4" />
2911
- </div>
2912
- } @else if (!selectedLanguageId()) {
2913
- <div class="text-center py-8 text-muted-color">
2914
- <p>{{ 'localization.translation.select.language.hint' | translate }}</p>
2915
- </div>
2916
- } @else if (filteredRows().length === 0) {
2917
- <div class="text-center py-8 text-muted-color">
2918
- <p>{{ 'localization.translation.no.keys' | translate }}</p>
2919
- </div>
2920
- } @else {
2921
- <p-table
2922
- [value]="filteredRows()"
2923
- [paginator]="true"
2924
- [rows]="20"
2925
- [rowsPerPageOptions]="[10, 20, 50, 100]"
2926
- [showCurrentPageReport]="true"
2927
- [currentPageReportTemplate]="paginationTemplate()"
2928
- styleClass="p-datatable-sm"
2929
- >
2930
- <ng-template #header>
2931
- <tr>
2932
- <th style="width: 30%">{{ 'localization.translation.key' | translate }}</th>
2933
- <th style="width: 25%">{{ 'localization.translation.default.message' | translate }}</th>
2934
- <th style="width: 35%">{{ 'localization.translation.value' | translate }}</th>
2935
- <th style="width: 10%">{{ 'localization.translation.verified' | translate }}</th>
2936
- </tr>
2937
- </ng-template>
2938
- <ng-template #body let-row>
2939
- <tr [class.bg-yellow-50]="row.isDirty">
2940
- <td>
2941
- <div class="font-medium text-sm">{{ row.translationKey.key }}</div>
2942
- @if (row.translationKey.description) {
2943
- <small class="text-muted-color">{{ row.translationKey.description }}</small>
2944
- }
2945
- @if (row.translationKey.variables?.length) {
2946
- <div class="mt-1">
2947
- @for (v of row.translationKey.variables; track v) {
2948
- <span class="text-xs bg-surface-100 px-2 py-1 rounded mr-1">{{ formatVariable(v) }}</span>
2949
- }
2950
- </div>
2951
- }
2952
- </td>
2953
- <td>
2954
- <span class="text-sm">{{ row.translationKey.defaultMessage }}</span>
2955
- </td>
2956
- <td>
2957
- <textarea
2958
- pInputTextarea
2959
- [(ngModel)]="row.editValue"
2960
- [ngModelOptions]="{standalone: true}"
2961
- [rows]="2"
2962
- class="w-full"
2963
- [placeholder]="row.translationKey.defaultMessage"
2964
- (input)="onValueChange(row)"
2965
- ></textarea>
2966
- </td>
2967
- <td class="text-center">
2968
- <p-checkbox
2969
- [(ngModel)]="row.isVerified"
2970
- [ngModelOptions]="{standalone: true}"
2971
- [binary]="true"
2972
- (onChange)="onVerifiedChange(row)"
2973
- />
2974
- </td>
2975
- </tr>
2976
- </ng-template>
2977
- <ng-template #emptymessage>
2978
- <tr>
2979
- <td colspan="4" class="text-center py-4 text-muted-color">
2980
- {{ 'shared.no.results' | translate }}
2981
- </td>
2982
- </tr>
2983
- </ng-template>
2984
- </p-table>
2985
- }
2986
-
2987
- <div class="flex justify-between items-center mt-4">
2988
- <div class="text-muted-color">
2989
- @if (hasChanges()) {
2990
- <span class="text-orange-500">
2991
- <i class="pi pi-exclamation-circle mr-1"></i>
2992
- {{ 'localization.translation.unsaved.changes' | translate: { count: changedCount() } }}
2993
- </span>
2994
- }
2995
- </div>
2996
- <p-button
2997
- [label]="'shared.save' | translate"
2998
- icon="pi pi-save"
2999
- [disabled]="!hasChanges()"
3000
- [loading]="isSaving()"
3001
- (onClick)="saveChanges()"
3002
- />
3003
- </div>
3004
- </div>
3005
-
3006
- <p-toast />
3007
- `,
3008
- }]
3009
- }] });
3010
-
3011
- var translationList_page = /*#__PURE__*/Object.freeze({
3012
- __proto__: null,
3013
- TranslationListPage: TranslationListPage
3014
- });
3015
-
3016
2670
  // Interfaces
3017
2671
 
3018
2672
  /**
3019
2673
  * Generated bundle index. Do not edit.
3020
2674
  */
3021
2675
 
3022
- export { LOCALIZATION_CONFIG, LOCALIZATION_MESSAGES, LOCALIZATION_ROUTES, LanguageApiService, LanguageFormPage, LanguageListPage, LanguageSelectorComponent, LocalizationApiService, LocalizationContainerPage, LocalizationInitializerService, LocalizationStateService, STORAGE_KEYS, TranslateService, TranslationApiService, TranslationKeyApiService, TranslationKeyFormPage, TranslationKeyListPage, TranslationListPage, TranslationLoaderService, getLocalizationConfig, provideLocalization };
2676
+ export { LOCALIZATION_CONFIG, LOCALIZATION_MESSAGES, LOCALIZATION_ROUTES, LanguageApiService, LanguageFormPage, LanguageListPage, LanguageSelectorComponent, LocalizationApiService, LocalizationContainerPage, LocalizationInitializerService, LocalizationStateService, STORAGE_KEYS, TranslateService, TranslationApiService, TranslationKeyApiService, TranslationKeyFormPage, TranslationKeyListPage, TranslationLoaderService, getLocalizationConfig, provideLocalization };
3023
2677
  //# sourceMappingURL=flusys-ng-localization.mjs.map