@flusys/ng-localization 5.0.1 → 5.0.4

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.
@@ -1,34 +1,34 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, inject, PLATFORM_ID, ApplicationRef, signal, computed, effect, Injectable, DestroyRef, ElementRef, Component, APP_INITIALIZER } from '@angular/core';
3
- import { DOCUMENT, isPlatformBrowser } from '@angular/common';
4
- import { PrimeNG } from 'primeng/config';
2
+ import { InjectionToken, Injectable, inject, PLATFORM_ID, ApplicationRef, signal, computed, effect, DestroyRef, ElementRef, Component, APP_INITIALIZER } from '@angular/core';
3
+ import { ApiResourceService, TranslateService, AngularModule, PrimeModule, TranslatePipe, LANGUAGE_PERMISSIONS, HasPermissionDirective, PermissionValidatorService, TRANSLATION_KEY_PERMISSIONS, resolveTranslationModule, SHARED_MESSAGES } from '@flusys/ng-shared';
5
4
  import { HttpClient } from '@angular/common/http';
6
- import { APP_CONFIG, TRANSLATE_ADAPTER } from '@flusys/ng-core';
5
+ import { APP_CONFIG, LOCALIZATION_STATE_SERVICE, LOCALIZATION_API_SERVICE } from '@flusys/ng-core';
7
6
  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';
7
+ import { DOCUMENT, isPlatformBrowser } from '@angular/common';
8
+ import { PrimeNG } from 'primeng/config';
9
9
  import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
10
10
  import * as i1 from 'primeng/styleclass';
11
11
  import { StyleClassModule } from 'primeng/styleclass';
12
12
  import { filter } from 'rxjs/operators';
13
13
  import { LAYOUT_LANGUAGE_SELECTOR, LAYOUT_LANGUAGE_SELECTOR_PANEL } from '@flusys/ng-layout';
14
- import { RouterOutlet, RouterLink, RouterLinkActive, Router, ActivatedRoute } from '@angular/router';
15
- import { ConfirmationService, MessageService } from 'primeng/api';
16
- import * as i2 from 'primeng/button';
17
- import * as i2$1 from 'primeng/confirmdialog';
18
- import * as i3 from 'primeng/table';
19
- import * as i4 from 'primeng/tag';
20
- import * as i5 from 'primeng/toast';
21
- import * as i6 from 'primeng/tooltip';
22
14
  import * as i1$1 from '@angular/forms';
23
15
  import { FormsModule } from '@angular/forms';
24
16
  import { form, required, maxLength, FormField } from '@angular/forms/signals';
25
- import * as i3$1 from 'primeng/card';
26
- import * as i4$1 from 'primeng/inputtext';
27
- import * as i5$1 from 'primeng/select';
28
- import * as i4$2 from 'primeng/iconfield';
29
- import * as i5$2 from 'primeng/inputicon';
17
+ import { Router, ActivatedRoute, RouterOutlet, RouterLink, RouterLinkActive } from '@angular/router';
18
+ import { MessageService, ConfirmationService } from 'primeng/api';
19
+ import * as i2 from 'primeng/button';
20
+ import * as i3 from 'primeng/card';
21
+ import * as i4 from 'primeng/inputtext';
22
+ import * as i5 from 'primeng/select';
23
+ import * as i6 from 'primeng/toast';
24
+ import * as i2$1 from 'primeng/confirmdialog';
25
+ import * as i3$1 from 'primeng/table';
26
+ import * as i4$1 from 'primeng/tag';
27
+ import * as i6$1 from 'primeng/tooltip';
30
28
  import * as i7 from 'primeng/textarea';
31
29
  import * as i9 from 'primeng/progressspinner';
30
+ import * as i4$2 from 'primeng/iconfield';
31
+ import * as i5$1 from 'primeng/inputicon';
32
32
 
33
33
  const LOCALIZATION_CONFIG = new InjectionToken('LOCALIZATION_CONFIG');
34
34
  /** Get localization config with sensible defaults applied */
@@ -155,6 +155,41 @@ const LOCALIZATION_MESSAGES = {
155
155
  'localization.validation.default.message.required': 'Default message is required',
156
156
  };
157
157
 
158
+ class LanguageApiService extends ApiResourceService {
159
+ constructor() {
160
+ super('language', 'localization');
161
+ }
162
+ setDefault(id) {
163
+ return this.http.post(`${this.baseUrl}/set-default`, { id });
164
+ }
165
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LanguageApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
166
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LanguageApiService, providedIn: 'root' });
167
+ }
168
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LanguageApiService, decorators: [{
169
+ type: Injectable,
170
+ args: [{ providedIn: 'root' }]
171
+ }], ctorParameters: () => [] });
172
+
173
+ class LocalizationApiService {
174
+ http = inject(HttpClient);
175
+ appConfig = inject(APP_CONFIG);
176
+ get baseUrl() {
177
+ return `${this.appConfig.apiBaseUrl}/localization`;
178
+ }
179
+ getActiveLanguages() {
180
+ return this.http.post(`${this.baseUrl}/language/get-active`, {});
181
+ }
182
+ getTranslationsByLanguage(languageCode, modules) {
183
+ return this.http.post(`${this.baseUrl}/translation/get-by-language`, { languageCode, ...(modules?.length ? { modules } : {}) });
184
+ }
185
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LocalizationApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
186
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LocalizationApiService, providedIn: 'root' });
187
+ }
188
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LocalizationApiService, decorators: [{
189
+ type: Injectable,
190
+ args: [{ providedIn: 'root' }]
191
+ }] });
192
+
158
193
  class LocalizationStateService {
159
194
  platformId = inject(PLATFORM_ID);
160
195
  document = inject(DOCUMENT);
@@ -202,14 +237,50 @@ class LocalizationStateService {
202
237
  }
203
238
  PRIMENG_NUMERIC_KEYS = new Set(['firstDayOfWeek']);
204
239
  PRIMENG_ARRAY_DEFAULTS = {
205
- dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
240
+ dayNames: [
241
+ 'Sunday',
242
+ 'Monday',
243
+ 'Tuesday',
244
+ 'Wednesday',
245
+ 'Thursday',
246
+ 'Friday',
247
+ 'Saturday',
248
+ ],
206
249
  dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
207
250
  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'],
251
+ monthNames: [
252
+ 'January',
253
+ 'February',
254
+ 'March',
255
+ 'April',
256
+ 'May',
257
+ 'June',
258
+ 'July',
259
+ 'August',
260
+ 'September',
261
+ 'October',
262
+ 'November',
263
+ 'December',
264
+ ],
265
+ monthNamesShort: [
266
+ 'Jan',
267
+ 'Feb',
268
+ 'Mar',
269
+ 'Apr',
270
+ 'May',
271
+ 'Jun',
272
+ 'Jul',
273
+ 'Aug',
274
+ 'Sep',
275
+ 'Oct',
276
+ 'Nov',
277
+ 'Dec',
278
+ ],
210
279
  };
211
280
  syncPrimeNGTranslations(translations, fallbacks) {
212
- const primeNGTranslations = { ...this.PRIMENG_ARRAY_DEFAULTS };
281
+ const primeNGTranslations = {
282
+ ...this.PRIMENG_ARRAY_DEFAULTS,
283
+ };
213
284
  const ariaTranslations = {};
214
285
  const prefix = 'primeng.';
215
286
  const ariaPrefix = 'primeng.aria.';
@@ -221,7 +292,9 @@ class LocalizationStateService {
221
292
  }
222
293
  else if (key.startsWith(prefix)) {
223
294
  const primeKey = this.dotToCamel(key.substring(prefix.length));
224
- primeNGTranslations[primeKey] = this.PRIMENG_NUMERIC_KEYS.has(primeKey) ? parseInt(value, 10) : value;
295
+ primeNGTranslations[primeKey] = this.PRIMENG_NUMERIC_KEYS.has(primeKey)
296
+ ? parseInt(value, 10)
297
+ : value;
225
298
  }
226
299
  }
227
300
  if (Object.keys(ariaTranslations).length > 0) {
@@ -279,26 +352,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
279
352
  args: [{ providedIn: 'root' }]
280
353
  }], ctorParameters: () => [] });
281
354
 
282
- class LocalizationApiService {
283
- http = inject(HttpClient);
284
- appConfig = inject(APP_CONFIG);
285
- get baseUrl() {
286
- return `${this.appConfig.apiBaseUrl}/localization`;
287
- }
288
- getActiveLanguages() {
289
- return this.http.post(`${this.baseUrl}/language/get-active`, {});
290
- }
291
- getTranslationsByLanguage(languageCode, modules) {
292
- return this.http.post(`${this.baseUrl}/translation/get-by-language`, { languageCode, ...(modules?.length ? { modules } : {}) });
293
- }
294
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LocalizationApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
295
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LocalizationApiService, providedIn: 'root' });
296
- }
297
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LocalizationApiService, decorators: [{
298
- type: Injectable,
299
- args: [{ providedIn: 'root' }]
300
- }] });
301
-
302
355
  class LocalizationInitializerService {
303
356
  config = inject(LOCALIZATION_CONFIG, { optional: true });
304
357
  apiService = inject(LocalizationApiService);
@@ -342,49 +395,59 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
342
395
  args: [{ providedIn: 'root' }]
343
396
  }] });
344
397
 
345
- class TranslateService {
346
- localizationState = inject(LocalizationStateService);
347
- localizationConfig = inject(LOCALIZATION_CONFIG, {
348
- optional: true,
349
- });
350
- languageCode = this.localizationState.currentLanguageCode;
351
- translate(key, params) {
352
- if (!this.localizationConfig) {
353
- return this.getFromFallback(key, params);
354
- }
355
- const translations = this.localizationState.translations();
356
- const value = translations[key];
357
- if (!value) {
358
- return this.getFromFallback(key, params);
359
- }
360
- return this.interpolate(value, params);
398
+ class TranslationApiService extends ApiResourceService {
399
+ constructor() {
400
+ super('translation', 'localization');
401
+ }
402
+ getByLanguageId(languageId) {
403
+ return this.http.post(`${this.baseUrl}/get-all`, {
404
+ pagination: { currentPage: 0, pageSize: 10000 },
405
+ filter: { languageId },
406
+ sort: {},
407
+ });
361
408
  }
362
- translateSignal(key, params) {
363
- return computed(() => {
364
- this.localizationState.currentLanguage();
365
- return this.translate(key, params);
409
+ getByModuleAndLanguageId(module, languageId) {
410
+ return this.http.post(`${this.baseUrl}/get-all`, {
411
+ pagination: { currentPage: 0, pageSize: 10000 },
412
+ filter: { languageId, module },
413
+ sort: {},
366
414
  });
367
415
  }
368
- interpolate(template, params) {
369
- if (!params || !template)
370
- return template;
371
- return template.replace(/\{\{(\w+)\}\}/g, (match, paramKey) => {
372
- const value = params[paramKey];
373
- return value !== undefined ? String(value) : match;
416
+ getByKeyId(translationKeyId) {
417
+ return this.http.post(`${this.baseUrl}/get-all`, {
418
+ pagination: { currentPage: 0, pageSize: 1000 },
419
+ filter: { translationKeyId },
420
+ sort: {},
374
421
  });
375
422
  }
376
- getFromFallback(key, params) {
377
- const fallbacks = this.localizationState.fallbackMessages();
378
- const value = fallbacks[key] ?? key;
379
- return this.interpolate(value, params);
423
+ getCounts() {
424
+ return this.http.post(`${this.baseUrl}/get-counts`, {});
425
+ }
426
+ bulkUpdate(translations) {
427
+ return this.http.post(`${this.baseUrl}/update-many-values`, { translations });
428
+ }
429
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
430
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationApiService, providedIn: 'root' });
431
+ }
432
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationApiService, decorators: [{
433
+ type: Injectable,
434
+ args: [{ providedIn: 'root' }]
435
+ }], ctorParameters: () => [] });
436
+
437
+ class TranslationKeyApiService extends ApiResourceService {
438
+ constructor() {
439
+ super('translation-key', 'localization');
440
+ }
441
+ getModules() {
442
+ return this.http.post(`${this.baseUrl}/get-modules`, {});
380
443
  }
381
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
382
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslateService, providedIn: 'root' });
444
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationKeyApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
445
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationKeyApiService, providedIn: 'root' });
383
446
  }
384
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslateService, decorators: [{
447
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationKeyApiService, decorators: [{
385
448
  type: Injectable,
386
449
  args: [{ providedIn: 'root' }]
387
- }] });
450
+ }], ctorParameters: () => [] });
388
451
 
389
452
  class TranslationLoaderService {
390
453
  DEFAULT_INITIAL_MODULES = ['shared', 'layout'];
@@ -457,75 +520,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
457
520
  args: [{ providedIn: 'root' }]
458
521
  }] });
459
522
 
