@flusys/ng-localization 9.1.0 → 9.1.2

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.
@@ -0,0 +1,639 @@
1
+ import { LanguageApiService } from "./flusys-ng-localization.mjs";
2
+ import * as i0 from "@angular/core";
3
+ import { Component, DestroyRef, computed, effect, inject, signal } from "@angular/core";
4
+ import { TranslatePipe, TranslateService } from "@flusys/ng-shared";
5
+ import { finalize, from } from "rxjs";
6
+ import { takeUntilDestroyed, toSignal } from "@angular/core/rxjs-interop";
7
+ import { CardComponent, CheckboxComponent, FButtonDirective, InputTextDirective, MessageService, SelectComponent, UiIconComponent } from "@flusys/ng-ui";
8
+ import * as i1 from "@angular/forms";
9
+ import { FormsModule } from "@angular/forms";
10
+ import { FormField, disabled, form, maxLength, required } from "@angular/forms/signals";
11
+ import { ActivatedRoute, Router } from "@angular/router";
12
+ var LanguageFormPage = class LanguageFormPage {
13
+ router = inject(Router);
14
+ route = inject(ActivatedRoute);
15
+ languageService = inject(LanguageApiService);
16
+ messageService = inject(MessageService);
17
+ translateAdapter = inject(TranslateService, { optional: true });
18
+ destroyRef = inject(DestroyRef);
19
+ routeParams = toSignal(this.route.paramMap);
20
+ loadTriggered = false;
21
+ isSaving = signal(false, ...ngDevMode ? [{ debugName: "isSaving" }] : /* istanbul ignore next */ []);
22
+ existingLanguage = signal(null, ...ngDevMode ? [{ debugName: "existingLanguage" }] : /* istanbul ignore next */ []);
23
+ isEditMode = computed(() => !!this.existingLanguage(), ...ngDevMode ? [{ debugName: "isEditMode" }] : /* istanbul ignore next */ []);
24
+ isReadonly = computed(() => this.existingLanguage()?.isReadonly ?? false, ...ngDevMode ? [{ debugName: "isReadonly" }] : /* istanbul ignore next */ []);
25
+ translate(key, vars) {
26
+ return this.translateAdapter?.translate(key, vars) ?? key;
27
+ }
28
+ directionOptions = computed(() => [{
29
+ label: this.translate("localization.language.direction.ltr"),
30
+ value: "ltr"
31
+ }, {
32
+ label: this.translate("localization.language.direction.rtl"),
33
+ value: "rtl"
34
+ }], ...ngDevMode ? [{ debugName: "directionOptions" }] : /* istanbul ignore next */ []);
35
+ formModel = signal({
36
+ code: "",
37
+ name: "",
38
+ nativeName: "",
39
+ direction: "ltr",
40
+ serial: "",
41
+ isActive: true,
42
+ isDefault: false
43
+ }, ...ngDevMode ? [{ debugName: "formModel" }] : /* istanbul ignore next */ []);
44
+ formTree = form(this.formModel, (f) => {
45
+ required(f.code, { message: this.translate("localization.validation.code.required") });
46
+ maxLength(f.code, 10, { message: this.translate("localization.validation.code.max.length") });
47
+ required(f.name, { message: this.translate("localization.validation.name.required") });
48
+ maxLength(f.name, 100, { message: this.translate("localization.validation.name.max.length") });
49
+ disabled(f.code, { when: () => this.isReadonly() });
50
+ disabled(f.name, { when: () => this.isReadonly() });
51
+ disabled(f.nativeName, { when: () => this.isReadonly() });
52
+ });
53
+ isFormValid = computed(() => {
54
+ const model = this.formModel();
55
+ return model.code.trim().length > 0 && model.name.trim().length > 0;
56
+ }, ...ngDevMode ? [{ debugName: "isFormValid" }] : /* istanbul ignore next */ []);
57
+ constructor() {
58
+ effect(() => {
59
+ const params = this.routeParams();
60
+ if (!params || this.loadTriggered) return;
61
+ this.loadTriggered = true;
62
+ const id = params.get("id");
63
+ if (id && id !== "new") this.loadLanguage(id);
64
+ });
65
+ }
66
+ updateDirection(value) {
67
+ this.formModel.update((m) => ({
68
+ ...m,
69
+ direction: value
70
+ }));
71
+ }
72
+ loadLanguage(id) {
73
+ this.isSaving.set(true);
74
+ from(this.languageService.findById(id)).pipe(takeUntilDestroyed(this.destroyRef), finalize(() => this.isSaving.set(false))).subscribe({
75
+ next: (response) => {
76
+ if (response.success && response.data) {
77
+ this.existingLanguage.set(response.data);
78
+ this.formModel.set(this.toFormModel(response.data));
79
+ }
80
+ },
81
+ error: () => this.router.navigate(["/localization/languages"])
82
+ });
83
+ }
84
+ toFormModel(language) {
85
+ return {
86
+ code: language.code,
87
+ name: language.name,
88
+ nativeName: language.nativeName ?? "",
89
+ direction: language.direction ?? "ltr",
90
+ serial: language.serial?.toString() ?? "",
91
+ isActive: language.isActive,
92
+ isDefault: language.isDefault
93
+ };
94
+ }
95
+ onSubmit() {
96
+ if (!this.isFormValid()) return;
97
+ this.isSaving.set(true);
98
+ const model = this.formModel();
99
+ const dto = {
100
+ code: model.code,
101
+ name: model.name,
102
+ nativeName: model.nativeName || void 0,
103
+ direction: model.direction,
104
+ serial: model.serial ? parseInt(model.serial, 10) : void 0,
105
+ isActive: model.isActive,
106
+ isDefault: model.isDefault
107
+ };
108
+ (this.isEditMode() ? from(this.languageService.update({
109
+ id: this.existingLanguage().id,
110
+ ...dto
111
+ })) : from(this.languageService.insert(dto))).pipe(takeUntilDestroyed(this.destroyRef), finalize(() => this.isSaving.set(false))).subscribe({ next: (response) => {
112
+ if (response.success) {
113
+ this.messageService.add({
114
+ severity: "success",
115
+ summary: this.translate("shared.success"),
116
+ detail: response.messageKey ? this.translate(response.messageKey, response.messageVariables) : response.message ?? ""
117
+ });
118
+ this.router.navigate(["/localization/languages"]);
119
+ }
120
+ } });
121
+ }
122
+ onCancel() {
123
+ this.router.navigate(["/localization/languages"]);
124
+ }
125
+ static ɵfac = i0.ɵɵngDeclareFactory({
126
+ minVersion: "12.0.0",
127
+ version: "22.2.1",
128
+ ngImport: i0,
129
+ type: LanguageFormPage,
130
+ deps: [],
131
+ target: i0.ɵɵFactoryTarget.Component
132
+ });
133
+ static ɵcmp = i0.ɵɵngDeclareComponent({
134
+ minVersion: "17.0.0",
135
+ version: "22.2.1",
136
+ type: LanguageFormPage,
137
+ isStandalone: true,
138
+ selector: "lib-language-form-page",
139
+ providers: [MessageService],
140
+ ngImport: i0,
141
+ template: `
142
+ <f-card>
143
+ <ng-template #header>
144
+ <div class="flex justify-between items-center p-4">
145
+ <h2 class="text-xl font-semibold m-0">
146
+ {{
147
+ isEditMode()
148
+ ? ('localization.language.edit' | translate)
149
+ : ('localization.language.new' | translate)
150
+ }}
151
+ </h2>
152
+ </div>
153
+ </ng-template>
154
+
155
+ <form
156
+ (ngSubmit)="onSubmit()"
157
+ class="grid grid-cols-1 md:grid-cols-2 gap-4"
158
+ >
159
+ <!-- Language Code -->
160
+ <div class="flex flex-col gap-2">
161
+ <label for="code" class="font-medium"
162
+ >{{ 'localization.language.code' | translate }} *</label
163
+ >
164
+ <input
165
+ fInputText
166
+ id="code"
167
+ [formField]="formTree.code"
168
+ [placeholder]="'localization.placeholder.code' | translate"
169
+ />
170
+ <small class="text-muted-color">{{
171
+ 'localization.language.iso.code.hint' | translate
172
+ }}</small>
173
+ </div>
174
+
175
+ <!-- Language Name -->
176
+ <div class="flex flex-col gap-2">
177
+ <label for="name" class="font-medium"
178
+ >{{ 'localization.language.name' | translate }} *</label
179
+ >
180
+ <input
181
+ fInputText
182
+ id="name"
183
+ [formField]="formTree.name"
184
+ [placeholder]="'localization.placeholder.name' | translate"
185
+ />
186
+ </div>
187
+
188
+ <!-- Native Name -->
189
+ <div class="flex flex-col gap-2">
190
+ <label for="nativeName" class="font-medium">{{
191
+ 'localization.language.native.name' | translate
192
+ }}</label>
193
+ <input
194
+ fInputText
195
+ id="nativeName"
196
+ [formField]="formTree.nativeName"
197
+ [placeholder]="'localization.placeholder.native.name' | translate"
198
+ />
199
+ <small class="text-muted-color">{{
200
+ 'localization.language.native.name.hint' | translate
201
+ }}</small>
202
+ </div>
203
+
204
+ <!-- Text Direction -->
205
+ <div class="flex flex-col gap-2">
206
+ <label for="direction" class="font-medium"
207
+ >{{ 'localization.language.direction' | translate }} *</label
208
+ >
209
+ <f-select
210
+ id="direction"
211
+ [ngModel]="formModel().direction"
212
+ (ngModelChange)="updateDirection($event)"
213
+ [ngModelOptions]="{ standalone: true }"
214
+ [options]="directionOptions()"
215
+ optionLabel="label"
216
+ optionValue="value"
217
+ [disabled]="isReadonly()"
218
+ class="w-full"
219
+ />
220
+ @if (isReadonly()) {
221
+ <small class="text-muted-color">{{
222
+ 'localization.language.readonly.edit.hint' | translate
223
+ }}</small>
224
+ }
225
+ </div>
226
+
227
+ <!-- Display Order -->
228
+ <div class="flex flex-col gap-2">
229
+ <label for="serial" class="font-medium">{{
230
+ 'localization.language.display.order' | translate
231
+ }}</label>
232
+ <input
233
+ fInputText
234
+ id="serial"
235
+ type="number"
236
+ [formField]="formTree.serial"
237
+ [placeholder]="'shared.display.order.placeholder' | translate"
238
+ />
239
+ </div>
240
+
241
+ <!-- Empty cell for alignment -->
242
+ <div class="hidden md:block"></div>
243
+
244
+ <!-- Checkboxes -->
245
+ <div class="flex items-center gap-6 md:col-span-2 pt-4">
246
+ <f-checkbox [formField]="formTree.isActive" [label]="'shared.active' | translate" />
247
+ <f-checkbox [formField]="formTree.isDefault" [label]="'localization.language.default' | translate" />
248
+ </div>
249
+
250
+ <!-- Actions -->
251
+ <div class="flex justify-end gap-4 md:col-span-2 pt-4">
252
+ <button
253
+ fButton
254
+ type="button"
255
+ severity="secondary"
256
+ [outlined]="true"
257
+ (click)="onCancel()"
258
+ >
259
+ <span pButtonLabel>{{ 'shared.cancel' | translate }}</span>
260
+ </button>
261
+ <button
262
+ fButton
263
+ type="submit"
264
+ [disabled]="!isFormValid() || isSaving()"
265
+ >
266
+ @if (isSaving()) {
267
+ <ui-icon pButtonIcon name="spinner" [spin]="true"></ui-icon>
268
+ }
269
+ <span pButtonLabel>{{
270
+ isEditMode()
271
+ ? ('shared.update' | translate)
272
+ : ('shared.create' | translate)
273
+ }}</span>
274
+ </button>
275
+ </div>
276
+ </form>
277
+ </f-card>
278
+ `,
279
+ isInline: true,
280
+ dependencies: [
281
+ {
282
+ kind: "component",
283
+ type: CardComponent,
284
+ selector: "f-card",
285
+ inputs: [
286
+ "header",
287
+ "subheader",
288
+ "styleClass",
289
+ "style"
290
+ ]
291
+ },
292
+ {
293
+ kind: "directive",
294
+ type: InputTextDirective,
295
+ selector: "[fInputText]",
296
+ inputs: [
297
+ "invalid",
298
+ "touched",
299
+ "variant",
300
+ "size",
301
+ "fluid"
302
+ ]
303
+ },
304
+ {
305
+ kind: "component",
306
+ type: SelectComponent,
307
+ selector: "f-select",
308
+ inputs: [
309
+ "options",
310
+ "optionLabel",
311
+ "optionValue",
312
+ "placeholder",
313
+ "disabled",
314
+ "filter",
315
+ "filterBy",
316
+ "filterMatchMode",
317
+ "filterLocale",
318
+ "filterPlaceholder",
319
+ "resetFilterOnHide",
320
+ "autofocusFilter",
321
+ "emptyMessage",
322
+ "emptyFilterMessage",
323
+ "showClear",
324
+ "loading",
325
+ "loadingIcon",
326
+ "dropdownIcon",
327
+ "clearIcon",
328
+ "inputId",
329
+ "invalid",
330
+ "touched",
331
+ "styleClass",
332
+ "style",
333
+ "appendTo",
334
+ "editable",
335
+ "readonly",
336
+ "autofocus",
337
+ "checkmark",
338
+ "group",
339
+ "optionGroupLabel",
340
+ "optionGroupChildren",
341
+ "virtualScroll",
342
+ "virtualScrollItemSize",
343
+ "lazy",
344
+ "selectedObject"
345
+ ],
346
+ outputs: [
347
+ "onFilter",
348
+ "onChange",
349
+ "onShow",
350
+ "onHide",
351
+ "onBlur",
352
+ "onFocus",
353
+ "onClear",
354
+ "onLazyLoad"
355
+ ]
356
+ },
357
+ {
358
+ kind: "component",
359
+ type: CheckboxComponent,
360
+ selector: "f-checkbox",
361
+ inputs: [
362
+ "label",
363
+ "binary",
364
+ "disabled",
365
+ "readonly",
366
+ "inputId",
367
+ "invalid",
368
+ "indeterminate",
369
+ "variant",
370
+ "size",
371
+ "touched",
372
+ "styleClass",
373
+ "style",
374
+ "checked"
375
+ ],
376
+ outputs: [
377
+ "onChange",
378
+ "onBlur",
379
+ "touch",
380
+ "checkedChange"
381
+ ]
382
+ },
383
+ {
384
+ kind: "directive",
385
+ type: FButtonDirective,
386
+ selector: "[fButton]",
387
+ inputs: [
388
+ "severity",
389
+ "text",
390
+ "outlined",
391
+ "link",
392
+ "variant",
393
+ "rounded",
394
+ "raised",
395
+ "fluid",
396
+ "disabled",
397
+ "loading",
398
+ "loadingIcon",
399
+ "size",
400
+ "label",
401
+ "icon",
402
+ "iconPos",
403
+ "ariaLabel",
404
+ "plain"
405
+ ]
406
+ },
407
+ {
408
+ kind: "component",
409
+ type: UiIconComponent,
410
+ selector: "ui-icon",
411
+ inputs: [
412
+ "name",
413
+ "size",
414
+ "spin",
415
+ "fill",
416
+ "severity"
417
+ ]
418
+ },
419
+ {
420
+ kind: "ngmodule",
421
+ type: FormsModule
422
+ },
423
+ {
424
+ kind: "directive",
425
+ type: i1.ɵNgNoValidate,
426
+ selector: "form:not([ngNoForm]):not([ngNativeValidate])"
427
+ },
428
+ {
429
+ kind: "directive",
430
+ type: i1.NgControlStatus,
431
+ selector: "[formControlName],[ngModel],[formControl]"
432
+ },
433
+ {
434
+ kind: "directive",
435
+ type: i1.NgControlStatusGroup,
436
+ selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]"
437
+ },
438
+ {
439
+ kind: "directive",
440
+ type: i1.NgModel,
441
+ selector: "[ngModel]:not([formControlName]):not([formControl])",
442
+ inputs: [
443
+ "name",
444
+ "disabled",
445
+ "ngModel",
446
+ "ngModelOptions"
447
+ ],
448
+ outputs: ["ngModelChange"],
449
+ exportAs: ["ngModel"]
450
+ },
451
+ {
452
+ kind: "directive",
453
+ type: i1.NgForm,
454
+ selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]",
455
+ inputs: ["ngFormOptions"],
456
+ outputs: ["ngSubmit"],
457
+ exportAs: ["ngForm"]
458
+ },
459
+ {
460
+ kind: "directive",
461
+ type: FormField,
462
+ selector: "[formField]",
463
+ inputs: ["formField"],
464
+ exportAs: ["formField"]
465
+ },
466
+ {
467
+ kind: "pipe",
468
+ type: TranslatePipe,
469
+ name: "translate"
470
+ }
471
+ ]
472
+ });
473
+ };
474
+ i0.ɵɵngDeclareClassMetadata({
475
+ minVersion: "12.0.0",
476
+ version: "22.2.1",
477
+ ngImport: i0,
478
+ type: LanguageFormPage,
479
+ decorators: [{
480
+ type: Component,
481
+ args: [{
482
+ selector: "lib-language-form-page",
483
+ imports: [
484
+ CardComponent,
485
+ InputTextDirective,
486
+ SelectComponent,
487
+ CheckboxComponent,
488
+ FButtonDirective,
489
+ UiIconComponent,
490
+ FormsModule,
491
+ FormField,
492
+ TranslatePipe
493
+ ],
494
+ providers: [MessageService],
495
+ template: `
496
+ <f-card>
497
+ <ng-template #header>
498
+ <div class="flex justify-between items-center p-4">
499
+ <h2 class="text-xl font-semibold m-0">
500
+ {{
501
+ isEditMode()
502
+ ? ('localization.language.edit' | translate)
503
+ : ('localization.language.new' | translate)
504
+ }}
505
+ </h2>
506
+ </div>
507
+ </ng-template>
508
+
509
+ <form
510
+ (ngSubmit)="onSubmit()"
511
+ class="grid grid-cols-1 md:grid-cols-2 gap-4"
512
+ >
513
+ <!-- Language Code -->
514
+ <div class="flex flex-col gap-2">
515
+ <label for="code" class="font-medium"
516
+ >{{ 'localization.language.code' | translate }} *</label
517
+ >
518
+ <input
519
+ fInputText
520
+ id="code"
521
+ [formField]="formTree.code"
522
+ [placeholder]="'localization.placeholder.code' | translate"
523
+ />
524
+ <small class="text-muted-color">{{
525
+ 'localization.language.iso.code.hint' | translate
526
+ }}</small>
527
+ </div>
528
+
529
+ <!-- Language Name -->
530
+ <div class="flex flex-col gap-2">
531
+ <label for="name" class="font-medium"
532
+ >{{ 'localization.language.name' | translate }} *</label
533
+ >
534
+ <input
535
+ fInputText
536
+ id="name"
537
+ [formField]="formTree.name"
538
+ [placeholder]="'localization.placeholder.name' | translate"
539
+ />
540
+ </div>
541
+
542
+ <!-- Native Name -->
543
+ <div class="flex flex-col gap-2">
544
+ <label for="nativeName" class="font-medium">{{
545
+ 'localization.language.native.name' | translate
546
+ }}</label>
547
+ <input
548
+ fInputText
549
+ id="nativeName"
550
+ [formField]="formTree.nativeName"
551
+ [placeholder]="'localization.placeholder.native.name' | translate"
552
+ />
553
+ <small class="text-muted-color">{{
554
+ 'localization.language.native.name.hint' | translate
555
+ }}</small>
556
+ </div>
557
+
558
+ <!-- Text Direction -->
559
+ <div class="flex flex-col gap-2">
560
+ <label for="direction" class="font-medium"
561
+ >{{ 'localization.language.direction' | translate }} *</label
562
+ >
563
+ <f-select
564
+ id="direction"
565
+ [ngModel]="formModel().direction"
566
+ (ngModelChange)="updateDirection($event)"
567
+ [ngModelOptions]="{ standalone: true }"
568
+ [options]="directionOptions()"
569
+ optionLabel="label"
570
+ optionValue="value"
571
+ [disabled]="isReadonly()"
572
+ class="w-full"
573
+ />
574
+ @if (isReadonly()) {
575
+ <small class="text-muted-color">{{
576
+ 'localization.language.readonly.edit.hint' | translate
577
+ }}</small>
578
+ }
579
+ </div>
580
+
581
+ <!-- Display Order -->
582
+ <div class="flex flex-col gap-2">
583
+ <label for="serial" class="font-medium">{{
584
+ 'localization.language.display.order' | translate
585
+ }}</label>
586
+ <input
587
+ fInputText
588
+ id="serial"
589
+ type="number"
590
+ [formField]="formTree.serial"
591
+ [placeholder]="'shared.display.order.placeholder' | translate"
592
+ />
593
+ </div>
594
+
595
+ <!-- Empty cell for alignment -->
596
+ <div class="hidden md:block"></div>
597
+
598
+ <!-- Checkboxes -->
599
+ <div class="flex items-center gap-6 md:col-span-2 pt-4">
600
+ <f-checkbox [formField]="formTree.isActive" [label]="'shared.active' | translate" />
601
+ <f-checkbox [formField]="formTree.isDefault" [label]="'localization.language.default' | translate" />
602
+ </div>
603
+
604
+ <!-- Actions -->
605
+ <div class="flex justify-end gap-4 md:col-span-2 pt-4">
606
+ <button
607
+ fButton
608
+ type="button"
609
+ severity="secondary"
610
+ [outlined]="true"
611
+ (click)="onCancel()"
612
+ >
613
+ <span pButtonLabel>{{ 'shared.cancel' | translate }}</span>
614
+ </button>
615
+ <button
616
+ fButton
617
+ type="submit"
618
+ [disabled]="!isFormValid() || isSaving()"
619
+ >
620
+ @if (isSaving()) {
621
+ <ui-icon pButtonIcon name="spinner" [spin]="true"></ui-icon>
622
+ }
623
+ <span pButtonLabel>{{
624
+ isEditMode()
625
+ ? ('shared.update' | translate)
626
+ : ('shared.create' | translate)
627
+ }}</span>
628
+ </button>
629
+ </div>
630
+ </form>
631
+ </f-card>
632
+ `
633
+ }]
634
+ }],
635
+ ctorParameters: () => []
636
+ });
637
+ export { LanguageFormPage };
638
+
639
+ //# sourceMappingURL=flusys-ng-localization-language-form.page-BdxZ6meb.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flusys-ng-localization-language-form.page-BdxZ6meb.mjs","names":[],"sources":["../../../projects/ng-localization/pages/language-form/language-form.page.ts"],"sourcesContent":["import {\n Component,\n computed,\n DestroyRef,\n effect,\n inject,\n signal,\n} from '@angular/core';\nimport { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';\nimport { FormsModule } from '@angular/forms';\nimport {\n disabled,\n form,\n FormField,\n maxLength,\n required,\n} from '@angular/forms/signals';\nimport { ActivatedRoute, Router } from '@angular/router';\nimport {\n CardComponent,\n CheckboxComponent,\n FButtonDirective,\n InputTextDirective,\n MessageService,\n SelectComponent,\n UiIconComponent,\n} from '@flusys/ng-ui';\nimport {\n TranslatePipe,\n TranslateService,\n} from '@flusys/ng-shared';\nimport { finalize, from } from 'rxjs';\nimport {\n ICreateLanguageDto,\n ILanguage,\n IUpdateLanguageDto,\n} from '../../interfaces/language.interface';\nimport { LanguageApiService } from '../../services/language-api.service';\n\ninterface ILanguageFormModel {\n code: string;\n name: string;\n nativeName: string;\n direction: 'ltr' | 'rtl';\n serial: string;\n isActive: boolean;\n isDefault: boolean;\n}\n\n@Component({\n selector: 'lib-language-form-page',\n imports: [\n CardComponent,\n InputTextDirective,\n SelectComponent,\n CheckboxComponent,\n FButtonDirective,\n UiIconComponent,\n FormsModule,\n FormField,\n TranslatePipe,\n ],\n providers: [MessageService],\n template: `\n <f-card>\n <ng-template #header>\n <div class=\"flex justify-between items-center p-4\">\n <h2 class=\"text-xl font-semibold m-0\">\n {{\n isEditMode()\n ? ('localization.language.edit' | translate)\n : ('localization.language.new' | translate)\n }}\n </h2>\n </div>\n </ng-template>\n\n <form\n (ngSubmit)=\"onSubmit()\"\n class=\"grid grid-cols-1 md:grid-cols-2 gap-4\"\n >\n <!-- Language Code -->\n <div class=\"flex flex-col gap-2\">\n <label for=\"code\" class=\"font-medium\"\n >{{ 'localization.language.code' | translate }} *</label\n >\n <input\n fInputText\n id=\"code\"\n [formField]=\"formTree.code\"\n [placeholder]=\"'localization.placeholder.code' | translate\"\n />\n <small class=\"text-muted-color\">{{\n 'localization.language.iso.code.hint' | translate\n }}</small>\n </div>\n\n <!-- Language Name -->\n <div class=\"flex flex-col gap-2\">\n <label for=\"name\" class=\"font-medium\"\n >{{ 'localization.language.name' | translate }} *</label\n >\n <input\n fInputText\n id=\"name\"\n [formField]=\"formTree.name\"\n [placeholder]=\"'localization.placeholder.name' | translate\"\n />\n </div>\n\n <!-- Native Name -->\n <div class=\"flex flex-col gap-2\">\n <label for=\"nativeName\" class=\"font-medium\">{{\n 'localization.language.native.name' | translate\n }}</label>\n <input\n fInputText\n id=\"nativeName\"\n [formField]=\"formTree.nativeName\"\n [placeholder]=\"'localization.placeholder.native.name' | translate\"\n />\n <small class=\"text-muted-color\">{{\n 'localization.language.native.name.hint' | translate\n }}</small>\n </div>\n\n <!-- Text Direction -->\n <div class=\"flex flex-col gap-2\">\n <label for=\"direction\" class=\"font-medium\"\n >{{ 'localization.language.direction' | translate }} *</label\n >\n <f-select\n id=\"direction\"\n [ngModel]=\"formModel().direction\"\n (ngModelChange)=\"updateDirection($event)\"\n [ngModelOptions]=\"{ standalone: true }\"\n [options]=\"directionOptions()\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [disabled]=\"isReadonly()\"\n class=\"w-full\"\n />\n @if (isReadonly()) {\n <small class=\"text-muted-color\">{{\n 'localization.language.readonly.edit.hint' | translate\n }}</small>\n }\n </div>\n\n <!-- Display Order -->\n <div class=\"flex flex-col gap-2\">\n <label for=\"serial\" class=\"font-medium\">{{\n 'localization.language.display.order' | translate\n }}</label>\n <input\n fInputText\n id=\"serial\"\n type=\"number\"\n [formField]=\"formTree.serial\"\n [placeholder]=\"'shared.display.order.placeholder' | translate\"\n />\n </div>\n\n <!-- Empty cell for alignment -->\n <div class=\"hidden md:block\"></div>\n\n <!-- Checkboxes -->\n <div class=\"flex items-center gap-6 md:col-span-2 pt-4\">\n <f-checkbox [formField]=\"formTree.isActive\" [label]=\"'shared.active' | translate\" />\n <f-checkbox [formField]=\"formTree.isDefault\" [label]=\"'localization.language.default' | translate\" />\n </div>\n\n <!-- Actions -->\n <div class=\"flex justify-end gap-4 md:col-span-2 pt-4\">\n <button\n fButton\n type=\"button\"\n severity=\"secondary\"\n [outlined]=\"true\"\n (click)=\"onCancel()\"\n >\n <span pButtonLabel>{{ 'shared.cancel' | translate }}</span>\n </button>\n <button\n fButton\n type=\"submit\"\n [disabled]=\"!isFormValid() || isSaving()\"\n >\n @if (isSaving()) {\n <ui-icon pButtonIcon name=\"spinner\" [spin]=\"true\"></ui-icon>\n }\n <span pButtonLabel>{{ \n isEditMode()\n ? ('shared.update' | translate)\n : ('shared.create' | translate)\n }}</span>\n </button>\n </div>\n </form>\n </f-card>\n `,\n})\nexport class LanguageFormPage {\n private readonly router = inject(Router);\n private readonly route = inject(ActivatedRoute);\n private readonly languageService = inject(LanguageApiService);\n private readonly messageService = inject(MessageService);\n private readonly translateAdapter = inject(TranslateService, {\n optional: true,\n });\n private readonly destroyRef = inject(DestroyRef);\n\n private readonly routeParams = toSignal(this.route.paramMap);\n private loadTriggered = false;\n\n readonly isSaving = signal(false);\n readonly existingLanguage = signal<ILanguage | null>(null);\n readonly isEditMode = computed(() => !!this.existingLanguage());\n readonly isReadonly = computed(() => this.existingLanguage()?.isReadonly ?? false);\n\n private translate(\n key: string,\n vars?: Record<string, string | number>,\n ): string {\n return this.translateAdapter?.translate(key, vars) ?? key;\n }\n\n readonly directionOptions = computed(() => [\n {\n label: this.translate('localization.language.direction.ltr'),\n value: 'ltr',\n },\n {\n label: this.translate('localization.language.direction.rtl'),\n value: 'rtl',\n },\n ]);\n\n readonly formModel = signal<ILanguageFormModel>({\n code: '',\n name: '',\n nativeName: '',\n direction: 'ltr',\n serial: '',\n isActive: true,\n isDefault: false,\n });\n\n readonly formTree = form(this.formModel, (f) => {\n required(f.code, {\n message: this.translate('localization.validation.code.required'),\n });\n maxLength(f.code, 10, {\n message: this.translate('localization.validation.code.max.length'),\n });\n required(f.name, {\n message: this.translate('localization.validation.name.required'),\n });\n maxLength(f.name, 100, {\n message: this.translate('localization.validation.name.max.length'),\n });\n disabled(f.code, { when: () => this.isReadonly() });\n disabled(f.name, { when: () => this.isReadonly() });\n disabled(f.nativeName, { when: () => this.isReadonly() });\n });\n\n readonly isFormValid = computed(() => {\n const model = this.formModel();\n return model.code.trim().length > 0 && model.name.trim().length > 0;\n });\n\n constructor() {\n effect(() => {\n const params = this.routeParams();\n if (!params || this.loadTriggered) return;\n\n this.loadTriggered = true;\n const id = params.get('id');\n if (id && id !== 'new') {\n this.loadLanguage(id);\n }\n });\n }\n\n updateDirection(value: 'ltr' | 'rtl'): void {\n this.formModel.update((m) => ({ ...m, direction: value }));\n }\n\n loadLanguage(id: string): void {\n this.isSaving.set(true);\n from(this.languageService.findById(id))\n .pipe(\n takeUntilDestroyed(this.destroyRef),\n finalize(() => this.isSaving.set(false)),\n )\n .subscribe({\n next: (response) => {\n if (response.success && response.data) {\n this.existingLanguage.set(response.data);\n this.formModel.set(this.toFormModel(response.data));\n }\n },\n error: () => this.router.navigate(['/localization/languages']),\n });\n }\n\n private toFormModel(language: ILanguage): ILanguageFormModel {\n return {\n code: language.code,\n name: language.name,\n nativeName: language.nativeName ?? '',\n direction: language.direction ?? 'ltr',\n serial: language.serial?.toString() ?? '',\n isActive: language.isActive,\n isDefault: language.isDefault,\n };\n }\n\n onSubmit(): void {\n if (!this.isFormValid()) return;\n\n this.isSaving.set(true);\n const model = this.formModel();\n\n const dto = {\n code: model.code,\n name: model.name,\n nativeName: model.nativeName || undefined,\n direction: model.direction,\n serial: model.serial ? parseInt(model.serial, 10) : undefined,\n isActive: model.isActive,\n isDefault: model.isDefault,\n };\n\n const operation$ = this.isEditMode()\n ? from(\n this.languageService.update({\n id: this.existingLanguage()!.id,\n ...dto,\n } as IUpdateLanguageDto),\n )\n : from(this.languageService.insert(dto as ICreateLanguageDto));\n\n operation$\n .pipe(\n takeUntilDestroyed(this.destroyRef),\n finalize(() => this.isSaving.set(false)),\n )\n .subscribe({\n next: (response) => {\n if (response.success) {\n this.messageService.add({\n severity: 'success',\n summary: this.translate('shared.success'),\n detail: response.messageKey\n ? this.translate(\n response.messageKey,\n response.messageVariables as Record<\n string,\n string | number\n >,\n )\n : (response.message ?? ''),\n });\n this.router.navigate(['/localization/languages']);\n }\n },\n });\n }\n\n onCancel(): void {\n this.router.navigate(['/localization/languages']);\n }\n}\n"],"mappings":"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"}