460
- class LanguageApiService extends ApiResourceService {
461
- constructor() {
462
- super('language', inject(HttpClient), 'localization');
463
- }
464
- setDefault(id) {
465
- return this.http.post(`${this.baseUrl}/set-default`, { id });
466
- }
467
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LanguageApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
468
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LanguageApiService, providedIn: 'root' });
469
- }
470
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LanguageApiService, decorators: [{
471
- type: Injectable,
472
- args: [{ providedIn: 'root' }]
473
- }], ctorParameters: () => [] });
474
-
475
- class TranslationKeyApiService extends ApiResourceService {
476
- constructor() {
477
- super('translation-key', inject(HttpClient), 'localization');
478
- }
479
- getModules() {
480
- return this.http.post(`${this.baseUrl}/get-modules`, {});
481
- }
482
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationKeyApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
483
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationKeyApiService, providedIn: 'root' });
484
- }
485
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationKeyApiService, decorators: [{
486
- type: Injectable,
487
- args: [{ providedIn: 'root' }]
488
- }], ctorParameters: () => [] });
489
-
490
- class TranslationApiService extends ApiResourceService {
491
- constructor() {
492
- super('translation', inject(HttpClient), 'localization');
493
- }
494
- getByLanguageId(languageId) {
495
- return this.http.post(`${this.baseUrl}/get-all`, {
496
- pagination: { currentPage: 0, pageSize: 10000 },
497
- filter: { languageId },
498
- sort: {},
499
- });
500
- }
501
- getByModuleAndLanguageId(module, languageId) {
502
- return this.http.post(`${this.baseUrl}/get-all`, {
503
- pagination: { currentPage: 0, pageSize: 10000 },
504
- filter: { languageId, module },
505
- sort: {},
506
- });
507
- }
508
- getByKeyId(translationKeyId) {
509
- return this.http.post(`${this.baseUrl}/get-all`, {
510
- pagination: { currentPage: 0, pageSize: 1000 },
511
- filter: { translationKeyId },
512
- sort: {},
513
- });
514
- }
515
- getCounts() {
516
- return this.http.post(`${this.baseUrl}/get-counts`, {});
517
- }
518
- bulkUpdate(translations) {
519
- return this.http.post(`${this.baseUrl}/update-many-values`, { translations });
520
- }
521
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
522
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationApiService, providedIn: 'root' });
523
- }
524
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationApiService, decorators: [{
525
- type: Injectable,
526
- args: [{ providedIn: 'root' }]
527
- }], ctorParameters: () => [] });
528
-
529
523
  class LanguageSelectorComponent {
530
524
  localizationState = inject(LocalizationStateService);
531
525
  translationLoader = inject(TranslationLoaderService);
@@ -726,8 +720,11 @@ function provideLocalization(config) {
726
720
  LocalizationStateService,
727
721
  LocalizationApiService,
728
722
  LocalizationInitializerService,
729
- TranslateService,
730
- { provide: TRANSLATE_ADAPTER, useExisting: TranslateService },
723
+ {
724
+ provide: LOCALIZATION_STATE_SERVICE,
725
+ useExisting: LocalizationStateService,
726
+ },
727
+ { provide: LOCALIZATION_API_SERVICE, useExisting: LocalizationApiService },
731
728
  {
732
729
  provide: APP_INITIALIZER,
733
730
  useFactory: (initializer) => () => initializer.initialize(),
@@ -736,358 +733,525 @@ function provideLocalization(config) {
736
733
  },
737
734
  ];
738
735
  if (config.enableLayoutSelector) {
739
- providers.push({ provide: LAYOUT_LANGUAGE_SELECTOR, useValue: LanguageSelectorComponent }, { provide: LAYOUT_LANGUAGE_SELECTOR_PANEL, useValue: LanguageSelectorPanelComponent });
736
+ providers.push({
737
+ provide: LAYOUT_LANGUAGE_SELECTOR,
738
+ useValue: LanguageSelectorComponent,
739
+ }, {
740
+ provide: LAYOUT_LANGUAGE_SELECTOR_PANEL,
741
+ useValue: LanguageSelectorPanelComponent,
742
+ });
740
743
  }
741
744
  return providers;
742
745
  }
743
746
 
744
- const LOCALIZATION_ROUTES = [
745
- {
746
- path: '',
747
- resolve: { translations: resolveTranslationModule({ modules: ['localization'], fallbackMessages: { ...LOCALIZATION_MESSAGES, ...SHARED_MESSAGES } }) },
748
- loadComponent: () => Promise.resolve().then(function () { return localizationContainer_page; }).then((m) => m.LocalizationContainerPage),
749
- children: [
750
- {
751
- path: 'languages',
752
- loadComponent: () => Promise.resolve().then(function () { return languageList_page; }).then((m) => m.LanguageListPage),
753
- },
754
- {
755
- path: 'languages/new',
756
- loadComponent: () => Promise.resolve().then(function () { return languageForm_page; }).then((m) => m.LanguageFormPage),
757
- },
758
- {
759
- path: 'languages/:id',
760
- loadComponent: () => Promise.resolve().then(function () { return languageForm_page; }).then((m) => m.LanguageFormPage),
761
- },
762
- {
763
- path: 'keys',
764
- loadComponent: () => Promise.resolve().then(function () { return translationKeyList_page; }).then((m) => m.TranslationKeyListPage),
765
- },
766
- {
767
- path: 'keys/new',
768
- loadComponent: () => Promise.resolve().then(function () { return translationKeyForm_page; }).then((m) => m.TranslationKeyFormPage),
769
- },
770
- {
771
- path: 'keys/:id',
772
- loadComponent: () => Promise.resolve().then(function () { return translationKeyForm_page; }).then((m) => m.TranslationKeyFormPage),
773
- },
774
- {
775
- path: '',
776
- redirectTo: 'languages',
777
- pathMatch: 'full',
778
- },
779
- ],
780
- },
781
- ];
782
-
783
- class LocalizationContainerPage {
784
- permissionValidator = inject(PermissionValidatorService);
785
- allTabs = [
747
+ class LanguageFormPage {
748
+ router = inject(Router);
749
+ route = inject(ActivatedRoute);
750
+ languageService = inject(LanguageApiService);
751
+ messageService = inject(MessageService);
752
+ translateAdapter = inject(TranslateService, {
753
+ optional: true,
754
+ });
755
+ destroyRef = inject(DestroyRef);
756
+ routeParams = toSignal(this.route.paramMap);
757
+ loadTriggered = false;
758
+ isSaving = signal(false, ...(ngDevMode ? [{ debugName: "isSaving" }] : []));
759
+ existingLanguage = signal(null, ...(ngDevMode ? [{ debugName: "existingLanguage" }] : []));
760
+ isEditMode = computed(() => !!this.existingLanguage(), ...(ngDevMode ? [{ debugName: "isEditMode" }] : []));
761
+ translate(key, vars) {
762
+ return this.translateAdapter?.translate(key, vars) ?? key;
763
+ }
764
+ directionOptions = computed(() => [
786
765
  {
787
- id: 'languages',
788
- labelKey: 'localization.tabs.languages',
789
- icon: 'pi pi-globe',
790
- route: 'languages',
791
- permission: LANGUAGE_PERMISSIONS.READ,
766
+ label: this.translate('localization.language.direction.ltr'),
767
+ value: 'ltr',
792
768
  },
793
769
  {
794
- id: 'keys',
795
- labelKey: 'localization.tabs.keys',
796
- icon: 'pi pi-key',
797
- route: 'keys',
798
- permission: TRANSLATION_KEY_PERMISSIONS.READ,
770
+ label: this.translate('localization.language.direction.rtl'),
771
+ value: 'rtl',
799
772
  },
800
- ];
801
- visibleTabs = computed(() => {
802
- // Explicitly track permissions signal for reactivity
803
- const permissions = this.permissionValidator.permissions();
804
- if (!permissions.length)
805
- return [];
806
- return this.allTabs.filter((tab) => this.permissionValidator.hasPermission(tab.permission));
807
- }, ...(ngDevMode ? [{ debugName: "visibleTabs" }] : []));
808
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LocalizationContainerPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
809
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: LocalizationContainerPage, isStandalone: true, selector: "lib-localization-container", ngImport: i0, template: `
810
- <div class="p-2 sm:p-4">
811
- <div class="mb-4">
812
- <h1 class="text-xl sm:text-2xl font-bold m-0">{{ 'localization.title' | translate }}</h1>
813
- <p class="text-muted-color mt-1 text-sm sm:text-base">{{ 'localization.subtitle' | translate }}</p>
814
- </div>
815
-
816
- <div class="scrollbar-hide flex gap-1 mb-4 border-b border-surface overflow-x-auto flex-nowrap">
817
- @for (tab of visibleTabs(); track tab.id) {
818
- <a
819
- [routerLink]="tab.route"
820
- routerLinkActive="tab-active"
821
- class="flex items-center gap-2 px-4 py-2 rounded-t-lg cursor-pointer transition-colors text-muted-color no-underline border-b-2 border-transparent -mb-px hover:bg-surface-hover [&.tab-active]:text-primary [&.tab-active]:border-primary [&.tab-active]:bg-surface-ground whitespace-nowrap shrink-0"
822
- >
823
- <i [class]="tab.icon"></i>
824
- <span>{{ tab.labelKey | translate }}</span>
825
- </a>
826
- }
827
- </div>
828
-
829
- <router-outlet />
830
- </div>
831
- `, isInline: true, styles: [".scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}.scrollbar-hide::-webkit-scrollbar{display:none}\n"], dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
832
- }
833
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LocalizationContainerPage, decorators: [{
834
- type: Component,
835
- args: [{ selector: 'lib-localization-container', imports: [RouterOutlet, RouterLink, RouterLinkActive, TranslatePipe], template: `
836
- <div class="p-2 sm:p-4">
837
- <div class="mb-4">
838
- <h1 class="text-xl sm:text-2xl font-bold m-0">{{ 'localization.title' | translate }}</h1>
839
- <p class="text-muted-color mt-1 text-sm sm:text-base">{{ 'localization.subtitle' | translate }}</p>
840
- </div>
841
-
842
- <div class="scrollbar-hide flex gap-1 mb-4 border-b border-surface overflow-x-auto flex-nowrap">
843
- @for (tab of visibleTabs(); track tab.id) {
844
- <a
845
- [routerLink]="tab.route"
846
- routerLinkActive="tab-active"
847
- class="flex items-center gap-2 px-4 py-2 rounded-t-lg cursor-pointer transition-colors text-muted-color no-underline border-b-2 border-transparent -mb-px hover:bg-surface-hover [&.tab-active]:text-primary [&.tab-active]:border-primary [&.tab-active]:bg-surface-ground whitespace-nowrap shrink-0"
848
- >
849
- <i [class]="tab.icon"></i>
850
- <span>{{ tab.labelKey | translate }}</span>
851
- </a>
852
- }
853
- </div>
854
-
855
- <router-outlet />
856
- </div>
857
- `, styles: [".scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}.scrollbar-hide::-webkit-scrollbar{display:none}\n"] }]
858
- }] });
859
-
860
- var localizationContainer_page = /*#__PURE__*/Object.freeze({
861
- __proto__: null,
862
- LocalizationContainerPage: LocalizationContainerPage
863
- });
864
-
865
- class LanguageListPage {
866
- LANGUAGE_PERMISSIONS = LANGUAGE_PERMISSIONS;
867
- router = inject(Router);
868
- languageService = inject(LanguageApiService);
869
- confirmationService = inject(ConfirmationService);
870
- messageService = inject(MessageService);
871
- translateService = inject(TranslateService);
872
- destroyRef = inject(DestroyRef);
873
- ngOnInit() {
874
- this.languageService.fetchList('', {
875
- pagination: { currentPage: 0, pageSize: 100 },
876
- filter: {},
877
- sort: { serial: 'ASC', name: 'ASC' },
773
+ ], ...(ngDevMode ? [{ debugName: "directionOptions" }] : []));
774
+ formModel = signal({
775
+ code: '',
776
+ name: '',
777
+ nativeName: '',
778
+ direction: 'ltr',
779
+ serial: '',
780
+ isActive: true,
781
+ isDefault: false,
782
+ }, ...(ngDevMode ? [{ debugName: "formModel" }] : []));
783
+ formTree = form(this.formModel, (f) => {
784
+ required(f.code, {
785
+ message: this.translate('localization.validation.code.required'),
786
+ });
787
+ maxLength(f.code, 10, {
788
+ message: this.translate('localization.validation.code.max.length'),
789
+ });
790
+ required(f.name, {
791
+ message: this.translate('localization.validation.name.required'),
792
+ });
793
+ maxLength(f.name, 100, {
794
+ message: this.translate('localization.validation.name.max.length'),
795
+ });
796
+ });
797
+ isFormValid = computed(() => {
798
+ const model = this.formModel();
799
+ return model.code.trim().length > 0 && model.name.trim().length > 0;
800
+ }, ...(ngDevMode ? [{ debugName: "isFormValid" }] : []));
801
+ constructor() {
802
+ effect(() => {
803
+ const params = this.routeParams();
804
+ if (!params || this.loadTriggered)
805
+ return;
806
+ this.loadTriggered = true;
807
+ const id = params.get('id');
808
+ if (id && id !== 'new') {
809
+ this.loadLanguage(id);
810
+ }
878
811
  });
879
812
  }
880
- onCreate() {
881
- this.router.navigate(['/localization/languages/new']);
882
- }
883
- onEdit(id) {
884
- this.router.navigate(['/localization/languages', id]);
813
+ updateDirection(value) {
814
+ this.formModel.update((m) => ({ ...m, direction: value }));
885
815
  }
886
- onSetDefault(lang) {
887
- this.confirmationService.confirm({
888
- message: this.translateService.translate('localization.language.confirm.set.default', { name: lang.name }),
889
- header: this.translateService.translate('localization.language.set.default'),
890
- icon: 'pi pi-star',
891
- accept: () => {
892
- this.languageService
893
- .setDefault(lang.id)
894
- .pipe(takeUntilDestroyed(this.destroyRef))
895
- .subscribe({
896
- next: (response) => {
897
- if (response.success) {
898
- this.messageService.add({
899
- severity: 'success',
900
- summary: this.translateService.translate('shared.success'),
901
- detail: response.messageKey
902
- ? this.translateService.translate(response.messageKey, response.messageVariables)
903
- : (response.message ?? ''),
904
- });
905
- this.languageService.reload();
906
- }
907
- },
908
- });
816
+ loadLanguage(id) {
817
+ this.isSaving.set(true);
818
+ from(this.languageService.findById(id))
819
+ .pipe(takeUntilDestroyed(this.destroyRef), finalize(() => this.isSaving.set(false)))
820
+ .subscribe({
821
+ next: (response) => {
822
+ if (response.success && response.data) {
823
+ this.existingLanguage.set(response.data);
824
+ this.formModel.set(this.toFormModel(response.data));
825
+ }
909
826
  },
827
+ error: () => this.router.navigate(['/localization/languages']),
910
828
  });
911
829
  }
912
- onDelete(lang) {
913
- this.confirmationService.confirm({
914
- message: this.translateService.translate('shared.confirm.delete'),
915
- header: this.translateService.translate('localization.language.delete.title'),
916
- icon: 'pi pi-exclamation-triangle',
917
- acceptButtonStyleClass: 'p-button-danger',
918
- accept: () => {
919
- from(this.languageService.delete({ id: lang.id, type: 'delete' }))
920
- .pipe(takeUntilDestroyed(this.destroyRef))
921
- .subscribe({
922
- next: (response) => {
923
- if (response.success) {
924
- this.messageService.add({
925
- severity: 'success',
926
- summary: this.translateService.translate('shared.success'),
927
- detail: response.messageKey
928
- ? this.translateService.translate(response.messageKey, response.messageVariables)
929
- : (response.message ?? ''),
930
- });
931
- this.languageService.reload();
932
- }
933
- },
934
- });
830
+ toFormModel(language) {
831
+ return {
832
+ code: language.code,
833
+ name: language.name,
834
+ nativeName: language.nativeName ?? '',
835
+ direction: language.direction ?? 'ltr',
836
+ serial: language.serial?.toString() ?? '',
837
+ isActive: language.isActive,
838
+ isDefault: language.isDefault,
839
+ };
840
+ }
841
+ onSubmit() {
842
+ if (!this.isFormValid())
843
+ return;
844
+ this.isSaving.set(true);
845
+ const model = this.formModel();
846
+ const dto = {
847
+ code: model.code,
848
+ name: model.name,
849
+ nativeName: model.nativeName || undefined,
850
+ direction: model.direction,
851
+ serial: model.serial ? parseInt(model.serial, 10) : undefined,
852
+ isActive: model.isActive,
853
+ isDefault: model.isDefault,
854
+ };
855
+ const operation$ = this.isEditMode()
856
+ ? from(this.languageService.update({
857
+ id: this.existingLanguage().id,
858
+ ...dto,
859
+ }))
860
+ : from(this.languageService.insert(dto));
861
+ operation$
862
+ .pipe(takeUntilDestroyed(this.destroyRef), finalize(() => this.isSaving.set(false)))
863
+ .subscribe({
864
+ next: (response) => {
865
+ if (response.success) {
866
+ this.messageService.add({
867
+ severity: 'success',
868
+ summary: this.translate('shared.success'),
869
+ detail: response.messageKey
870
+ ? this.translate(response.messageKey, response.messageVariables)
871
+ : (response.message ?? ''),
872
+ });
873
+ this.router.navigate(['/localization/languages']);
874
+ }
935
875
  },
936
876
  });
937
877
  }
938
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LanguageListPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
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: `
940
- <div class="card">
941
- <div
942
- class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4"
943
- >
944
- <div>
945
- <h3 class="text-lg sm:text-xl font-semibold m-0">
946
- {{ 'localization.language.title' | translate }}
947
- </h3>
948
- <p class="text-sm text-muted-color mt-1">
949
- {{ 'localization.language.subtitle' | translate }}
950
- </p>
878
+ onCancel() {
879
+ this.router.navigate(['/localization/languages']);
880
+ }
881
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LanguageFormPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
882
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.5", type: LanguageFormPage, isStandalone: true, selector: "lib-language-form-page", providers: [MessageService], ngImport: i0, template: `
883
+ <p-card>
884
+ <ng-template #header>
885
+ <div class="flex justify-between items-center p-4">
886
+ <h2 class="text-xl font-semibold m-0">
887
+ {{
888
+ isEditMode()
889
+ ? ('localization.language.edit' | translate)
890
+ : ('localization.language.new' | translate)
891
+ }}
892
+ </h2>
951
893
  </div>
952
- <p-button
953
- *hasPermission="LANGUAGE_PERMISSIONS.CREATE"
954
- [label]="'localization.language.new' | translate"
955
- icon="pi pi-plus"
956
- (onClick)="onCreate()"
957
- styleClass="w-full sm:w-auto"
958
- />
959
- </div>
894
+ </ng-template>
960
895
 
961
- <div class="overflow-x-auto -mx-4 sm:mx-0">
962
- <p-table
963
- [value]="languageService.data()"
964
- [loading]="languageService.isLoading()"
965
- styleClass="p-datatable-sm"
966
- [tableStyle]="{ 'min-width': '40rem' }"
967
- >
968
- <ng-template #header>
969
- <tr>
970
- <th>{{ 'localization.language.code' | translate }}</th>
971
- <th>{{ 'localization.language.name' | translate }}</th>
972
- <th>{{ 'localization.language.native.name' | translate }}</th>
973
- <th>{{ 'localization.language.direction' | translate }}</th>
974
- <th>{{ 'shared.status' | translate }}</th>
975
- <th class="w-[150px]">{{ 'shared.actions' | translate }}</th>
976
- </tr>
977
- </ng-template>
896
+ <form
897
+ (ngSubmit)="onSubmit()"
898
+ class="grid grid-cols-1 md:grid-cols-2 gap-4"
899
+ >
900
+ <!-- Language Code -->
901
+ <div class="flex flex-col gap-2">
902
+ <label for="code" class="font-medium"
903
+ >{{ 'localization.language.code' | translate }} *</label
904
+ >
905
+ <input
906
+ pInputText
907
+ id="code"
908
+ [formField]="formTree.code"
909
+ [placeholder]="'localization.placeholder.code' | translate"
910
+ />
911
+ <small class="text-muted-color">{{
912
+ 'localization.language.iso.code.hint' | translate
913
+ }}</small>
914
+ </div>
978
915
 
979
- <ng-template #body let-lang>
980
- <tr>
981
- <td>
982
- <span class="uppercase font-semibold">{{ lang.code }}</span>
983
- </td>
984
- <td>{{ lang.name }}</td>
985
- <td>{{ lang.nativeName || ('shared.na' | translate) }}</td>
986
- <td>
987
- <p-tag
988
- [value]="
989
- lang.direction === 'rtl'
990
- ? ('localization.language.rtl' | translate)
991
- : ('localization.language.ltr' | translate)
992
- "
993
- [severity]="lang.direction === 'rtl' ? 'warn' : 'info'"
994
- />
995
- </td>
996
- <td>
997
- <div class="flex gap-1 items-center">
998
- <p-tag
999
- [value]="
1000
- lang.isActive
1001
- ? ('shared.active' | translate)
1002
- : ('shared.inactive' | translate)
1003
- "
1004
- [severity]="lang.isActive ? 'success' : 'secondary'"
1005
- />
1006
- @if (lang.isDefault) {
1007
- <p-tag
1008
- [value]="'localization.language.default' | translate"
1009
- severity="contrast"
1010
- />
1011
- }
1012
- </div>
1013
- </td>
1014
- <td>
1015
- <div class="flex gap-1">
1016
- @if (!lang.isDefault) {
1017
- <p-button
1018
- *hasPermission="LANGUAGE_PERMISSIONS.UPDATE"
1019
- icon="pi pi-star"
1020
- [text]="true"
1021
- size="small"
1022
- [pTooltip]="
1023
- 'localization.language.set.default' | translate
1024
- "
1025
- (onClick)="onSetDefault(lang)"
1026
- />
1027
- }
1028
- <p-button
1029
- *hasPermission="LANGUAGE_PERMISSIONS.UPDATE"
1030
- icon="pi pi-pencil"
1031
- [text]="true"
1032
- severity="secondary"
1033
- size="small"
1034
- [pTooltip]="'shared.edit' | translate"
1035
- (onClick)="onEdit(lang.id)"
1036
- />
1037
- <p-button
1038
- *hasPermission="LANGUAGE_PERMISSIONS.DELETE"
1039
- icon="pi pi-trash"
1040
- [text]="true"
1041
- severity="danger"
1042
- size="small"
1043
- [pTooltip]="'shared.delete' | translate"
1044
- [disabled]="lang.isDefault"
1045
- (onClick)="onDelete(lang)"
1046
- />
1047
- </div>
1048
- </td>
1049
- </tr>
1050
- </ng-template>
916
+ <!-- Language Name -->
917
+ <div class="flex flex-col gap-2">
918
+ <label for="name" class="font-medium"
919
+ >{{ 'localization.language.name' | translate }} *</label
920
+ >
921
+ <input
922
+ pInputText
923
+ id="name"
924
+ [formField]="formTree.name"
925
+ [placeholder]="'localization.placeholder.name' | translate"
926
+ />
927
+ </div>
1051
928
 
1052
- <ng-template #emptymessage>
1053
- <tr>
1054
- <td colspan="6" class="text-center py-4 text-muted-color">
1055
- {{ 'localization.language.empty' | translate }}
1056
- </td>
1057
- </tr>
1058
- </ng-template>
1059
- </p-table>
1060
- </div>
1061
- </div>
929
+ <!-- Native Name -->
930
+ <div class="flex flex-col gap-2">
931
+ <label for="nativeName" class="font-medium">{{
932
+ 'localization.language.native.name' | translate
933
+ }}</label>
934
+ <input
935
+ pInputText
936
+ id="nativeName"
937
+ [formField]="formTree.nativeName"
938
+ [placeholder]="'localization.placeholder.native.name' | translate"
939
+ />
940
+ <small class="text-muted-color">{{
941
+ 'localization.language.native.name.hint' | translate
942
+ }}</small>
943
+ </div>
944
+
945
+ <!-- Text Direction -->
946
+ <div class="flex flex-col gap-2">
947
+ <label for="direction" class="font-medium"
948
+ >{{ 'localization.language.direction' | translate }} *</label
949
+ >
950
+ <p-select
951
+ id="direction"
952
+ [ngModel]="formModel().direction"
953
+ (ngModelChange)="updateDirection($event)"
954
+ [ngModelOptions]="{ standalone: true }"
955
+ [options]="directionOptions()"
956
+ optionLabel="label"
957
+ optionValue="value"
958
+ class="w-full"
959
+ />
960
+ </div>
961
+
962
+ <!-- Display Order -->
963
+ <div class="flex flex-col gap-2">
964
+ <label for="serial" class="font-medium">{{
965
+ 'localization.language.display.order' | translate
966
+ }}</label>
967
+ <input
968
+ pInputText
969
+ id="serial"
970
+ type="number"
971
+ [formField]="formTree.serial"
972
+ [placeholder]="'shared.display.order.placeholder' | translate"
973
+ />
974
+ </div>
975
+
976
+ <!-- Empty cell for alignment -->
977
+ <div class="hidden md:block"></div>
978
+
979
+ <!-- Checkboxes -->
980
+ <div class="flex items-center gap-6 md:col-span-2 pt-4">
981
+ <label class="p-checkbox-native">
982
+ <input type="checkbox" [formField]="formTree.isActive" />
983
+ <span>{{ 'shared.active' | translate }}</span>
984
+ </label>
985
+ <label class="p-checkbox-native">
986
+ <input type="checkbox" [formField]="formTree.isDefault" />
987
+ <span>{{ 'localization.language.default' | translate }}</span>
988
+ </label>
989
+ </div>
990
+
991
+ <!-- Actions -->
992
+ <div class="flex justify-end gap-4 md:col-span-2 pt-4">
993
+ <p-button
994
+ [label]="'shared.cancel' | translate"
995
+ severity="secondary"
996
+ [outlined]="true"
997
+ (onClick)="onCancel()"
998
+ />
999
+ <p-button
1000
+ [label]="
1001
+ isEditMode()
1002
+ ? ('shared.update' | translate)
1003
+ : ('shared.create' | translate)
1004
+ "
1005
+ type="submit"
1006
+ [loading]="isSaving()"
1007
+ [disabled]="!isFormValid() || isSaving()"
1008
+ />
1009
+ </div>
1010
+ </form>
1011
+ </p-card>
1062
1012
 
1063
- <p-confirmDialog />
1064
1013
  <p-toast />
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" }] });
1014
+ `, 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.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: i6.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" }] });
1066
1015
  }
1067
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LanguageListPage, decorators: [{
1016
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LanguageFormPage, decorators: [{
1068
1017
  type: Component,
1069
1018
  args: [{
1070
- selector: 'lib-language-list-page',
1071
- imports: [AngularModule, PrimeModule, HasPermissionDirective, TranslatePipe],
1072
- providers: [ConfirmationService, MessageService],
1019
+ selector: 'lib-language-form-page',
1020
+ imports: [AngularModule, PrimeModule, FormsModule, FormField, TranslatePipe],
1021
+ providers: [MessageService],
1073
1022
  template: `
1074
- <div class="card">
1075
- <div
1076
- class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4"
1077
- >
1078
- <div>
1079
- <h3 class="text-lg sm:text-xl font-semibold m-0">
1080
- {{ 'localization.language.title' | translate }}
1081
- </h3>
1082
- <p class="text-sm text-muted-color mt-1">
1083
- {{ 'localization.language.subtitle' | translate }}
1084
- </p>
1023
+ <p-card>
1024
+ <ng-template #header>
1025
+ <div class="flex justify-between items-center p-4">
1026
+ <h2 class="text-xl font-semibold m-0">
1027
+ {{
1028
+ isEditMode()
1029
+ ? ('localization.language.edit' | translate)
1030
+ : ('localization.language.new' | translate)
1031
+ }}
1032
+ </h2>
1085
1033
  </div>
1086
- <p-button
1087
- *hasPermission="LANGUAGE_PERMISSIONS.CREATE"
1088
- [label]="'localization.language.new' | translate"
1089
- icon="pi pi-plus"
1090
- (onClick)="onCreate()"
1034
+ </ng-template>
1035
+
1036
+ <form
1037
+ (ngSubmit)="onSubmit()"
1038
+ class="grid grid-cols-1 md:grid-cols-2 gap-4"
1039
+ >
1040
+ <!-- Language Code -->
1041
+ <div class="flex flex-col gap-2">
1042
+ <label for="code" class="font-medium"
1043
+ >{{ 'localization.language.code' | translate }} *</label
1044
+ >
1045
+ <input
1046
+ pInputText
1047
+ id="code"
1048
+ [formField]="formTree.code"
1049
+ [placeholder]="'localization.placeholder.code' | translate"
1050
+ />
1051
+ <small class="text-muted-color">{{
1052
+ 'localization.language.iso.code.hint' | translate
1053
+ }}</small>
1054
+ </div>
1055
+
1056
+ <!-- Language Name -->
1057
+ <div class="flex flex-col gap-2">
1058
+ <label for="name" class="font-medium"
1059
+ >{{ 'localization.language.name' | translate }} *</label
1060
+ >
1061
+ <input
1062
+ pInputText
1063
+ id="name"
1064
+ [formField]="formTree.name"
1065
+ [placeholder]="'localization.placeholder.name' | translate"
1066
+ />
1067
+ </div>
1068
+
1069
+ <!-- Native Name -->
1070
+ <div class="flex flex-col gap-2">
1071
+ <label for="nativeName" class="font-medium">{{
1072
+ 'localization.language.native.name' | translate
1073
+ }}</label>
1074
+ <input
1075
+ pInputText
1076
+ id="nativeName"
1077
+ [formField]="formTree.nativeName"
1078
+ [placeholder]="'localization.placeholder.native.name' | translate"
1079
+ />
1080
+ <small class="text-muted-color">{{
1081
+ 'localization.language.native.name.hint' | translate
1082
+ }}</small>
1083
+ </div>
1084
+
1085
+ <!-- Text Direction -->
1086
+ <div class="flex flex-col gap-2">
1087
+ <label for="direction" class="font-medium"
1088
+ >{{ 'localization.language.direction' | translate }} *</label
1089
+ >
1090
+ <p-select
1091
+ id="direction"
1092
+ [ngModel]="formModel().direction"
1093
+ (ngModelChange)="updateDirection($event)"
1094
+ [ngModelOptions]="{ standalone: true }"
1095
+ [options]="directionOptions()"
1096
+ optionLabel="label"
1097
+ optionValue="value"
1098
+ class="w-full"
1099
+ />
1100
+ </div>
1101
+
1102
+ <!-- Display Order -->
1103
+ <div class="flex flex-col gap-2">
1104
+ <label for="serial" class="font-medium">{{
1105
+ 'localization.language.display.order' | translate
1106
+ }}</label>
1107
+ <input
1108
+ pInputText
1109
+ id="serial"
1110
+ type="number"
1111
+ [formField]="formTree.serial"
1112
+ [placeholder]="'shared.display.order.placeholder' | translate"
1113
+ />
1114
+ </div>
1115
+
1116
+ <!-- Empty cell for alignment -->
1117
+ <div class="hidden md:block"></div>
1118
+
1119
+ <!-- Checkboxes -->
1120
+ <div class="flex items-center gap-6 md:col-span-2 pt-4">
1121
+ <label class="p-checkbox-native">
1122
+ <input type="checkbox" [formField]="formTree.isActive" />
1123
+ <span>{{ 'shared.active' | translate }}</span>
1124
+ </label>
1125
+ <label class="p-checkbox-native">
1126
+ <input type="checkbox" [formField]="formTree.isDefault" />
1127
+ <span>{{ 'localization.language.default' | translate }}</span>
1128
+ </label>
1129
+ </div>
1130
+
1131
+ <!-- Actions -->
1132
+ <div class="flex justify-end gap-4 md:col-span-2 pt-4">
1133
+ <p-button
1134
+ [label]="'shared.cancel' | translate"
1135
+ severity="secondary"
1136
+ [outlined]="true"
1137
+ (onClick)="onCancel()"
1138
+ />
1139
+ <p-button
1140
+ [label]="
1141
+ isEditMode()
1142
+ ? ('shared.update' | translate)
1143
+ : ('shared.create' | translate)
1144
+ "
1145
+ type="submit"
1146
+ [loading]="isSaving()"
1147
+ [disabled]="!isFormValid() || isSaving()"
1148
+ />
1149
+ </div>
1150
+ </form>
1151
+ </p-card>
1152
+
1153
+ <p-toast />
1154
+ `,
1155
+ }]
1156
+ }], ctorParameters: () => [] });
1157
+
1158
+ var languageForm_page = /*#__PURE__*/Object.freeze({
1159
+ __proto__: null,
1160
+ LanguageFormPage: LanguageFormPage
1161
+ });
1162
+
1163
+ class LanguageListPage {
1164
+ LANGUAGE_PERMISSIONS = LANGUAGE_PERMISSIONS;
1165
+ router = inject(Router);
1166
+ languageService = inject(LanguageApiService);
1167
+ confirmationService = inject(ConfirmationService);
1168
+ messageService = inject(MessageService);
1169
+ translateService = inject(TranslateService);
1170
+ destroyRef = inject(DestroyRef);
1171
+ ngOnInit() {
1172
+ this.languageService.fetchList('', {
1173
+ pagination: { currentPage: 0, pageSize: 100 },
1174
+ filter: {},
1175
+ sort: { serial: 'ASC', name: 'ASC' },
1176
+ });
1177
+ }
1178
+ onCreate() {
1179
+ this.router.navigate(['/localization/languages/new']);
1180
+ }
1181
+ onEdit(id) {
1182
+ this.router.navigate(['/localization/languages', id]);
1183
+ }
1184
+ onSetDefault(lang) {
1185
+ this.confirmationService.confirm({
1186
+ message: this.translateService.translate('localization.language.confirm.set.default', { name: lang.name }),
1187
+ header: this.translateService.translate('localization.language.set.default'),
1188
+ icon: 'pi pi-star',
1189
+ accept: () => {
1190
+ this.languageService
1191
+ .setDefault(lang.id)
1192
+ .pipe(takeUntilDestroyed(this.destroyRef))
1193
+ .subscribe({
1194
+ next: (response) => {
1195
+ if (response.success) {
1196
+ this.messageService.add({
1197
+ severity: 'success',
1198
+ summary: this.translateService.translate('shared.success'),
1199
+ detail: response.messageKey
1200
+ ? this.translateService.translate(response.messageKey, response.messageVariables)
1201
+ : (response.message ?? ''),
1202
+ });
1203
+ this.languageService.reload();
1204
+ }
1205
+ },
1206
+ });
1207
+ },
1208
+ });
1209
+ }
1210
+ onDelete(lang) {
1211
+ this.confirmationService.confirm({
1212
+ message: this.translateService.translate('shared.confirm.delete'),
1213
+ header: this.translateService.translate('localization.language.delete.title'),
1214
+ icon: 'pi pi-exclamation-triangle',
1215
+ acceptButtonStyleClass: 'p-button-danger',
1216
+ accept: () => {
1217
+ from(this.languageService.delete({ id: lang.id, type: 'delete' }))
1218
+ .pipe(takeUntilDestroyed(this.destroyRef))
1219
+ .subscribe({
1220
+ next: (response) => {
1221
+ if (response.success) {
1222
+ this.messageService.add({
1223
+ severity: 'success',
1224
+ summary: this.translateService.translate('shared.success'),
1225
+ detail: response.messageKey
1226
+ ? this.translateService.translate(response.messageKey, response.messageVariables)
1227
+ : (response.message ?? ''),
1228
+ });
1229
+ this.languageService.reload();
1230
+ }
1231
+ },
1232
+ });
1233
+ },
1234
+ });
1235
+ }
1236
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LanguageListPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
1237
+ 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: `
1238
+ <div class="card">
1239
+ <div
1240
+ class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4"
1241
+ >
1242
+ <div>
1243
+ <h3 class="text-lg sm:text-xl font-semibold m-0">
1244
+ {{ 'localization.language.title' | translate }}
1245
+ </h3>
1246
+ <p class="text-sm text-muted-color mt-1">
1247
+ {{ 'localization.language.subtitle' | translate }}
1248
+ </p>
1249
+ </div>
1250
+ <p-button
1251
+ *hasPermission="LANGUAGE_PERMISSIONS.CREATE"
1252
+ [label]="'localization.language.new' | translate"
1253
+ icon="pi pi-plus"
1254
+ (onClick)="onCreate()"
1091
1255
  styleClass="w-full sm:w-auto"
1092
1256
  />
1093
1257
  </div>
@@ -1196,854 +1360,270 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
1196
1360
 
1197
1361
  <p-confirmDialog />
1198
1362
  <p-toast />
1199
- `,
1200
- }]
1201
- }] });
1363
+ `, 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$1.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$1.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "component", type: i6.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$1.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" }] });
1364
+ }
1365
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LanguageListPage, decorators: [{
1366
+ type: Component,
1367
+ args: [{
1368
+ selector: 'lib-language-list-page',
1369
+ imports: [AngularModule, PrimeModule, HasPermissionDirective, TranslatePipe],
1370
+ providers: [ConfirmationService, MessageService],
1371
+ template: `
1372
+ <div class="card">
1373
+ <div
1374
+ class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4"
1375
+ >
1376
+ <div>
1377
+ <h3 class="text-lg sm:text-xl font-semibold m-0">
1378
+ {{ 'localization.language.title' | translate }}
1379
+ </h3>
1380
+ <p class="text-sm text-muted-color mt-1">
1381
+ {{ 'localization.language.subtitle' | translate }}
1382
+ </p>
1383
+ </div>
1384
+ <p-button
1385
+ *hasPermission="LANGUAGE_PERMISSIONS.CREATE"
1386
+ [label]="'localization.language.new' | translate"
1387
+ icon="pi pi-plus"
1388
+ (onClick)="onCreate()"
1389
+ styleClass="w-full sm:w-auto"
1390
+ />
1391
+ </div>
1202
1392
 
1203
- var languageList_page = /*#__PURE__*/Object.freeze({
1204
- __proto__: null,
1205
- LanguageListPage: LanguageListPage
1206
- });
1207
-
1208
- class LanguageFormPage {
1209
- router = inject(Router);
1210
- route = inject(ActivatedRoute);
1211
- languageService = inject(LanguageApiService);
1212
- messageService = inject(MessageService);
1213
- translateAdapter = inject(TRANSLATE_ADAPTER, {
1214
- optional: true,
1215
- });
1216
- destroyRef = inject(DestroyRef);
1217
- routeParams = toSignal(this.route.paramMap);
1218
- loadTriggered = false;
1219
- isSaving = signal(false, ...(ngDevMode ? [{ debugName: "isSaving" }] : []));
1220
- existingLanguage = signal(null, ...(ngDevMode ? [{ debugName: "existingLanguage" }] : []));
1221
- isEditMode = computed(() => !!this.existingLanguage(), ...(ngDevMode ? [{ debugName: "isEditMode" }] : []));
1222
- translate(key, vars) {
1223
- return this.translateAdapter?.translate(key, vars) ?? key;
1224
- }
1225
- directionOptions = computed(() => [
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
- },
1234
- ], ...(ngDevMode ? [{ debugName: "directionOptions" }] : []));
1235
- formModel = signal({
1236
- code: '',
1237
- name: '',
1238
- nativeName: '',
1239
- direction: 'ltr',
1240
- serial: '',
1241
- isActive: true,
1242
- isDefault: false,
1243
- }, ...(ngDevMode ? [{ debugName: "formModel" }] : []));
1244
- formTree = form(this.formModel, (f) => {
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
- });
1257
- });
1258
- isFormValid = computed(() => {
1259
- const model = this.formModel();
1260
- return model.code.trim().length > 0 && model.name.trim().length > 0;
1261
- }, ...(ngDevMode ? [{ debugName: "isFormValid" }] : []));
1262
- constructor() {
1263
- effect(() => {
1264
- const params = this.routeParams();
1265
- if (!params || this.loadTriggered)
1266
- return;
1267
- this.loadTriggered = true;
1268
- const id = params.get('id');
1269
- if (id && id !== 'new') {
1270
- this.loadLanguage(id);
1271
- }
1272
- });
1273
- }
1274
- updateDirection(value) {
1275
- this.formModel.update((m) => ({ ...m, direction: value }));
1276
- }
1277
- loadLanguage(id) {
1278
- this.isSaving.set(true);
1279
- from(this.languageService.findById(id))
1280
- .pipe(takeUntilDestroyed(this.destroyRef), finalize(() => this.isSaving.set(false)))
1281
- .subscribe({
1282
- next: (response) => {
1283
- if (response.success && response.data) {
1284
- this.existingLanguage.set(response.data);
1285
- this.formModel.set(this.toFormModel(response.data));
1286
- }
1287
- },
1288
- error: () => this.router.navigate(['/localization/languages']),
1289
- });
1290
- }
1291
- toFormModel(language) {
1292
- return {
1293
- code: language.code,
1294
- name: language.name,
1295
- nativeName: language.nativeName ?? '',
1296
- direction: language.direction ?? 'ltr',
1297
- serial: language.serial?.toString() ?? '',
1298
- isActive: language.isActive,
1299
- isDefault: language.isDefault,
1300
- };
1301
- }
1302
- onSubmit() {
1303
- if (!this.isFormValid())
1304
- return;
1305
- this.isSaving.set(true);
1306
- const model = this.formModel();
1307
- const dto = {
1308
- code: model.code,
1309
- name: model.name,
1310
- nativeName: model.nativeName || undefined,
1311
- direction: model.direction,
1312
- serial: model.serial ? parseInt(model.serial, 10) : undefined,
1313
- isActive: model.isActive,
1314
- isDefault: model.isDefault,
1315
- };
1316
- const operation$ = this.isEditMode()
1317
- ? from(this.languageService.update({
1318
- id: this.existingLanguage().id,
1319
- ...dto,
1320
- }))
1321
- : from(this.languageService.insert(dto));
1322
- operation$
1323
- .pipe(takeUntilDestroyed(this.destroyRef), finalize(() => this.isSaving.set(false)))
1324
- .subscribe({
1325
- next: (response) => {
1326
- if (response.success) {
1327
- this.messageService.add({
1328
- severity: 'success',
1329
- summary: this.translate('shared.success'),
1330
- detail: response.messageKey
1331
- ? this.translate(response.messageKey, response.messageVariables)
1332
- : (response.message ?? ''),
1333
- });
1334
- this.router.navigate(['/localization/languages']);
1335
- }
1336
- },
1337
- });
1338
- }
1339
- onCancel() {
1340
- this.router.navigate(['/localization/languages']);
1341
- }
1342
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LanguageFormPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
1343
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.5", type: LanguageFormPage, isStandalone: true, selector: "lib-language-form-page", providers: [MessageService], ngImport: i0, template: `
1344
- <p-card>
1345
- <ng-template #header>
1346
- <div class="flex justify-between items-center p-4">
1347
- <h2 class="text-xl font-semibold m-0">
1348
- {{
1349
- isEditMode()
1350
- ? ('localization.language.edit' | translate)
1351
- : ('localization.language.new' | translate)
1352
- }}
1353
- </h2>
1354
- </div>
1355
- </ng-template>
1356
-
1357
- <form
1358
- (ngSubmit)="onSubmit()"
1359
- class="grid grid-cols-1 md:grid-cols-2 gap-4"
1360
- >
1361
- <!-- Language Code -->
1362
- <div class="flex flex-col gap-2">
1363
- <label for="code" class="font-medium"
1364
- >{{ 'localization.language.code' | translate }} *</label
1365
- >
1366
- <input
1367
- pInputText
1368
- id="code"
1369
- [formField]="formTree.code"
1370
- [placeholder]="'localization.placeholder.code' | translate"
1371
- />
1372
- <small class="text-muted-color">{{
1373
- 'localization.language.iso.code.hint' | translate
1374
- }}</small>
1375
- </div>
1376
-
1377
- <!-- Language Name -->
1378
- <div class="flex flex-col gap-2">
1379
- <label for="name" class="font-medium"
1380
- >{{ 'localization.language.name' | translate }} *</label
1381
- >
1382
- <input
1383
- pInputText
1384
- id="name"
1385
- [formField]="formTree.name"
1386
- [placeholder]="'localization.placeholder.name' | translate"
1387
- />
1388
- </div>
1389
-
1390
- <!-- Native Name -->
1391
- <div class="flex flex-col gap-2">
1392
- <label for="nativeName" class="font-medium">{{
1393
- 'localization.language.native.name' | translate
1394
- }}</label>
1395
- <input
1396
- pInputText
1397
- id="nativeName"
1398
- [formField]="formTree.nativeName"
1399
- [placeholder]="'localization.placeholder.native.name' | translate"
1400
- />
1401
- <small class="text-muted-color">{{
1402
- 'localization.language.native.name.hint' | translate
1403
- }}</small>
1404
- </div>
1405
-
1406
- <!-- Text Direction -->
1407
- <div class="flex flex-col gap-2">
1408
- <label for="direction" class="font-medium"
1409
- >{{ 'localization.language.direction' | translate }} *</label
1410
- >
1411
- <p-select
1412
- id="direction"
1413
- [ngModel]="formModel().direction"
1414
- (ngModelChange)="updateDirection($event)"
1415
- [ngModelOptions]="{ standalone: true }"
1416
- [options]="directionOptions()"
1417
- optionLabel="label"
1418
- optionValue="value"
1419
- class="w-full"
1420
- />
1421
- </div>
1422
-
1423
- <!-- Display Order -->
1424
- <div class="flex flex-col gap-2">
1425
- <label for="serial" class="font-medium">{{
1426
- 'localization.language.display.order' | translate
1427
- }}</label>
1428
- <input
1429
- pInputText
1430
- id="serial"
1431
- type="number"
1432
- [formField]="formTree.serial"
1433
- [placeholder]="'shared.display.order.placeholder' | translate"
1434
- />
1435
- </div>
1436
-
1437
- <!-- Empty cell for alignment -->
1438
- <div class="hidden md:block"></div>
1439
-
1440
- <!-- Checkboxes -->
1441
- <div class="flex items-center gap-6 md:col-span-2 pt-4">
1442
- <label class="p-checkbox-native">
1443
- <input type="checkbox" [formField]="formTree.isActive" />
1444
- <span>{{ 'shared.active' | translate }}</span>
1445
- </label>
1446
- <label class="p-checkbox-native">
1447
- <input type="checkbox" [formField]="formTree.isDefault" />
1448
- <span>{{ 'localization.language.default' | translate }}</span>
1449
- </label>
1450
- </div>
1451
-
1452
- <!-- Actions -->
1453
- <div class="flex justify-end gap-4 md:col-span-2 pt-4">
1454
- <p-button
1455
- [label]="'shared.cancel' | translate"
1456
- severity="secondary"
1457
- [outlined]="true"
1458
- (onClick)="onCancel()"
1459
- />
1460
- <p-button
1461
- [label]="
1462
- isEditMode()
1463
- ? ('shared.update' | translate)
1464
- : ('shared.create' | translate)
1465
- "
1466
- type="submit"
1467
- [loading]="isSaving()"
1468
- [disabled]="!isFormValid() || isSaving()"
1469
- />
1470
- </div>
1471
- </form>
1472
- </p-card>
1473
-
1474
- <p-toast />
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" }] });
1476
- }
1477
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LanguageFormPage, decorators: [{
1478
- type: Component,
1479
- args: [{
1480
- selector: 'lib-language-form-page',
1481
- imports: [AngularModule, PrimeModule, FormsModule, FormField, TranslatePipe],
1482
- providers: [MessageService],
1483
- template: `
1484
- <p-card>
1485
- <ng-template #header>
1486
- <div class="flex justify-between items-center p-4">
1487
- <h2 class="text-xl font-semibold m-0">
1488
- {{
1489
- isEditMode()
1490
- ? ('localization.language.edit' | translate)
1491
- : ('localization.language.new' | translate)
1492
- }}
1493
- </h2>
1494
- </div>
1495
- </ng-template>
1496
-
1497
- <form
1498
- (ngSubmit)="onSubmit()"
1499
- class="grid grid-cols-1 md:grid-cols-2 gap-4"
1500
- >
1501
- <!-- Language Code -->
1502
- <div class="flex flex-col gap-2">
1503
- <label for="code" class="font-medium"
1504
- >{{ 'localization.language.code' | translate }} *</label
1505
- >
1506
- <input
1507
- pInputText
1508
- id="code"
1509
- [formField]="formTree.code"
1510
- [placeholder]="'localization.placeholder.code' | translate"
1511
- />
1512
- <small class="text-muted-color">{{
1513
- 'localization.language.iso.code.hint' | translate
1514
- }}</small>
1515
- </div>
1516
-
1517
- <!-- Language Name -->
1518
- <div class="flex flex-col gap-2">
1519
- <label for="name" class="font-medium"
1520
- >{{ 'localization.language.name' | translate }} *</label
1521
- >
1522
- <input
1523
- pInputText
1524
- id="name"
1525
- [formField]="formTree.name"
1526
- [placeholder]="'localization.placeholder.name' | translate"
1527
- />
1528
- </div>
1529
-
1530
- <!-- Native Name -->
1531
- <div class="flex flex-col gap-2">
1532
- <label for="nativeName" class="font-medium">{{
1533
- 'localization.language.native.name' | translate
1534
- }}</label>
1535
- <input
1536
- pInputText
1537
- id="nativeName"
1538
- [formField]="formTree.nativeName"
1539
- [placeholder]="'localization.placeholder.native.name' | translate"
1540
- />
1541
- <small class="text-muted-color">{{
1542
- 'localization.language.native.name.hint' | translate
1543
- }}</small>
1544
- </div>
1545
-
1546
- <!-- Text Direction -->
1547
- <div class="flex flex-col gap-2">
1548
- <label for="direction" class="font-medium"
1549
- >{{ 'localization.language.direction' | translate }} *</label
1550
- >
1551
- <p-select
1552
- id="direction"
1553
- [ngModel]="formModel().direction"
1554
- (ngModelChange)="updateDirection($event)"
1555
- [ngModelOptions]="{ standalone: true }"
1556
- [options]="directionOptions()"
1557
- optionLabel="label"
1558
- optionValue="value"
1559
- class="w-full"
1560
- />
1561
- </div>
1562
-
1563
- <!-- Display Order -->
1564
- <div class="flex flex-col gap-2">
1565
- <label for="serial" class="font-medium">{{
1566
- 'localization.language.display.order' | translate
1567
- }}</label>
1568
- <input
1569
- pInputText
1570
- id="serial"
1571
- type="number"
1572
- [formField]="formTree.serial"
1573
- [placeholder]="'shared.display.order.placeholder' | translate"
1574
- />
1575
- </div>
1576
-
1577
- <!-- Empty cell for alignment -->
1578
- <div class="hidden md:block"></div>
1579
-
1580
- <!-- Checkboxes -->
1581
- <div class="flex items-center gap-6 md:col-span-2 pt-4">
1582
- <label class="p-checkbox-native">
1583
- <input type="checkbox" [formField]="formTree.isActive" />
1584
- <span>{{ 'shared.active' | translate }}</span>
1585
- </label>
1586
- <label class="p-checkbox-native">
1587
- <input type="checkbox" [formField]="formTree.isDefault" />
1588
- <span>{{ 'localization.language.default' | translate }}</span>
1589
- </label>
1590
- </div>
1591
-
1592
- <!-- Actions -->
1593
- <div class="flex justify-end gap-4 md:col-span-2 pt-4">
1594
- <p-button
1595
- [label]="'shared.cancel' | translate"
1596
- severity="secondary"
1597
- [outlined]="true"
1598
- (onClick)="onCancel()"
1599
- />
1600
- <p-button
1601
- [label]="
1602
- isEditMode()
1603
- ? ('shared.update' | translate)
1604
- : ('shared.create' | translate)
1605
- "
1606
- type="submit"
1607
- [loading]="isSaving()"
1608
- [disabled]="!isFormValid() || isSaving()"
1609
- />
1610
- </div>
1611
- </form>
1612
- </p-card>
1613
-
1614
- <p-toast />
1615
- `,
1616
- }]
1617
- }], ctorParameters: () => [] });
1618
-
1619
- var languageForm_page = /*#__PURE__*/Object.freeze({
1620
- __proto__: null,
1621
- LanguageFormPage: LanguageFormPage
1622
- });
1623
-
1624
- class TranslationKeyListPage {
1625
- TRANSLATION_KEY_PERMISSIONS = TRANSLATION_KEY_PERMISSIONS;
1626
- router = inject(Router);
1627
- languageService = inject(LanguageApiService);
1628
- translationKeyService = inject(TranslationKeyApiService);
1629
- translationService = inject(TranslationApiService);
1630
- confirmationService = inject(ConfirmationService);
1631
- messageService = inject(MessageService);
1632
- translateService = inject(TranslateService);
1633
- destroyRef = inject(DestroyRef);
1634
- isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
1635
- languages = signal([], ...(ngDevMode ? [{ debugName: "languages" }] : []));
1636
- modules = signal([], ...(ngDevMode ? [{ debugName: "modules" }] : []));
1637
- translationKeys = signal([], ...(ngDevMode ? [{ debugName: "translationKeys" }] : []));
1638
- translationCounts = signal(new Map(), ...(ngDevMode ? [{ debugName: "translationCounts" }] : []));
1639
- selectedModule = signal(null, ...(ngDevMode ? [{ debugName: "selectedModule" }] : []));
1640
- searchTerm = signal('', ...(ngDevMode ? [{ debugName: "searchTerm" }] : []));
1641
- moduleOptions = computed(() => [
1642
- { label: this.translateService.translate('shared.all'), value: null },
1643
- ...this.modules().map((m) => ({ label: m, value: m })),
1644
- ], ...(ngDevMode ? [{ debugName: "moduleOptions" }] : []));
1645
- filteredKeys = computed(() => {
1646
- let keys = this.translationKeys();
1647
- const module = this.selectedModule();
1648
- const search = this.searchTerm().toLowerCase().trim();
1649
- if (module) {
1650
- keys = keys.filter((k) => k.module === module);
1651
- }
1652
- if (search) {
1653
- keys = keys.filter((k) => k.key.toLowerCase().includes(search) ||
1654
- k.defaultMessage.toLowerCase().includes(search) ||
1655
- k.module.toLowerCase().includes(search));
1656
- }
1657
- return keys;
1658
- }, ...(ngDevMode ? [{ debugName: "filteredKeys" }] : []));
1659
- ngOnInit() {
1660
- this.loadData();
1661
- }
1662
- loadData() {
1663
- this.isLoading.set(true);
1664
- forkJoin({
1665
- languages: from(this.languageService.getAll({
1666
- pagination: { currentPage: 0, pageSize: 100 },
1667
- filter: {},
1668
- sort: { serial: 'ASC', name: 'ASC' },
1669
- })),
1670
- modules: this.translationKeyService.getModules(),
1671
- keys: from(this.translationKeyService.getAll({
1672
- pagination: { currentPage: 0, pageSize: 1000 },
1673
- filter: {},
1674
- sort: { module: 'ASC', key: 'ASC' },
1675
- })),
1676
- })
1677
- .pipe(takeUntilDestroyed(this.destroyRef))
1678
- .subscribe({
1679
- next: ({ languages, modules, keys }) => {
1680
- if (languages.success && languages.data) {
1681
- this.languages.set(languages.data.filter((l) => l.isActive));
1682
- }
1683
- if (modules.success && modules.data) {
1684
- this.modules.set(modules.data);
1685
- }
1686
- if (keys.success && keys.data) {
1687
- this.translationKeys.set(keys.data);
1688
- this.loadTranslationCounts();
1689
- }
1690
- this.isLoading.set(false);
1691
- },
1692
- error: () => {
1693
- this.isLoading.set(false);
1694
- },
1695
- });
1696
- }
1697
- loadTranslationCounts() {
1698
- this.translationService
1699
- .getCounts()
1700
- .pipe(takeUntilDestroyed(this.destroyRef))
1701
- .subscribe({
1702
- next: (response) => {
1703
- if (response.success && response.data) {
1704
- this.translationCounts.set(new Map(Object.entries(response.data)));
1705
- }
1706
- },
1707
- });
1708
- }
1709
- getTranslationCount(keyId) {
1710
- return this.translationCounts().get(keyId) ?? 0;
1711
- }
1712
- onModuleChange(value) {
1713
- this.selectedModule.set(value);
1714
- }
1715
- onCreate() {
1716
- this.router.navigate(['/localization/keys/new']);
1717
- }
1718
- onEdit(key) {
1719
- this.router.navigate(['/localization/keys', key.id]);
1720
- }
1721
- onDelete(key) {
1722
- this.confirmationService.confirm({
1723
- message: this.translateService.translate('shared.confirm.delete'),
1724
- header: this.translateService.translate('localization.key.delete.title'),
1725
- icon: 'pi pi-exclamation-triangle',
1726
- acceptButtonStyleClass: 'p-button-danger',
1727
- accept: () => {
1728
- from(this.translationKeyService.delete({ id: key.id, type: 'delete' }))
1729
- .pipe(takeUntilDestroyed(this.destroyRef))
1730
- .subscribe({
1731
- next: (response) => {
1732
- if (response.success) {
1733
- this.messageService.add({
1734
- severity: 'success',
1735
- summary: this.translateService.translate('shared.success'),
1736
- detail: response.messageKey
1737
- ? this.translateService.translate(response.messageKey, response.messageVariables)
1738
- : (response.message ?? ''),
1739
- });
1740
- this.loadData();
1741
- }
1742
- },
1743
- });
1744
- },
1745
- });
1746
- }
1747
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationKeyListPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
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: `
1749
- <div class="card">
1750
- <div
1751
- class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4"
1752
- >
1753
- <div>
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>
1760
- </div>
1761
- <div class="flex gap-2">
1762
- <p-button
1763
- *hasPermission="TRANSLATION_KEY_PERMISSIONS.CREATE"
1764
- [label]="'localization.key.new' | translate"
1765
- icon="pi pi-plus"
1766
- (onClick)="onCreate()"
1767
- />
1768
- </div>
1769
- </div>
1770
-
1771
- <div class="flex flex-col md:flex-row gap-4 mb-4">
1772
- <div class="flex-1">
1773
- <p-select
1774
- [options]="moduleOptions()"
1775
- [ngModel]="selectedModule()"
1776
- (ngModelChange)="onModuleChange($event)"
1777
- [ngModelOptions]="{ standalone: true }"
1778
- optionLabel="label"
1779
- optionValue="value"
1780
- [placeholder]="'localization.key.filter.module' | translate"
1781
- [showClear]="true"
1782
- class="w-full"
1783
- />
1784
- </div>
1785
- <div class="flex-1">
1786
- <p-iconfield class="w-full">
1787
- <p-inputicon class="pi pi-search" />
1788
- <input
1789
- pInputText
1790
- [ngModel]="searchTerm()"
1791
- (ngModelChange)="searchTerm.set($event)"
1792
- [ngModelOptions]="{ standalone: true }"
1793
- [placeholder]="'shared.search' | translate"
1794
- class="w-full"
1795
- />
1796
- </p-iconfield>
1797
- </div>
1798
- </div>
1393
+ <div class="overflow-x-auto -mx-4 sm:mx-0">
1394
+ <p-table
1395
+ [value]="languageService.data()"
1396
+ [loading]="languageService.isLoading()"
1397
+ styleClass="p-datatable-sm"
1398
+ [tableStyle]="{ 'min-width': '40rem' }"
1399
+ >
1400
+ <ng-template #header>
1401
+ <tr>
1402
+ <th>{{ 'localization.language.code' | translate }}</th>
1403
+ <th>{{ 'localization.language.name' | translate }}</th>
1404
+ <th>{{ 'localization.language.native.name' | translate }}</th>
1405
+ <th>{{ 'localization.language.direction' | translate }}</th>
1406
+ <th>{{ 'shared.status' | translate }}</th>
1407
+ <th class="w-[150px]">{{ 'shared.actions' | translate }}</th>
1408
+ </tr>
1409
+ </ng-template>
1799
1410
 
1800
- <!-- Table -->
1801
- <p-table
1802
- [value]="filteredKeys()"
1803
- [loading]="isLoading()"
1804
- [paginator]="true"
1805
- [rows]="15"
1806
- [rowsPerPageOptions]="[10, 15, 25, 50]"
1807
- class="p-datatable-sm"
1808
- >
1809
- <ng-template #header>
1810
- <tr>
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>
1823
- <th style="width: 15%">{{ 'shared.actions' | translate }}</th>
1824
- </tr>
1825
- </ng-template>
1826
- <ng-template #body let-key>
1827
- @let translationCount = getTranslationCount(key.id);
1828
- @let languageCount = languages().length;
1829
- <tr>
1830
- <td>
1831
- <p-tag [value]="key.module" severity="secondary" />
1832
- </td>
1833
- <td>
1834
- <div class="font-medium">{{ key.key }}</div>
1835
- @if (key.description) {
1836
- <small class="text-muted-color">{{ key.description }}</small>
1837
- }
1838
- </td>
1839
- <td>{{ key.defaultMessage }}</td>
1840
- <td class="text-center">
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 }}
1848
- </span>
1849
- </td>
1850
- <td>
1851
- <div class="flex gap-1">
1852
- <p-button
1853
- *hasPermission="TRANSLATION_KEY_PERMISSIONS.UPDATE"
1854
- icon="pi pi-pencil"
1855
- [text]="true"
1856
- severity="secondary"
1857
- size="small"
1858
- [pTooltip]="'shared.edit' | translate"
1859
- (onClick)="onEdit(key)"
1860
- />
1861
- <p-button
1862
- *hasPermission="TRANSLATION_KEY_PERMISSIONS.DELETE"
1863
- icon="pi pi-trash"
1864
- [text]="true"
1865
- severity="danger"
1866
- size="small"
1867
- [pTooltip]="'shared.delete' | translate"
1868
- (onClick)="onDelete(key)"
1411
+ <ng-template #body let-lang>
1412
+ <tr>
1413
+ <td>
1414
+ <span class="uppercase font-semibold">{{ lang.code }}</span>
1415
+ </td>
1416
+ <td>{{ lang.name }}</td>
1417
+ <td>{{ lang.nativeName || ('shared.na' | translate) }}</td>
1418
+ <td>
1419
+ <p-tag
1420
+ [value]="
1421
+ lang.direction === 'rtl'
1422
+ ? ('localization.language.rtl' | translate)
1423
+ : ('localization.language.ltr' | translate)
1424
+ "
1425
+ [severity]="lang.direction === 'rtl' ? 'warn' : 'info'"
1869
1426
  />
1870
- </div>
1871
- </td>
1872
- </tr>
1873
- </ng-template>
1874
- <ng-template #emptymessage>
1875
- <tr>
1876
- <td colspan="5" class="text-center py-4 text-muted-color">
1877
- {{ 'localization.key.empty' | translate }}
1878
- </td>
1879
- </tr>
1880
- </ng-template>
1881
- </p-table>
1427
+ </td>
1428
+ <td>
1429
+ <div class="flex gap-1 items-center">
1430
+ <p-tag
1431
+ [value]="
1432
+ lang.isActive
1433
+ ? ('shared.active' | translate)
1434
+ : ('shared.inactive' | translate)
1435
+ "
1436
+ [severity]="lang.isActive ? 'success' : 'secondary'"
1437
+ />
1438
+ @if (lang.isDefault) {
1439
+ <p-tag
1440
+ [value]="'localization.language.default' | translate"
1441
+ severity="contrast"
1442
+ />
1443
+ }
1444
+ </div>
1445
+ </td>
1446
+ <td>
1447
+ <div class="flex gap-1">
1448
+ @if (!lang.isDefault) {
1449
+ <p-button
1450
+ *hasPermission="LANGUAGE_PERMISSIONS.UPDATE"
1451
+ icon="pi pi-star"
1452
+ [text]="true"
1453
+ size="small"
1454
+ [pTooltip]="
1455
+ 'localization.language.set.default' | translate
1456
+ "
1457
+ (onClick)="onSetDefault(lang)"
1458
+ />
1459
+ }
1460
+ <p-button
1461
+ *hasPermission="LANGUAGE_PERMISSIONS.UPDATE"
1462
+ icon="pi pi-pencil"
1463
+ [text]="true"
1464
+ severity="secondary"
1465
+ size="small"
1466
+ [pTooltip]="'shared.edit' | translate"
1467
+ (onClick)="onEdit(lang.id)"
1468
+ />
1469
+ <p-button
1470
+ *hasPermission="LANGUAGE_PERMISSIONS.DELETE"
1471
+ icon="pi pi-trash"
1472
+ [text]="true"
1473
+ severity="danger"
1474
+ size="small"
1475
+ [pTooltip]="'shared.delete' | translate"
1476
+ [disabled]="lang.isDefault"
1477
+ (onClick)="onDelete(lang)"
1478
+ />
1479
+ </div>
1480
+ </td>
1481
+ </tr>
1482
+ </ng-template>
1483
+
1484
+ <ng-template #emptymessage>
1485
+ <tr>
1486
+ <td colspan="6" class="text-center py-4 text-muted-color">
1487
+ {{ 'localization.language.empty' | translate }}
1488
+ </td>
1489
+ </tr>
1490
+ </ng-template>
1491
+ </p-table>
1492
+ </div>
1882
1493
  </div>
1883
1494
 
1884
1495
  <p-confirmDialog />
1885
1496
  <p-toast />
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" }] });
1887
- }
1888
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationKeyListPage, decorators: [{
1889
- type: Component,
1890
- args: [{
1891
- selector: 'lib-translation-key-list-page',
1892
- imports: [
1893
- AngularModule,
1894
- PrimeModule,
1895
- FormsModule,
1896
- HasPermissionDirective,
1897
- TranslatePipe,
1898
- ],
1899
- providers: [ConfirmationService, MessageService],
1900
- template: `
1901
- <div class="card">
1902
- <div
1903
- class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4"
1904
- >
1905
- <div>
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>
1912
- </div>
1913
- <div class="flex gap-2">
1914
- <p-button
1915
- *hasPermission="TRANSLATION_KEY_PERMISSIONS.CREATE"
1916
- [label]="'localization.key.new' | translate"
1917
- icon="pi pi-plus"
1918
- (onClick)="onCreate()"
1919
- />
1920
- </div>
1497
+ `,
1498
+ }]
1499
+ }] });
1500
+
1501
+ var languageList_page = /*#__PURE__*/Object.freeze({
1502
+ __proto__: null,
1503
+ LanguageListPage: LanguageListPage
1504
+ });
1505
+
1506
+ class LocalizationContainerPage {
1507
+ permissionValidator = inject(PermissionValidatorService);
1508
+ allTabs = [
1509
+ {
1510
+ id: 'languages',
1511
+ labelKey: 'localization.tabs.languages',
1512
+ icon: 'pi pi-globe',
1513
+ route: 'languages',
1514
+ permission: LANGUAGE_PERMISSIONS.READ,
1515
+ },
1516
+ {
1517
+ id: 'keys',
1518
+ labelKey: 'localization.tabs.keys',
1519
+ icon: 'pi pi-key',
1520
+ route: 'keys',
1521
+ permission: TRANSLATION_KEY_PERMISSIONS.READ,
1522
+ },
1523
+ ];
1524
+ visibleTabs = computed(() => {
1525
+ // Explicitly track permissions signal for reactivity
1526
+ const permissions = this.permissionValidator.permissions();
1527
+ if (!permissions.length)
1528
+ return [];
1529
+ return this.allTabs.filter((tab) => this.permissionValidator.hasPermission(tab.permission));
1530
+ }, ...(ngDevMode ? [{ debugName: "visibleTabs" }] : []));
1531
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LocalizationContainerPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
1532
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: LocalizationContainerPage, isStandalone: true, selector: "lib-localization-container", ngImport: i0, template: `
1533
+ <div class="p-2 sm:p-4">
1534
+ <div class="mb-4">
1535
+ <h1 class="text-xl sm:text-2xl font-bold m-0">{{ 'localization.title' | translate }}</h1>
1536
+ <p class="text-muted-color mt-1 text-sm sm:text-base">{{ 'localization.subtitle' | translate }}</p>
1921
1537
  </div>
1922
1538
 
1923
- <div class="flex flex-col md:flex-row gap-4 mb-4">
1924
- <div class="flex-1">
1925
- <p-select
1926
- [options]="moduleOptions()"
1927
- [ngModel]="selectedModule()"
1928
- (ngModelChange)="onModuleChange($event)"
1929
- [ngModelOptions]="{ standalone: true }"
1930
- optionLabel="label"
1931
- optionValue="value"
1932
- [placeholder]="'localization.key.filter.module' | translate"
1933
- [showClear]="true"
1934
- class="w-full"
1935
- />
1936
- </div>
1937
- <div class="flex-1">
1938
- <p-iconfield class="w-full">
1939
- <p-inputicon class="pi pi-search" />
1940
- <input
1941
- pInputText
1942
- [ngModel]="searchTerm()"
1943
- (ngModelChange)="searchTerm.set($event)"
1944
- [ngModelOptions]="{ standalone: true }"
1945
- [placeholder]="'shared.search' | translate"
1946
- class="w-full"
1947
- />
1948
- </p-iconfield>
1949
- </div>
1539
+ <div class="scrollbar-hide flex gap-1 mb-4 border-b border-surface overflow-x-auto flex-nowrap">
1540
+ @for (tab of visibleTabs(); track tab.id) {
1541
+ <a
1542
+ [routerLink]="tab.route"
1543
+ routerLinkActive="tab-active"
1544
+ class="flex items-center gap-2 px-4 py-2 rounded-t-lg cursor-pointer transition-colors text-muted-color no-underline border-b-2 border-transparent -mb-px hover:bg-surface-hover [&.tab-active]:text-primary [&.tab-active]:border-primary [&.tab-active]:bg-surface-ground whitespace-nowrap shrink-0"
1545
+ >
1546
+ <i [class]="tab.icon"></i>
1547
+ <span>{{ tab.labelKey | translate }}</span>
1548
+ </a>
1549
+ }
1950
1550
  </div>
1951
1551
 
1952
- <!-- Table -->
1953
- <p-table
1954
- [value]="filteredKeys()"
1955
- [loading]="isLoading()"
1956
- [paginator]="true"
1957
- [rows]="15"
1958
- [rowsPerPageOptions]="[10, 15, 25, 50]"
1959
- class="p-datatable-sm"
1960
- >
1961
- <ng-template #header>
1962
- <tr>
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>
1975
- <th style="width: 15%">{{ 'shared.actions' | translate }}</th>
1976
- </tr>
1977
- </ng-template>
1978
- <ng-template #body let-key>
1979
- @let translationCount = getTranslationCount(key.id);
1980
- @let languageCount = languages().length;
1981
- <tr>
1982
- <td>
1983
- <p-tag [value]="key.module" severity="secondary" />
1984
- </td>
1985
- <td>
1986
- <div class="font-medium">{{ key.key }}</div>
1987
- @if (key.description) {
1988
- <small class="text-muted-color">{{ key.description }}</small>
1989
- }
1990
- </td>
1991
- <td>{{ key.defaultMessage }}</td>
1992
- <td class="text-center">
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 }}
2000
- </span>
2001
- </td>
2002
- <td>
2003
- <div class="flex gap-1">
2004
- <p-button
2005
- *hasPermission="TRANSLATION_KEY_PERMISSIONS.UPDATE"
2006
- icon="pi pi-pencil"
2007
- [text]="true"
2008
- severity="secondary"
2009
- size="small"
2010
- [pTooltip]="'shared.edit' | translate"
2011
- (onClick)="onEdit(key)"
2012
- />
2013
- <p-button
2014
- *hasPermission="TRANSLATION_KEY_PERMISSIONS.DELETE"
2015
- icon="pi pi-trash"
2016
- [text]="true"
2017
- severity="danger"
2018
- size="small"
2019
- [pTooltip]="'shared.delete' | translate"
2020
- (onClick)="onDelete(key)"
2021
- />
2022
- </div>
2023
- </td>
2024
- </tr>
2025
- </ng-template>
2026
- <ng-template #emptymessage>
2027
- <tr>
2028
- <td colspan="5" class="text-center py-4 text-muted-color">
2029
- {{ 'localization.key.empty' | translate }}
2030
- </td>
2031
- </tr>
2032
- </ng-template>
2033
- </p-table>
1552
+ <router-outlet />
2034
1553
  </div>
1554
+ `, isInline: true, styles: [".scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}.scrollbar-hide::-webkit-scrollbar{display:none}\n"], dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
1555
+ }
1556
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LocalizationContainerPage, decorators: [{
1557
+ type: Component,
1558
+ args: [{ selector: 'lib-localization-container', imports: [RouterOutlet, RouterLink, RouterLinkActive, TranslatePipe], template: `
1559
+ <div class="p-2 sm:p-4">
1560
+ <div class="mb-4">
1561
+ <h1 class="text-xl sm:text-2xl font-bold m-0">{{ 'localization.title' | translate }}</h1>
1562
+ <p class="text-muted-color mt-1 text-sm sm:text-base">{{ 'localization.subtitle' | translate }}</p>
1563
+ </div>
1564
+
1565
+ <div class="scrollbar-hide flex gap-1 mb-4 border-b border-surface overflow-x-auto flex-nowrap">
1566
+ @for (tab of visibleTabs(); track tab.id) {
1567
+ <a
1568
+ [routerLink]="tab.route"
1569
+ routerLinkActive="tab-active"
1570
+ class="flex items-center gap-2 px-4 py-2 rounded-t-lg cursor-pointer transition-colors text-muted-color no-underline border-b-2 border-transparent -mb-px hover:bg-surface-hover [&.tab-active]:text-primary [&.tab-active]:border-primary [&.tab-active]:bg-surface-ground whitespace-nowrap shrink-0"
1571
+ >
1572
+ <i [class]="tab.icon"></i>
1573
+ <span>{{ tab.labelKey | translate }}</span>
1574
+ </a>
1575
+ }
1576
+ </div>
2035
1577
 
2036
- <p-confirmDialog />
2037
- <p-toast />
2038
- `,
2039
- }]
1578
+ <router-outlet />
1579
+ </div>
1580
+ `, styles: [".scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}.scrollbar-hide::-webkit-scrollbar{display:none}\n"] }]
2040
1581
  }] });
2041
1582
 
2042
- var translationKeyList_page = /*#__PURE__*/Object.freeze({
1583
+ var localizationContainer_page = /*#__PURE__*/Object.freeze({
2043
1584
  __proto__: null,
2044
- TranslationKeyListPage: TranslationKeyListPage
1585
+ LocalizationContainerPage: LocalizationContainerPage
2045
1586
  });
2046
1587
 
1588
+ const LOCALIZATION_ROUTES = [
1589
+ {
1590
+ path: '',
1591
+ resolve: { translations: resolveTranslationModule({ modules: ['localization'], fallbackMessages: { ...LOCALIZATION_MESSAGES, ...SHARED_MESSAGES } }) },
1592
+ loadComponent: () => Promise.resolve().then(function () { return localizationContainer_page; }).then((m) => m.LocalizationContainerPage),
1593
+ children: [
1594
+ {
1595
+ path: 'languages',
1596
+ loadComponent: () => Promise.resolve().then(function () { return languageList_page; }).then((m) => m.LanguageListPage),
1597
+ },
1598
+ {
1599
+ path: 'languages/new',
1600
+ loadComponent: () => Promise.resolve().then(function () { return languageForm_page; }).then((m) => m.LanguageFormPage),
1601
+ },
1602
+ {
1603
+ path: 'languages/:id',
1604
+ loadComponent: () => Promise.resolve().then(function () { return languageForm_page; }).then((m) => m.LanguageFormPage),
1605
+ },
1606
+ {
1607
+ path: 'keys',
1608
+ loadComponent: () => Promise.resolve().then(function () { return translationKeyList_page; }).then((m) => m.TranslationKeyListPage),
1609
+ },
1610
+ {
1611
+ path: 'keys/new',
1612
+ loadComponent: () => Promise.resolve().then(function () { return translationKeyForm_page; }).then((m) => m.TranslationKeyFormPage),
1613
+ },
1614
+ {
1615
+ path: 'keys/:id',
1616
+ loadComponent: () => Promise.resolve().then(function () { return translationKeyForm_page; }).then((m) => m.TranslationKeyFormPage),
1617
+ },
1618
+ {
1619
+ path: '',
1620
+ redirectTo: 'languages',
1621
+ pathMatch: 'full',
1622
+ },
1623
+ ],
1624
+ },
1625
+ ];
1626
+
2047
1627
  class TranslationKeyFormPage {
2048
1628
  router = inject(Router);
2049
1629
  route = inject(ActivatedRoute);
@@ -2051,7 +1631,7 @@ class TranslationKeyFormPage {
2051
1631
  translationKeyService = inject(TranslationKeyApiService);
2052
1632
  translationService = inject(TranslationApiService);
2053
1633
  messageService = inject(MessageService);
2054
- translateAdapter = inject(TRANSLATE_ADAPTER, {
1634
+ translateAdapter = inject(TranslateService, {
2055
1635
  optional: true,
2056
1636
  });
2057
1637
  destroyRef = inject(DestroyRef);
@@ -2474,7 +2054,7 @@ class TranslationKeyFormPage {
2474
2054
  </p-card>
2475
2055
 
2476
2056
  <p-toast />
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" }] });
2057
+ `, 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.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$1.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: i6.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" }] });
2478
2058
  }
2479
2059
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationKeyFormPage, decorators: [{
2480
2060
  type: Component,
@@ -2635,36 +2215,463 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImpor
2635
2215
  }
2636
2216
  </div>
2637
2217
 
2638
- <!-- Actions -->
2639
- <div class="flex justify-end gap-4 pt-4">
2218
+ <!-- Actions -->
2219
+ <div class="flex justify-end gap-4 pt-4">
2220
+ <p-button
2221
+ [label]="'shared.cancel' | translate"
2222
+ severity="secondary"
2223
+ [outlined]="true"
2224
+ (onClick)="onCancel()"
2225
+ />
2226
+ <p-button
2227
+ [label]="
2228
+ isEditMode()
2229
+ ? ('shared.update' | translate)
2230
+ : ('shared.create' | translate)
2231
+ "
2232
+ type="submit"
2233
+ [loading]="isSaving()"
2234
+ [disabled]="!isFormValid() || isSaving()"
2235
+ />
2236
+ </div>
2237
+ </form>
2238
+ </p-card>
2239
+
2240
+ <p-toast />
2241
+ `,
2242
+ }]
2243
+ }], ctorParameters: () => [] });
2244
+
2245
+ var translationKeyForm_page = /*#__PURE__*/Object.freeze({
2246
+ __proto__: null,
2247
+ TranslationKeyFormPage: TranslationKeyFormPage
2248
+ });
2249
+
2250
+ class TranslationKeyListPage {
2251
+ TRANSLATION_KEY_PERMISSIONS = TRANSLATION_KEY_PERMISSIONS;
2252
+ router = inject(Router);
2253
+ languageService = inject(LanguageApiService);
2254
+ translationKeyService = inject(TranslationKeyApiService);
2255
+ translationService = inject(TranslationApiService);
2256
+ confirmationService = inject(ConfirmationService);
2257
+ messageService = inject(MessageService);
2258
+ translateService = inject(TranslateService);
2259
+ destroyRef = inject(DestroyRef);
2260
+ isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
2261
+ languages = signal([], ...(ngDevMode ? [{ debugName: "languages" }] : []));
2262
+ modules = signal([], ...(ngDevMode ? [{ debugName: "modules" }] : []));
2263
+ translationKeys = signal([], ...(ngDevMode ? [{ debugName: "translationKeys" }] : []));
2264
+ translationCounts = signal(new Map(), ...(ngDevMode ? [{ debugName: "translationCounts" }] : []));
2265
+ selectedModule = signal(null, ...(ngDevMode ? [{ debugName: "selectedModule" }] : []));
2266
+ searchTerm = signal('', ...(ngDevMode ? [{ debugName: "searchTerm" }] : []));
2267
+ moduleOptions = computed(() => [
2268
+ { label: this.translateService.translate('shared.all'), value: null },
2269
+ ...this.modules().map((m) => ({ label: m, value: m })),
2270
+ ], ...(ngDevMode ? [{ debugName: "moduleOptions" }] : []));
2271
+ filteredKeys = computed(() => {
2272
+ let keys = this.translationKeys();
2273
+ const module = this.selectedModule();
2274
+ const search = this.searchTerm().toLowerCase().trim();
2275
+ if (module) {
2276
+ keys = keys.filter((k) => k.module === module);
2277
+ }
2278
+ if (search) {
2279
+ keys = keys.filter((k) => k.key.toLowerCase().includes(search) ||
2280
+ k.defaultMessage.toLowerCase().includes(search) ||
2281
+ k.module.toLowerCase().includes(search));
2282
+ }
2283
+ return keys;
2284
+ }, ...(ngDevMode ? [{ debugName: "filteredKeys" }] : []));
2285
+ ngOnInit() {
2286
+ this.loadData();
2287
+ }
2288
+ loadData() {
2289
+ this.isLoading.set(true);
2290
+ forkJoin({
2291
+ languages: from(this.languageService.getAll({
2292
+ pagination: { currentPage: 0, pageSize: 100 },
2293
+ filter: {},
2294
+ sort: { serial: 'ASC', name: 'ASC' },
2295
+ })),
2296
+ modules: this.translationKeyService.getModules(),
2297
+ keys: from(this.translationKeyService.getAll({
2298
+ pagination: { currentPage: 0, pageSize: 1000 },
2299
+ filter: {},
2300
+ sort: { module: 'ASC', key: 'ASC' },
2301
+ })),
2302
+ })
2303
+ .pipe(takeUntilDestroyed(this.destroyRef))
2304
+ .subscribe({
2305
+ next: ({ languages, modules, keys }) => {
2306
+ if (languages.success && languages.data) {
2307
+ this.languages.set(languages.data.filter((l) => l.isActive));
2308
+ }
2309
+ if (modules.success && modules.data) {
2310
+ this.modules.set(modules.data);
2311
+ }
2312
+ if (keys.success && keys.data) {
2313
+ this.translationKeys.set(keys.data);
2314
+ this.loadTranslationCounts();
2315
+ }
2316
+ this.isLoading.set(false);
2317
+ },
2318
+ error: () => {
2319
+ this.isLoading.set(false);
2320
+ },
2321
+ });
2322
+ }
2323
+ loadTranslationCounts() {
2324
+ this.translationService
2325
+ .getCounts()
2326
+ .pipe(takeUntilDestroyed(this.destroyRef))
2327
+ .subscribe({
2328
+ next: (response) => {
2329
+ if (response.success && response.data) {
2330
+ this.translationCounts.set(new Map(Object.entries(response.data)));
2331
+ }
2332
+ },
2333
+ });
2334
+ }
2335
+ getTranslationCount(keyId) {
2336
+ return this.translationCounts().get(keyId) ?? 0;
2337
+ }
2338
+ onModuleChange(value) {
2339
+ this.selectedModule.set(value);
2340
+ }
2341
+ onCreate() {
2342
+ this.router.navigate(['/localization/keys/new']);
2343
+ }
2344
+ onEdit(key) {
2345
+ this.router.navigate(['/localization/keys', key.id]);
2346
+ }
2347
+ onDelete(key) {
2348
+ this.confirmationService.confirm({
2349
+ message: this.translateService.translate('shared.confirm.delete'),
2350
+ header: this.translateService.translate('localization.key.delete.title'),
2351
+ icon: 'pi pi-exclamation-triangle',
2352
+ acceptButtonStyleClass: 'p-button-danger',
2353
+ accept: () => {
2354
+ from(this.translationKeyService.delete({ id: key.id, type: 'delete' }))
2355
+ .pipe(takeUntilDestroyed(this.destroyRef))
2356
+ .subscribe({
2357
+ next: (response) => {
2358
+ if (response.success) {
2359
+ this.messageService.add({
2360
+ severity: 'success',
2361
+ summary: this.translateService.translate('shared.success'),
2362
+ detail: response.messageKey
2363
+ ? this.translateService.translate(response.messageKey, response.messageVariables)
2364
+ : (response.message ?? ''),
2365
+ });
2366
+ this.loadData();
2367
+ }
2368
+ },
2369
+ });
2370
+ },
2371
+ });
2372
+ }
2373
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationKeyListPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
2374
+ 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: `
2375
+ <div class="card">
2376
+ <div
2377
+ class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4"
2378
+ >
2379
+ <div>
2380
+ <h3 class="text-lg sm:text-xl font-semibold m-0">
2381
+ {{ 'localization.key.title' | translate }}
2382
+ </h3>
2383
+ <p class="text-sm text-muted-color mt-1">
2384
+ {{ 'localization.key.subtitle' | translate }}
2385
+ </p>
2386
+ </div>
2387
+ <div class="flex gap-2">
2388
+ <p-button
2389
+ *hasPermission="TRANSLATION_KEY_PERMISSIONS.CREATE"
2390
+ [label]="'localization.key.new' | translate"
2391
+ icon="pi pi-plus"
2392
+ (onClick)="onCreate()"
2393
+ />
2394
+ </div>
2395
+ </div>
2396
+
2397
+ <div class="flex flex-col md:flex-row gap-4 mb-4">
2398
+ <div class="flex-1">
2399
+ <p-select
2400
+ [options]="moduleOptions()"
2401
+ [ngModel]="selectedModule()"
2402
+ (ngModelChange)="onModuleChange($event)"
2403
+ [ngModelOptions]="{ standalone: true }"
2404
+ optionLabel="label"
2405
+ optionValue="value"
2406
+ [placeholder]="'localization.key.filter.module' | translate"
2407
+ [showClear]="true"
2408
+ class="w-full"
2409
+ />
2410
+ </div>
2411
+ <div class="flex-1">
2412
+ <p-iconfield class="w-full">
2413
+ <p-inputicon class="pi pi-search" />
2414
+ <input
2415
+ pInputText
2416
+ [ngModel]="searchTerm()"
2417
+ (ngModelChange)="searchTerm.set($event)"
2418
+ [ngModelOptions]="{ standalone: true }"
2419
+ [placeholder]="'shared.search' | translate"
2420
+ class="w-full"
2421
+ />
2422
+ </p-iconfield>
2423
+ </div>
2424
+ </div>
2425
+
2426
+ <!-- Table -->
2427
+ <p-table
2428
+ [value]="filteredKeys()"
2429
+ [loading]="isLoading()"
2430
+ [paginator]="true"
2431
+ [rows]="15"
2432
+ [rowsPerPageOptions]="[10, 15, 25, 50]"
2433
+ class="p-datatable-sm"
2434
+ >
2435
+ <ng-template #header>
2436
+ <tr>
2437
+ <th style="width: 15%">
2438
+ {{ 'localization.key.module' | translate }}
2439
+ </th>
2440
+ <th style="width: 25%">
2441
+ {{ 'localization.key.name' | translate }}
2442
+ </th>
2443
+ <th style="width: 35%">
2444
+ {{ 'localization.key.default.message' | translate }}
2445
+ </th>
2446
+ <th style="width: 10%">
2447
+ {{ 'localization.key.translations' | translate }}
2448
+ </th>
2449
+ <th style="width: 15%">{{ 'shared.actions' | translate }}</th>
2450
+ </tr>
2451
+ </ng-template>
2452
+ <ng-template #body let-key>
2453
+ @let translationCount = getTranslationCount(key.id);
2454
+ @let languageCount = languages().length;
2455
+ <tr>
2456
+ <td>
2457
+ <p-tag [value]="key.module" severity="secondary" />
2458
+ </td>
2459
+ <td>
2460
+ <div class="font-medium">{{ key.key }}</div>
2461
+ @if (key.description) {
2462
+ <small class="text-muted-color">{{ key.description }}</small>
2463
+ }
2464
+ </td>
2465
+ <td>{{ key.defaultMessage }}</td>
2466
+ <td class="text-center">
2467
+ <span
2468
+ class="text-sm"
2469
+ [class.text-green-500]="translationCount === languageCount"
2470
+ [class.text-orange-500]="
2471
+ translationCount < languageCount && translationCount > 0
2472
+ "
2473
+ [class.text-muted-color]="translationCount === 0"
2474
+ >
2475
+ {{ translationCount }}/{{ languageCount }}
2476
+ </span>
2477
+ </td>
2478
+ <td>
2479
+ <div class="flex gap-1">
2480
+ <p-button
2481
+ *hasPermission="TRANSLATION_KEY_PERMISSIONS.UPDATE"
2482
+ icon="pi pi-pencil"
2483
+ [text]="true"
2484
+ severity="secondary"
2485
+ size="small"
2486
+ [pTooltip]="'shared.edit' | translate"
2487
+ (onClick)="onEdit(key)"
2488
+ />
2489
+ <p-button
2490
+ *hasPermission="TRANSLATION_KEY_PERMISSIONS.DELETE"
2491
+ icon="pi pi-trash"
2492
+ [text]="true"
2493
+ severity="danger"
2494
+ size="small"
2495
+ [pTooltip]="'shared.delete' | translate"
2496
+ (onClick)="onDelete(key)"
2497
+ />
2498
+ </div>
2499
+ </td>
2500
+ </tr>
2501
+ </ng-template>
2502
+ <ng-template #emptymessage>
2503
+ <tr>
2504
+ <td colspan="5" class="text-center py-4 text-muted-color">
2505
+ {{ 'localization.key.empty' | translate }}
2506
+ </td>
2507
+ </tr>
2508
+ </ng-template>
2509
+ </p-table>
2510
+ </div>
2511
+
2512
+ <p-confirmDialog />
2513
+ <p-toast />
2514
+ `, 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.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: i3$1.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$1.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "component", type: i6.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$1.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" }] });
2515
+ }
2516
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TranslationKeyListPage, decorators: [{
2517
+ type: Component,
2518
+ args: [{
2519
+ selector: 'lib-translation-key-list-page',
2520
+ imports: [
2521
+ AngularModule,
2522
+ PrimeModule,
2523
+ FormsModule,
2524
+ HasPermissionDirective,
2525
+ TranslatePipe,
2526
+ ],
2527
+ providers: [ConfirmationService, MessageService],
2528
+ template: `
2529
+ <div class="card">
2530
+ <div
2531
+ class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4"
2532
+ >
2533
+ <div>
2534
+ <h3 class="text-lg sm:text-xl font-semibold m-0">
2535
+ {{ 'localization.key.title' | translate }}
2536
+ </h3>
2537
+ <p class="text-sm text-muted-color mt-1">
2538
+ {{ 'localization.key.subtitle' | translate }}
2539
+ </p>
2540
+ </div>
2541
+ <div class="flex gap-2">
2640
2542
  <p-button
2641
- [label]="'shared.cancel' | translate"
2642
- severity="secondary"
2643
- [outlined]="true"
2644
- (onClick)="onCancel()"
2543
+ *hasPermission="TRANSLATION_KEY_PERMISSIONS.CREATE"
2544
+ [label]="'localization.key.new' | translate"
2545
+ icon="pi pi-plus"
2546
+ (onClick)="onCreate()"
2645
2547
  />
2646
- <p-button
2647
- [label]="
2648
- isEditMode()
2649
- ? ('shared.update' | translate)
2650
- : ('shared.create' | translate)
2651
- "
2652
- type="submit"
2653
- [loading]="isSaving()"
2654
- [disabled]="!isFormValid() || isSaving()"
2548
+ </div>
2549
+ </div>
2550
+
2551
+ <div class="flex flex-col md:flex-row gap-4 mb-4">
2552
+ <div class="flex-1">
2553
+ <p-select
2554
+ [options]="moduleOptions()"
2555
+ [ngModel]="selectedModule()"
2556
+ (ngModelChange)="onModuleChange($event)"
2557
+ [ngModelOptions]="{ standalone: true }"
2558
+ optionLabel="label"
2559
+ optionValue="value"
2560
+ [placeholder]="'localization.key.filter.module' | translate"
2561
+ [showClear]="true"
2562
+ class="w-full"
2655
2563
  />
2656
2564
  </div>
2657
- </form>
2658
- </p-card>
2565
+ <div class="flex-1">
2566
+ <p-iconfield class="w-full">
2567
+ <p-inputicon class="pi pi-search" />
2568
+ <input
2569
+ pInputText
2570
+ [ngModel]="searchTerm()"
2571
+ (ngModelChange)="searchTerm.set($event)"
2572
+ [ngModelOptions]="{ standalone: true }"
2573
+ [placeholder]="'shared.search' | translate"
2574
+ class="w-full"
2575
+ />
2576
+ </p-iconfield>
2577
+ </div>
2578
+ </div>
2579
+
2580
+ <!-- Table -->
2581
+ <p-table
2582
+ [value]="filteredKeys()"
2583
+ [loading]="isLoading()"
2584
+ [paginator]="true"
2585
+ [rows]="15"
2586
+ [rowsPerPageOptions]="[10, 15, 25, 50]"
2587
+ class="p-datatable-sm"
2588
+ >
2589
+ <ng-template #header>
2590
+ <tr>
2591
+ <th style="width: 15%">
2592
+ {{ 'localization.key.module' | translate }}
2593
+ </th>
2594
+ <th style="width: 25%">
2595
+ {{ 'localization.key.name' | translate }}
2596
+ </th>
2597
+ <th style="width: 35%">
2598
+ {{ 'localization.key.default.message' | translate }}
2599
+ </th>
2600
+ <th style="width: 10%">
2601
+ {{ 'localization.key.translations' | translate }}
2602
+ </th>
2603
+ <th style="width: 15%">{{ 'shared.actions' | translate }}</th>
2604
+ </tr>
2605
+ </ng-template>
2606
+ <ng-template #body let-key>
2607
+ @let translationCount = getTranslationCount(key.id);
2608
+ @let languageCount = languages().length;
2609
+ <tr>
2610
+ <td>
2611
+ <p-tag [value]="key.module" severity="secondary" />
2612
+ </td>
2613
+ <td>
2614
+ <div class="font-medium">{{ key.key }}</div>
2615
+ @if (key.description) {
2616
+ <small class="text-muted-color">{{ key.description }}</small>
2617
+ }
2618
+ </td>
2619
+ <td>{{ key.defaultMessage }}</td>
2620
+ <td class="text-center">
2621
+ <span
2622
+ class="text-sm"
2623
+ [class.text-green-500]="translationCount === languageCount"
2624
+ [class.text-orange-500]="
2625
+ translationCount < languageCount && translationCount > 0
2626
+ "
2627
+ [class.text-muted-color]="translationCount === 0"
2628
+ >
2629
+ {{ translationCount }}/{{ languageCount }}
2630
+ </span>
2631
+ </td>
2632
+ <td>
2633
+ <div class="flex gap-1">
2634
+ <p-button
2635
+ *hasPermission="TRANSLATION_KEY_PERMISSIONS.UPDATE"
2636
+ icon="pi pi-pencil"
2637
+ [text]="true"
2638
+ severity="secondary"
2639
+ size="small"
2640
+ [pTooltip]="'shared.edit' | translate"
2641
+ (onClick)="onEdit(key)"
2642
+ />
2643
+ <p-button
2644
+ *hasPermission="TRANSLATION_KEY_PERMISSIONS.DELETE"
2645
+ icon="pi pi-trash"
2646
+ [text]="true"
2647
+ severity="danger"
2648
+ size="small"
2649
+ [pTooltip]="'shared.delete' | translate"
2650
+ (onClick)="onDelete(key)"
2651
+ />
2652
+ </div>
2653
+ </td>
2654
+ </tr>
2655
+ </ng-template>
2656
+ <ng-template #emptymessage>
2657
+ <tr>
2658
+ <td colspan="5" class="text-center py-4 text-muted-color">
2659
+ {{ 'localization.key.empty' | translate }}
2660
+ </td>
2661
+ </tr>
2662
+ </ng-template>
2663
+ </p-table>
2664
+ </div>
2659
2665
 
2666
+ <p-confirmDialog />
2660
2667
  <p-toast />
2661
2668
  `,
2662
2669
  }]
2663
- }], ctorParameters: () => [] });
2670
+ }] });
2664
2671
 
2665
- var translationKeyForm_page = /*#__PURE__*/Object.freeze({
2672
+ var translationKeyList_page = /*#__PURE__*/Object.freeze({
2666
2673
  __proto__: null,
2667
- TranslationKeyFormPage: TranslationKeyFormPage
2674
+ TranslationKeyListPage: TranslationKeyListPage
2668
2675
  });
2669
2676
 
2670
2677
  // Interfaces
@@ -2673,5 +2680,5 @@ var translationKeyForm_page = /*#__PURE__*/Object.freeze({
2673
2680
  * Generated bundle index. Do not edit.
2674
2681
  */
2675
2682
 
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 };
2683
+ export { LOCALIZATION_CONFIG, LOCALIZATION_MESSAGES, LOCALIZATION_ROUTES, LanguageApiService, LanguageFormPage, LanguageListPage, LanguageSelectorComponent, LocalizationApiService, LocalizationContainerPage, LocalizationInitializerService, LocalizationStateService, STORAGE_KEYS, TranslationApiService, TranslationKeyApiService, TranslationKeyFormPage, TranslationKeyListPage, TranslationLoaderService, getLocalizationConfig, provideLocalization };
2677
2684
  //# sourceMappingURL=flusys-ng-localization.mjs.map