@yomotech/view-toolkit 1.0.15 → 1.0.17

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,7 +1,7 @@
1
1
  import * as i5 from '@angular/common';
2
2
  import { CommonModule, NgIf } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
- import { EventEmitter, HostListener, Output, Input, Component, signal, Injectable, Directive, NgZone, ViewChild, NgModule, inject } from '@angular/core';
4
+ import { EventEmitter, HostListener, Output, Input, Component, signal, Injectable, Directive, inject, ElementRef, NgZone, ViewChild, NgModule, DestroyRef } from '@angular/core';
5
5
  import * as i2 from '@angular/forms';
6
6
  import { FormsModule } from '@angular/forms';
7
7
  import * as i1$1 from '@angular/common/http';
@@ -27,6 +27,7 @@ import { InputTextModule } from 'primeng/inputtext';
27
27
  import * as i18 from 'primeng/autocomplete';
28
28
  import { AutoCompleteModule } from 'primeng/autocomplete';
29
29
  import { Preferences } from '@capacitor/preferences';
30
+ import { SecureStorage } from '@aparajita/capacitor-secure-storage';
30
31
  import { loadStripe } from '@stripe/stripe-js';
31
32
  import { throwError, lastValueFrom, BehaviorSubject } from 'rxjs';
32
33
  import * as i1$2 from 'ngx-toastr';
@@ -57,6 +58,7 @@ import { NzTooltipModule } from 'ng-zorro-antd/tooltip';
57
58
  import { App } from '@capacitor/app';
58
59
  import { Browser } from '@capacitor/browser';
59
60
  import { OAuth2Client } from '@byteowls/capacitor-oauth2';
61
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
60
62
 
61
63
  class PaginationComponent {
62
64
  platform;
@@ -318,6 +320,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
318
320
  type: Input
319
321
  }] } });
320
322
 
323
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07
324
+ // - new: resolveCustomKeyboardType. The keyboards were only switched on by option.keyboardType,
325
+ // which no layout schema sets, so on the devices every field got the system keyboard and the
326
+ // toolkit keyboards never appeared. The type of the attribute now selects the keyboard;
327
+ // option.keyboardType still wins, and 'native' or 'none' keeps the system keyboard for a field
321
328
  function normalizeCustomKeyboardType(value) {
322
329
  if (value === 'email' ||
323
330
  value === 'numeric' ||
@@ -329,9 +336,63 @@ function normalizeCustomKeyboardType(value) {
329
336
  }
330
337
  return null;
331
338
  }
339
+ /**
340
+ * Keyboard for a field: option.keyboardType when the layout sets one, otherwise derived from the
341
+ * attribute (property schema) type. Returns null where the system keyboard stays in charge.
342
+ */
343
+ function resolveCustomKeyboardType(option, property) {
344
+ const explicit = option?.keyboardType;
345
+ if (explicit === 'native' || explicit === 'none') {
346
+ return null;
347
+ }
348
+ const normalized = normalizeCustomKeyboardType(explicit);
349
+ if (normalized) {
350
+ return normalized;
351
+ }
352
+ if (option?.password) {
353
+ return null;
354
+ }
355
+ if (option?.email || property?.format === 'email') {
356
+ return 'email';
357
+ }
358
+ switch (property?.type) {
359
+ case 'date':
360
+ return 'date';
361
+ case 'time':
362
+ return 'time';
363
+ case 'number':
364
+ case 'integer':
365
+ return 'numeric';
366
+ case 'string':
367
+ case 'autocomplete':
368
+ return 'string';
369
+ default:
370
+ return null;
371
+ }
372
+ }
332
373
 
374
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07
375
+ // - change: string accepts every printable character and alpha every letter of any language. The
376
+ // old A-Z rules rejected ä, ö, ü, ß and all punctuation the new layouts offer
377
+ // - new: reserveBottomArea / releaseBottomArea. A date or time picker docked at the bottom takes
378
+ // the place of the keyboard, and the dialog has to end above it exactly as it does above the
379
+ // keyboard, otherwise the lower fields and the save button lie underneath
380
+ // - change: the keyboard starts with its last measured height instead of the 360 px CSS fallback
381
+ // - change: switching between keyboard and picker - either way - keeps the bottom area reserved, so the dialog
382
+ // no longer jumps up and down in between
383
+ // - change: on iOS the active field is no longer set readonly, only inputmode="none"; iOS draws no
384
+ // cursor in a readonly field, so tapping into a date to change the month did nothing
385
+ // - change: date and time are edited at the cursor - a digit overwrites the one right of it,
386
+ // Backspace at the end removes a digit and inside the value steps one digit back; before,
387
+ // both only worked at the end
388
+ // - change: every typed key sends keydown before input, as a real keyboard does. PrimeNG DatePicker
389
+ // ignores input without it, so dates typed on the toolkit keyboard were lost
390
+ // - change: open() clears a stale "picker open" flag when no picker panel is left in the page
391
+ // - change: endPickerOverlay can skip the 250 ms reopen pause. When the user closes a picker by
392
+ // tapping into its field, that tap must open the keyboard, not be swallowed as a focus bounce
333
393
  class CustomKeyboardService {
334
394
  openingSuspendedUntil = 0;
395
+ lastKeyboardHeight = 0;
335
396
  pickerOverlayOpen = false;
336
397
  pickerTriggerPressedUntil = 0;
337
398
  interactionBlocked = signal(false, ...(ngDevMode ? [{ debugName: "interactionBlocked" }] : /* istanbul ignore next */ []));
@@ -343,6 +404,14 @@ class CustomKeyboardService {
343
404
  Capacitor.getPlatform() === 'ios'));
344
405
  }
345
406
  async open(target, type) {
407
+ /*
408
+ * A picker whose dialog was destroyed never reports its end. With no picker panel left in
409
+ * the page the flag is stale; keeping it would block every keyboard from now on.
410
+ */
411
+ if (this.pickerOverlayOpen &&
412
+ !document.querySelector('.model-datepicker-panel')) {
413
+ this.endPickerOverlay({ suspendReopen: false });
414
+ }
346
415
  if (!this.isNativeMobile() ||
347
416
  target.disabled ||
348
417
  this.pickerOverlayOpen ||
@@ -369,10 +438,13 @@ class CustomKeyboardService {
369
438
  originalInputType
370
439
  };
371
440
  /*
372
- * readonly is the most reliable way to prevent the native
373
- * keyboard from opening on both Android and iOS.
441
+ * readonly is the most reliable way to prevent the native keyboard on Android. iOS shows no
442
+ * cursor in a readonly field, so a date could not be edited at a tapped position there;
443
+ * inputmode="none" alone keeps the iOS keyboard away (iOS 12.2+) and leaves the cursor.
374
444
  */
375
- target.readOnly = true;
445
+ if (Capacitor.getPlatform() !== 'ios') {
446
+ target.readOnly = true;
447
+ }
376
448
  target.setAttribute('inputmode', 'none');
377
449
  /*
378
450
  * type="number" cannot temporarily contain values such as:
@@ -387,6 +459,10 @@ class CustomKeyboardService {
387
459
  this.activeSession.set(session);
388
460
  this.blockInitialInteraction(session);
389
461
  document.body.classList.add('vl-custom-keyboard-open');
462
+ // Start with the last measured height, not the CSS fallback, so the dialog does not flicker.
463
+ if (this.lastKeyboardHeight) {
464
+ document.body.style.setProperty('--vl-custom-keyboard-height', `${this.lastKeyboardHeight}px`);
465
+ }
390
466
  this.updateKeyboardHeight();
391
467
  document.addEventListener('pointerdown', this.documentPointerDownHandler, true);
392
468
  try {
@@ -479,6 +555,7 @@ class CustomKeyboardService {
479
555
  return;
480
556
  }
481
557
  const keyboardHeight = Math.ceil(keyboardElement.getBoundingClientRect().height);
558
+ this.lastKeyboardHeight = keyboardHeight;
482
559
  document.body.style.setProperty('--vl-custom-keyboard-height', `${keyboardHeight}px`);
483
560
  });
484
561
  });
@@ -557,7 +634,7 @@ class CustomKeyboardService {
557
634
  if (!session) {
558
635
  return;
559
636
  }
560
- const { blurTarget = true, restoreAccessoryBar = true } = option;
637
+ const { blurTarget = true, restoreAccessoryBar = true, keepBottomArea = false } = option;
561
638
  const { target } = session;
562
639
  document.removeEventListener('pointerdown', this.documentPointerDownHandler, true);
563
640
  target.removeEventListener('blur', this.activeTargetBlurHandler);
@@ -592,8 +669,10 @@ class CustomKeyboardService {
592
669
  if (blurTarget) {
593
670
  target.blur();
594
671
  }
595
- document.body.classList.remove('vl-custom-keyboard-open');
596
- document.body.style.removeProperty('--vl-custom-keyboard-height');
672
+ if (!keepBottomArea) {
673
+ document.body.classList.remove('vl-custom-keyboard-open');
674
+ document.body.style.removeProperty('--vl-custom-keyboard-height');
675
+ }
597
676
  if (restoreAccessoryBar &&
598
677
  this.isNativeMobile() &&
599
678
  Capacitor.getPlatform() === 'ios') {
@@ -611,6 +690,15 @@ class CustomKeyboardService {
611
690
  }
612
691
  }
613
692
  updateTarget(target, value, cursorPosition) {
693
+ /*
694
+ * A real key sends keydown before the value changes. PrimeNG DatePicker drops every input
695
+ * event without one (onUserInput checks isKeydown), so a date typed here never reached the
696
+ * model and vanished on blur.
697
+ */
698
+ target.dispatchEvent(new KeyboardEvent('keydown', {
699
+ bubbles: true,
700
+ key: 'Unidentified'
701
+ }));
614
702
  target.value = value;
615
703
  /*
616
704
  * Angular's DefaultValueAccessor and NumberValueAccessor
@@ -649,9 +737,10 @@ class CustomKeyboardService {
649
737
  value.length <= 5);
650
738
  }
651
739
  case 'alpha':
652
- return /^[A-Za-z ]*$/.test(value);
740
+ return /^[\p{L}\p{M} ]*$/u.test(value);
653
741
  case 'string':
654
- return /^[A-Za-z0-9 ]*$/.test(value);
742
+ // Anything printable; control characters never come from the keys.
743
+ return /^[^\p{Cc}\p{Cs}]*$/u.test(value);
655
744
  default:
656
745
  return false;
657
746
  }
@@ -718,6 +807,19 @@ class CustomKeyboardService {
718
807
  }, 0);
719
808
  return;
720
809
  }
810
+ /*
811
+ * The toolkit's own picker icon: the picker is about to take the keyboard's place, so the
812
+ * bottom area stays reserved and the dialog does not jump in between.
813
+ */
814
+ if (eventTarget instanceof Element &&
815
+ eventTarget.closest('.picker-field__icon')) {
816
+ this.close({
817
+ blurTarget: false,
818
+ restoreAccessoryBar: false,
819
+ keepBottomArea: true
820
+ });
821
+ return;
822
+ }
721
823
  /*
722
824
  * Detect the PrimeNG DatePicker icon button.
723
825
  */
@@ -784,21 +886,84 @@ class CustomKeyboardService {
784
886
  this.close();
785
887
  }, 0);
786
888
  }
889
+ /*
890
+ * Date and time have a fixed shape (yyyy-mm-dd, hh:mm), so they are edited on their digits:
891
+ * a digit overwrites the one right of the cursor, Backspace removes the last digit or, inside
892
+ * the value, steps one digit back; the separators are skipped. With the cursor at the end this is the old append/delete.
893
+ */
787
894
  insertDateTimeDigit(target, type, character) {
788
895
  if (!/^\d$/.test(character)) {
789
896
  return;
790
897
  }
791
898
  const maximumDigits = type === 'date' ? 8 : 4;
792
- const currentDigits = (target.value ?? '')
793
- .replace(/\D/g, '');
794
- if (currentDigits.length >= maximumDigits) {
899
+ const digits = (target.value ?? '').replace(/\D/g, '');
900
+ const caret = this.digitsBeforeCaret(target);
901
+ let nextDigits;
902
+ if (caret < digits.length) {
903
+ nextDigits = digits.slice(0, caret) + character + digits.slice(caret + 1);
904
+ }
905
+ else if (digits.length < maximumDigits) {
906
+ nextDigits = digits + character;
907
+ }
908
+ else {
909
+ return;
910
+ }
911
+ this.updateDateTimeTarget(target, type, nextDigits, caret + 1);
912
+ }
913
+ removeDateTimeDigit(target, type) {
914
+ const digits = (target.value ?? '').replace(/\D/g, '');
915
+ const caret = this.digitsBeforeCaret(target);
916
+ if (caret <= 0) {
917
+ return;
918
+ }
919
+ /*
920
+ * Inside the value Backspace only steps one digit back, ready to be overwritten; removing
921
+ * it would shift the rest and turn 2026-11-21 into 2026-12-1.
922
+ */
923
+ if (caret < digits.length) {
924
+ this.setCaret(target, this.positionAfterDigits(target.value ?? '', caret - 1));
795
925
  return;
796
926
  }
797
- const nextDigits = currentDigits + character;
798
- const formattedValue = type === 'date'
799
- ? this.formatDateDigits(nextDigits)
800
- : this.formatTimeDigits(nextDigits);
801
- this.updateTarget(target, formattedValue, formattedValue.length);
927
+ this.updateDateTimeTarget(target, type, digits.slice(0, caret - 1), caret - 1);
928
+ }
929
+ setCaret(target, position) {
930
+ try {
931
+ target.setSelectionRange(position, position);
932
+ }
933
+ catch {
934
+ // Some inputs do not support selection ranges.
935
+ }
936
+ }
937
+ /** Number of digits left of the cursor; the end when the field has no usable selection. */
938
+ digitsBeforeCaret(target) {
939
+ const value = target.value ?? '';
940
+ let position = value.length;
941
+ try {
942
+ position = target.selectionStart ?? value.length;
943
+ }
944
+ catch {
945
+ // Some inputs do not expose a selection.
946
+ }
947
+ return value.slice(0, position).replace(/\D/g, '').length;
948
+ }
949
+ updateDateTimeTarget(target, type, digits, digitsBeforeCaret) {
950
+ const formatted = type === 'date'
951
+ ? this.formatDateDigits(digits)
952
+ : this.formatTimeDigits(digits);
953
+ this.updateTarget(target, formatted, this.positionAfterDigits(formatted, digitsBeforeCaret));
954
+ }
955
+ /** String position right after the given number of digits, separators included. */
956
+ positionAfterDigits(value, count) {
957
+ if (count <= 0) {
958
+ return 0;
959
+ }
960
+ let seen = 0;
961
+ for (let index = 0; index < value.length; index++) {
962
+ if (/\d/.test(value[index]) && ++seen === count) {
963
+ return index + 1;
964
+ }
965
+ }
966
+ return value.length;
802
967
  }
803
968
  formatDateDigits(value) {
804
969
  const digits = value
@@ -825,15 +990,6 @@ class CustomKeyboardService {
825
990
  return (`${digits.slice(0, 2)}:` +
826
991
  digits.slice(2));
827
992
  }
828
- removeDateTimeDigit(target, type) {
829
- const digits = (target.value ?? '')
830
- .replace(/\D/g, '')
831
- .slice(0, -1);
832
- const formattedValue = type === 'date'
833
- ? this.formatDateDigits(digits)
834
- : this.formatTimeDigits(digits);
835
- this.updateTarget(target, formattedValue, formattedValue.length);
836
- }
837
993
  beginPickerOverlay() {
838
994
  this.pickerOverlayOpen = true;
839
995
  /*
@@ -843,9 +999,14 @@ class CustomKeyboardService {
843
999
  * Do not force another blur because PrimeNG is already
844
1000
  * handling focus during the icon click.
845
1001
  */
1002
+ /*
1003
+ * The picker takes the keyboard's place: keep the reserved bottom area until the picker
1004
+ * reserves its own, otherwise the dialog grows and shrinks again in between.
1005
+ */
846
1006
  this.close({
847
1007
  blurTarget: false,
848
- restoreAccessoryBar: false
1008
+ restoreAccessoryBar: false,
1009
+ keepBottomArea: true
849
1010
  });
850
1011
  if (this.isNativeMobile()) {
851
1012
  void Keyboard.hide().catch(() => {
@@ -853,14 +1014,32 @@ class CustomKeyboardService {
853
1014
  });
854
1015
  }
855
1016
  }
856
- endPickerOverlay() {
1017
+ endPickerOverlay(option = {}) {
857
1018
  this.pickerOverlayOpen = false;
1019
+ if (option.keepBottomArea) {
1020
+ /*
1021
+ * The keyboard is about to take the picker's place (tap into the field): releasing the
1022
+ * area now would grow the dialog and pull the field away under the finger. Release it
1023
+ * only if no keyboard follows.
1024
+ */
1025
+ window.setTimeout(() => {
1026
+ if (!this.activeSession() && !this.pickerOverlayOpen) {
1027
+ this.releaseBottomArea();
1028
+ }
1029
+ }, 600);
1030
+ }
1031
+ else {
1032
+ this.releaseBottomArea();
1033
+ }
858
1034
  /*
859
1035
  * PrimeNG may focus the input again while closing.
860
- * Suppress that focus bounce briefly.
1036
+ * Suppress that focus bounce briefly - unless the user
1037
+ * closed the picker by tapping into the field on purpose.
861
1038
  */
862
- this.openingSuspendedUntil =
863
- Date.now() + 250;
1039
+ if (option.suspendReopen !== false) {
1040
+ this.openingSuspendedUntil =
1041
+ Date.now() + 250;
1042
+ }
864
1043
  if (this.isNativeMobile() &&
865
1044
  Capacitor.getPlatform() === 'ios') {
866
1045
  void Keyboard.setAccessoryBarVisible({
@@ -870,6 +1049,27 @@ class CustomKeyboardService {
870
1049
  });
871
1050
  }
872
1051
  }
1052
+ /**
1053
+ * A panel docked at the bottom (date or time picker) takes the keyboard's place: the same body
1054
+ * class and height variable let the dialog end above it.
1055
+ */
1056
+ reserveBottomArea(panel) {
1057
+ // A picker that is not docked (the time picker at its field) needs no area down there.
1058
+ if (!panel) {
1059
+ this.releaseBottomArea();
1060
+ return;
1061
+ }
1062
+ const height = Math.ceil(panel.getBoundingClientRect().height);
1063
+ document.body.classList.add('vl-custom-keyboard-open');
1064
+ document.body.style.setProperty('--vl-custom-keyboard-height', `${height}px`);
1065
+ }
1066
+ releaseBottomArea() {
1067
+ if (this.activeSession()) {
1068
+ return;
1069
+ }
1070
+ document.body.classList.remove('vl-custom-keyboard-open');
1071
+ document.body.style.removeProperty('--vl-custom-keyboard-height');
1072
+ }
873
1073
  getActiveValue(type, targetName) {
874
1074
  const session = this.activeSession();
875
1075
  if (!session || session.type !== type) {
@@ -966,6 +1166,331 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
966
1166
  args: ['focusin', ['$event']]
967
1167
  }] } });
968
1168
 
1169
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07
1170
+ // - new: DateTimePickerService, the date and time picker behaviour for every container. It lived
1171
+ // in the dialog only, so the filter dates of a card, the form and the grid still opened the
1172
+ // calendar and the system keyboard together - the bug this work started from. One picker can be
1173
+ // open in the whole app, so its state belongs to one root service, not to each component:
1174
+ // tapping the field opens the toolkit keyboard, the calendar icon the calendar docked at the
1175
+ // bottom, the clock icon the compact time picker at its field, and the screen stays still.
1176
+ // - new: the docked calendar keeps the height of six week rows, so paging through months with
1177
+ // five and six weeks no longer moves it up and down
1178
+ class DateTimePickerService {
1179
+ keyboard;
1180
+ /** On the devices the date picker docks at the bottom like a keyboard; in a browser it drops down. */
1181
+ isNativeMobile = Capacitor.isNativePlatform() &&
1182
+ (Capacitor.getPlatform() === 'ios' || Capacitor.getPlatform() === 'android');
1183
+ /** panelStyleClass of a date picker. */
1184
+ datePanelClass = this.isNativeMobile
1185
+ ? 'model-datepicker-panel model-datepicker-panel--docked'
1186
+ : 'model-datepicker-panel';
1187
+ /** panelStyleClass of a time picker: compact and attached to its field everywhere. */
1188
+ timePanelClass = 'model-datepicker-panel model-timepicker-panel';
1189
+ /**
1190
+ * The docked date picker runs without PrimeNG's open/close animation. The animation scales the
1191
+ * wrapper around the panel, and a transformed wrapper becomes the reference of the panel's
1192
+ * position: fixed - the docked calendar flew up next to its field while closing.
1193
+ */
1194
+ dockedMotion = this.isNativeMobile ? { disabled: true } : undefined;
1195
+ activePicker = null;
1196
+ activePickerRequest = null;
1197
+ pickerOpening = false;
1198
+ lockedPickerInput = null;
1199
+ constructor(keyboard) {
1200
+ this.keyboard = keyboard;
1201
+ }
1202
+ /** (onShow) of a picker. */
1203
+ onShown() {
1204
+ this.pickerOpening = true;
1205
+ if (!this.isNativeMobile) {
1206
+ return;
1207
+ }
1208
+ /*
1209
+ * A docked panel stands where the keyboard would; the dialog ends above it. Measured one
1210
+ * frame later, once PrimeNG has laid the panel out. A picker at its field needs no area.
1211
+ */
1212
+ window.requestAnimationFrame(() => {
1213
+ const docked = document.querySelector('.model-datepicker-panel--docked');
1214
+ this.keepSixWeekHeight(docked);
1215
+ this.keyboard.reserveBottomArea(docked);
1216
+ window.requestAnimationFrame(() => this.scrollFieldAbovePicker());
1217
+ });
1218
+ }
1219
+ /** (onClose) of a picker; pass the picker so a late close of a replaced one is ignored. */
1220
+ onClose(picker) {
1221
+ /*
1222
+ * A picker that was closed because another one is opening reports onClose late, after
1223
+ * its leave animation. It must not cancel the request of the picker opening now.
1224
+ */
1225
+ if (picker && this.activePicker && picker !== this.activePicker) {
1226
+ return;
1227
+ }
1228
+ /*
1229
+ * Closed by a selection (not by a tap into the field, which unlocks the field first):
1230
+ * PrimeNG hands the focus back to the field, which then showed a focus frame without a
1231
+ * keyboard.
1232
+ */
1233
+ const lockedInput = this.lockedPickerInput?.input;
1234
+ if (lockedInput && document.activeElement === lockedInput) {
1235
+ lockedInput.blur();
1236
+ }
1237
+ this.activePicker = null;
1238
+ this.activePickerRequest = null;
1239
+ this.pickerOpening = false;
1240
+ this.unlockPickerInput();
1241
+ this.keyboard.endPickerOverlay();
1242
+ }
1243
+ /** (pointerdown) of the calendar or clock icon inside a .picker-field. */
1244
+ async openFromIcon(event, picker) {
1245
+ event.preventDefault();
1246
+ event.stopPropagation();
1247
+ event.stopImmediatePropagation();
1248
+ if (this.pickerOpening) {
1249
+ if (this.activePicker === picker) {
1250
+ return;
1251
+ }
1252
+ // Another picker is still open: close it and open the requested one.
1253
+ this.closeActivePicker();
1254
+ }
1255
+ const button = event.currentTarget;
1256
+ this.swallowShiftedClick(button);
1257
+ const pickerInput = button
1258
+ .closest('.picker-field')
1259
+ ?.querySelector('input');
1260
+ if (!pickerInput || pickerInput.disabled) {
1261
+ return;
1262
+ }
1263
+ /*
1264
+ * preventDefault keeps the focus where it was, so whatever field had it - an autocomplete
1265
+ * with its list and keyboard, say - would stay open next to the picker. Let go of it.
1266
+ */
1267
+ const focused = document.activeElement;
1268
+ if (focused instanceof HTMLElement && focused !== pickerInput) {
1269
+ focused.blur();
1270
+ }
1271
+ const request = Symbol('picker-open');
1272
+ this.activePicker = picker;
1273
+ this.activePickerRequest = request;
1274
+ this.pickerOpening = true;
1275
+ this.lockPickerInput(pickerInput);
1276
+ pickerInput.blur();
1277
+ this.keyboard.beginPickerOverlay();
1278
+ let keyboardDidHideListener;
1279
+ let resolveKeyboardHidden;
1280
+ const keyboardHidden = new Promise(resolve => {
1281
+ resolveKeyboardHidden = resolve;
1282
+ });
1283
+ try {
1284
+ if (Capacitor.isNativePlatform()) {
1285
+ keyboardDidHideListener = await Keyboard.addListener('keyboardDidHide', () => resolveKeyboardHidden?.());
1286
+ await Keyboard.hide().catch(() => {
1287
+ // The native keyboard may already be hidden.
1288
+ });
1289
+ await Promise.race([
1290
+ keyboardHidden,
1291
+ new Promise(resolve => {
1292
+ window.setTimeout(resolve, 500);
1293
+ })
1294
+ ]);
1295
+ await new Promise(resolve => {
1296
+ window.requestAnimationFrame(() => {
1297
+ window.requestAnimationFrame(() => resolve());
1298
+ });
1299
+ });
1300
+ }
1301
+ // The user may have tapped the input while Keyboard.hide() was pending.
1302
+ if (this.activePickerRequest !== request) {
1303
+ return;
1304
+ }
1305
+ if (Capacitor.isNativePlatform()) {
1306
+ window.dispatchEvent(new Event('resize'));
1307
+ }
1308
+ /*
1309
+ * PrimeNG 21 DatePicker has no show(); showOverlay() is the API. The picker is OnPush,
1310
+ * so it is rendered explicitly - otherwise the overlay only appeared on the next event
1311
+ * inside it, a tap on the input, together with a keyboard.
1312
+ */
1313
+ picker.showOverlay();
1314
+ picker.cd?.markForCheck();
1315
+ picker.cd?.detectChanges();
1316
+ }
1317
+ catch (error) {
1318
+ if (this.activePickerRequest === request) {
1319
+ this.activePicker = null;
1320
+ this.activePickerRequest = null;
1321
+ this.pickerOpening = false;
1322
+ this.unlockPickerInput();
1323
+ this.keyboard.endPickerOverlay();
1324
+ }
1325
+ console.warn('Unable to open date/time picker:', error);
1326
+ }
1327
+ finally {
1328
+ await keyboardDidHideListener?.remove().catch(() => {
1329
+ // Listener may already be removed.
1330
+ });
1331
+ }
1332
+ }
1333
+ /** (click) of the icon. */
1334
+ blockIconClick(event) {
1335
+ /*
1336
+ * No stopPropagation: the click has to reach the document, where PrimeNG closes other open
1337
+ * lists (autocomplete, dropdown) on an outside click.
1338
+ */
1339
+ event.preventDefault();
1340
+ }
1341
+ /** (pointerdown) of the p-datepicker: a tap into its field while a picker is open. */
1342
+ onFieldPointerDown(event, picker) {
1343
+ const target = event.target;
1344
+ if (!(target instanceof HTMLInputElement)) {
1345
+ return;
1346
+ }
1347
+ this.configureDateTimeInput(target);
1348
+ if (!this.pickerOpening) {
1349
+ return;
1350
+ }
1351
+ /*
1352
+ * Close the picker that is actually open and let this tap bring up the keyboard of the
1353
+ * field; the bottom area stays until the keyboard takes it, so the field does not move
1354
+ * away under the finger.
1355
+ */
1356
+ this.closeActivePicker(picker, { suspendReopen: false, keepBottomArea: true });
1357
+ }
1358
+ /** (onFocus) of the p-datepicker. */
1359
+ onFieldFocus(event) {
1360
+ const input = (event?.target ?? event?.originalEvent?.target);
1361
+ if (input?.tagName === 'INPUT' && this.lockedPickerInput?.input !== input) {
1362
+ this.configureDateTimeInput(input);
1363
+ }
1364
+ }
1365
+ /**
1366
+ * For ngOnDestroy of a container: a picker can still be open when its dialog or page goes away.
1367
+ * Without this the keyboard service keeps "picker open" and the reserved bottom area: no
1368
+ * toolkit keyboard opens any more and the next dialog is cut short above an empty strip.
1369
+ */
1370
+ closeIfWithin(container) {
1371
+ const input = this.lockedPickerInput?.input;
1372
+ const host = this.activePicker?.el?.nativeElement;
1373
+ if ((input && container.contains(input)) ||
1374
+ (host && container.contains(host))) {
1375
+ this.closeActivePicker();
1376
+ }
1377
+ }
1378
+ /**
1379
+ * A month has five or six week rows, so the docked calendar grew and shrank while paging
1380
+ * through the months and its upper edge went up and down. It keeps the height of six rows.
1381
+ */
1382
+ keepSixWeekHeight(panel) {
1383
+ const rows = panel?.querySelectorAll('.p-datepicker-day-view tbody tr');
1384
+ if (!panel || !rows?.length) {
1385
+ return;
1386
+ }
1387
+ const rowHeight = rows[0].getBoundingClientRect().height;
1388
+ const height = panel.getBoundingClientRect().height + (6 - rows.length) * rowHeight;
1389
+ panel.style.minHeight = `${Math.ceil(height)}px`;
1390
+ }
1391
+ /**
1392
+ * Scrolls the dialog so the field sits right above the docked picker: the eye and the thumb
1393
+ * move a short way from the value to the picker instead of across the whole screen.
1394
+ */
1395
+ scrollFieldAbovePicker() {
1396
+ const field = this.lockedPickerInput?.input;
1397
+ const content = field?.closest('.p-dialog-content');
1398
+ if (!field || !content) {
1399
+ return;
1400
+ }
1401
+ const spacing = 12;
1402
+ const fieldBottom = field.getBoundingClientRect().bottom;
1403
+ const contentBottom = content.getBoundingClientRect().bottom - spacing;
1404
+ content.scrollTop += fieldBottom - contentBottom;
1405
+ }
1406
+ configureDateTimeInput(input) {
1407
+ // On the devices the toolkit keyboard types into these fields; inputmode belongs to it.
1408
+ if (this.isNativeMobile) {
1409
+ return;
1410
+ }
1411
+ input.setAttribute('inputmode', 'numeric');
1412
+ input.setAttribute('pattern', '[0-9:]*');
1413
+ input.setAttribute('autocomplete', 'off');
1414
+ }
1415
+ lockPickerInput(input) {
1416
+ this.unlockPickerInput();
1417
+ this.lockedPickerInput = {
1418
+ input,
1419
+ originalReadOnly: input.readOnly,
1420
+ originalInputMode: input.getAttribute('inputmode')
1421
+ };
1422
+ // Prevent PrimeNG focus changes from reopening the native keyboard.
1423
+ input.readOnly = true;
1424
+ input.setAttribute('inputmode', 'none');
1425
+ // The field loses its focus to keep keyboards away; mark it so its picker stays traceable.
1426
+ input.closest('.picker-field')?.classList.add('picker-field--active');
1427
+ }
1428
+ unlockPickerInput() {
1429
+ const locked = this.lockedPickerInput;
1430
+ if (!locked) {
1431
+ return;
1432
+ }
1433
+ locked.input.closest('.picker-field')?.classList.remove('picker-field--active');
1434
+ locked.input.readOnly = locked.originalReadOnly;
1435
+ if (locked.originalInputMode === null) {
1436
+ locked.input.removeAttribute('inputmode');
1437
+ }
1438
+ else {
1439
+ locked.input.setAttribute('inputmode', locked.originalInputMode);
1440
+ }
1441
+ this.lockedPickerInput = null;
1442
+ }
1443
+ /**
1444
+ * The pointerdown on the icon closes the toolkit keyboard and the layout below the finger can
1445
+ * change. The click that follows lands wherever the finger is now - in the emulator it opened
1446
+ * the autocomplete list of the field that had moved under it. A click that no longer hits the
1447
+ * icon is dropped; one on the icon passes, so other lists still close on it.
1448
+ */
1449
+ swallowShiftedClick(button) {
1450
+ const handler = (click) => {
1451
+ document.removeEventListener('click', handler, true);
1452
+ window.clearTimeout(timer);
1453
+ if (click.target instanceof Node && button.contains(click.target)) {
1454
+ return;
1455
+ }
1456
+ click.preventDefault();
1457
+ click.stopImmediatePropagation();
1458
+ };
1459
+ const timer = window.setTimeout(() => document.removeEventListener('click', handler, true), 800);
1460
+ document.addEventListener('click', handler, true);
1461
+ }
1462
+ closeActivePicker(fallbackPicker, option = {}) {
1463
+ const openPicker = this.activePicker ?? fallbackPicker;
1464
+ this.activePicker = null;
1465
+ this.activePickerRequest = null;
1466
+ this.unlockPickerInput();
1467
+ this.pickerOpening = false;
1468
+ try {
1469
+ /*
1470
+ * Not hideOverlay(): it focuses the picker's own input, which would open a keyboard on
1471
+ * the field being left.
1472
+ */
1473
+ if (openPicker) {
1474
+ openPicker.overlayVisible = false;
1475
+ openPicker.clearTimePickerTimer?.();
1476
+ openPicker.cd?.markForCheck();
1477
+ }
1478
+ }
1479
+ catch {
1480
+ // Picker may already be closing.
1481
+ }
1482
+ this.keyboard.endPickerOverlay(option);
1483
+ }
1484
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: DateTimePickerService, deps: [{ token: CustomKeyboardService }], target: i0.ɵɵFactoryTarget.Injectable });
1485
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: DateTimePickerService, providedIn: 'root' });
1486
+ }
1487
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: DateTimePickerService, decorators: [{
1488
+ type: Injectable,
1489
+ args: [{
1490
+ providedIn: 'root'
1491
+ }]
1492
+ }], ctorParameters: () => [{ type: CustomKeyboardService }] });
1493
+
969
1494
  // Shuaib 2026-09-24
970
1495
  // - change: getLoginUser requires the selected project's Okta issuer.
971
1496
  // - new: reject a missing or non-HTTPS issuer before sending the request.
@@ -1158,6 +1683,10 @@ class UtilityService {
1158
1683
  return { key: property, productSchema: itemInfo.propertySchema[property] };
1159
1684
  }
1160
1685
  async setStorage(key, value) {
1686
+ if (this.isSecureKey(key)) {
1687
+ await SecureStorage.setItem(key, value);
1688
+ return;
1689
+ }
1161
1690
  if ((this.platform.ANDROID || this.platform.IOS) && !this.platform?.isBrowser) {
1162
1691
  await Preferences.set({ key, value });
1163
1692
  }
@@ -1166,6 +1695,20 @@ class UtilityService {
1166
1695
  }
1167
1696
  }
1168
1697
  async getStorage(key) {
1698
+ if (this.isSecureKey(key)) {
1699
+ const secured = await SecureStorage.getItem(key).catch(() => null);
1700
+ if (secured !== null) {
1701
+ return secured;
1702
+ }
1703
+ // Stored the old way: move it into the keychain.
1704
+ const legacy = (await Preferences.get({ key })).value ?? sessionStorage.getItem(key);
1705
+ if (legacy !== null) {
1706
+ await SecureStorage.setItem(key, legacy);
1707
+ await Preferences.remove({ key });
1708
+ sessionStorage.removeItem(key);
1709
+ }
1710
+ return legacy;
1711
+ }
1169
1712
  if ((this.platform.ANDROID || this.platform.IOS) && !this.platform?.isBrowser) {
1170
1713
  const { value } = await Preferences.get({ key });
1171
1714
  return value;
@@ -1175,8 +1718,20 @@ class UtilityService {
1175
1718
  return storage;
1176
1719
  }
1177
1720
  }
1721
+ /**
1722
+ * Keys that hold a saved sign-in; on the devices they live in the keychain / keystore.
1723
+ * keycloak: KeycloakInfo, oktaAuth: loginedUser, jwtToken: <project>Logined.
1724
+ */
1725
+ secureKeys = new Set(['KeycloakInfo', 'loginedUser']);
1726
+ isSecureKey(key) {
1727
+ return Capacitor.isNativePlatform() && (this.secureKeys.has(key) || key.endsWith('Logined'));
1728
+ }
1178
1729
  async setLocalStorage(key, value) {
1179
1730
  const stringValue = JSON.stringify(value);
1731
+ if (this.isSecureKey(key)) {
1732
+ await SecureStorage.setItem(key, stringValue);
1733
+ return;
1734
+ }
1180
1735
  if ((this.platform.ANDROID || this.platform.IOS) && !this.platform?.isBrowser) {
1181
1736
  await Preferences.set({ key, value: stringValue });
1182
1737
  }
@@ -1185,6 +1740,13 @@ class UtilityService {
1185
1740
  }
1186
1741
  }
1187
1742
  async removeLocalStorage(keyName) {
1743
+ if (this.isSecureKey(keyName)) {
1744
+ await SecureStorage.removeItem(keyName);
1745
+ // Also the place it was kept before, so nothing stays behind there.
1746
+ localStorage.removeItem(keyName);
1747
+ await Preferences.remove({ key: keyName });
1748
+ return;
1749
+ }
1188
1750
  if ((this.platform.ANDROID || this.platform.IOS) && !this.platform?.isBrowser) {
1189
1751
  await Preferences.remove({ key: keyName });
1190
1752
  }
@@ -1193,6 +1755,12 @@ class UtilityService {
1193
1755
  }
1194
1756
  }
1195
1757
  async removeStorage(key) {
1758
+ if (this.isSecureKey(key)) {
1759
+ await SecureStorage.removeItem(key);
1760
+ await Preferences.remove({ key });
1761
+ sessionStorage.removeItem(key);
1762
+ return;
1763
+ }
1196
1764
  if ((this.platform.ANDROID || this.platform.IOS) && !this.platform?.isBrowser) {
1197
1765
  await Preferences.remove({ key });
1198
1766
  }
@@ -1200,7 +1768,25 @@ class UtilityService {
1200
1768
  sessionStorage.removeItem(key);
1201
1769
  }
1202
1770
  }
1771
+ /** Reads a credential key from the keychain, moving a value stored the old way on first read. */
1772
+ async getSecureItem(key) {
1773
+ const secured = await SecureStorage.getItem(key).catch(() => null);
1774
+ if (secured) {
1775
+ return JSON.parse(secured);
1776
+ }
1777
+ const legacy = localStorage.getItem(key) ?? (await Preferences.get({ key })).value;
1778
+ if (!legacy) {
1779
+ return null;
1780
+ }
1781
+ await SecureStorage.setItem(key, legacy);
1782
+ localStorage.removeItem(key);
1783
+ await Preferences.remove({ key });
1784
+ return JSON.parse(legacy);
1785
+ }
1203
1786
  async getLocalStorage(key) {
1787
+ if (this.isSecureKey(key)) {
1788
+ return this.getSecureItem(key);
1789
+ }
1204
1790
  if ((this.platform.ANDROID || this.platform.IOS) && !this.platform?.isBrowser) {
1205
1791
  const { value } = await Preferences.get({ key });
1206
1792
  return value ? JSON.parse(value) : null;
@@ -1625,6 +2211,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
1625
2211
  }]
1626
2212
  }], ctorParameters: () => [{ type: i1.Platform }, { type: RequestService }] });
1627
2213
 
2214
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-29
2215
+ // - change: getMediaUrlFromCouchDb removed. It asked /media-token for a signed URL, built
2216
+ // before the decision of 2026-09-21: an application has its own sign-in, unrelated to our
2217
+ // realm, and read access to CouchDB is open to anyone holding a tenant code. The library
2218
+ // therefore has no token and gets none, so media cannot be secured that way. Media are read
2219
+ // like any other document - GET with the tenant header - and Traefik routes them to the
2220
+ // cache. Nothing called this function; leaving it invited someone to wire it in
1628
2221
  // Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-18
1629
2222
  // - change: getFileContentFromCouchDb now treats anything not known to be text as binary. The
1630
2223
  // rule was the other way round - an allow-list of media extensions, everything else decoded as
@@ -1633,9 +2226,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
1633
2226
  // can be uploaded into the Media folder, that list could never be complete
1634
2227
  // - new: isTextFile, holding the inverted rule. SVG stays binary on purpose although it is text,
1635
2228
  // because the menu icons expect the media object with a src
1636
- // - new: getMediaUrlFromCouchDb, which asks for a signed URL instead of reading the file, so a
1637
- // media element can fetch it itself with Range requests. Nothing calls it yet - which of the
1638
- // image resolution paths should use it is still open
1639
2229
  class Couchdb {
1640
2230
  utilityService;
1641
2231
  platform;
@@ -1904,53 +2494,6 @@ class Couchdb {
1904
2494
  const mimeType = doc.contentType || 'image/png';
1905
2495
  return `data:${mimeType};base64,${doc.data}`;
1906
2496
  }
1907
- /**
1908
- * Returns a URL that a <video>, <audio> or <img> element can load directly.
1909
- *
1910
- * Unlike getImageSrcFromCouchDb above, this never reads the file. It asks
1911
- * jwt-auth for a short-lived signed URL and hands that back, so the browser
1912
- * fetches the attachment itself - with Range requests, so a video starts on
1913
- * the first chunk and a seek costs one request instead of the whole file.
1914
- *
1915
- * The reason a signed URL is needed at all: a media element issues its own
1916
- * HTTP requests, for the initial load and again for every seek, and those
1917
- * requests carry none of the tenant headers buildTenantHeaders adds here.
1918
- * The token in the query string is the only credential the element can pass.
1919
- *
1920
- * The path comes back from the endpoint and is used as given. The signature
1921
- * covers the raw, still-encoded path, so rebuilding it here with a different
1922
- * encoding would produce a URL that fails with a bare 403.
1923
- */
1924
- async getMediaUrlFromCouchDb(couchDbName, filePath, attachmentName) {
1925
- const dbName = couchDbName?.toLowerCase();
1926
- const docId = filePath?.trim();
1927
- if (!docId) {
1928
- throw new Error('Missing file path');
1929
- }
1930
- // The upload stores the attachment under the file name, so the last
1931
- // segment of the document path is the default (see upsertFileToCouchDb).
1932
- const name = attachmentName?.trim() || docId.split('/').pop() || '';
1933
- if (!name) {
1934
- throw new Error(`Could not derive an attachment name from "${docId}"`);
1935
- }
1936
- const response = await fetch(`${this.couchDbBaseUrl}/media-token`, {
1937
- method: 'POST',
1938
- headers: {
1939
- Accept: 'application/json',
1940
- 'Content-Type': 'application/json',
1941
- ...(await this.buildTenantHeaders()),
1942
- },
1943
- body: JSON.stringify({ db: dbName, path: docId, name }),
1944
- });
1945
- const result = await this.parseJsonOrText(response);
1946
- if (!response.ok) {
1947
- throw new Error(`Failed to sign media URL for "${docId}" in "${dbName}": ${response.status} ${this.stringifyErrorDetails(result)}`);
1948
- }
1949
- if (!result?.path || !result?.token) {
1950
- throw new Error(`Media token response for "${docId}" is incomplete`);
1951
- }
1952
- return `${this.couchDbBaseUrl}${result.path}?token=${encodeURIComponent(result.token)}`;
1953
- }
1954
2497
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: Couchdb, deps: [{ token: UtilityService }, { token: i1.Platform }], target: i0.ɵɵFactoryTarget.Injectable });
1955
2498
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: Couchdb, providedIn: 'root' });
1956
2499
  }
@@ -1961,6 +2504,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
1961
2504
  }]
1962
2505
  }], ctorParameters: () => [{ type: UtilityService }, { type: i1.Platform }] });
1963
2506
 
2507
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07
2508
+ // - new: getKeycloakMobileConfig passes login.mobile.offlineAccess on
1964
2509
  class ConfigService {
1965
2510
  couchdbService;
1966
2511
  utilityService;
@@ -2093,7 +2638,9 @@ class ConfigService {
2093
2638
  redirectUri: this.keycloakConfig.redirectUri,
2094
2639
  logoutRedirectUri: this.keycloakConfig.logoutRedirectUri,
2095
2640
  scope: this.keycloakConfig.scope,
2096
- responseType: this.keycloakConfig.responseType
2641
+ responseType: this.keycloakConfig.responseType,
2642
+ // login.mobile.offlineAccess: request an offline token (see MobileKeycloakService.login)
2643
+ offlineAccess: this.keycloakConfig.offlineAccess === true
2097
2644
  };
2098
2645
  }
2099
2646
  updateLoaderValue = new BehaviorSubject(null);
@@ -2138,6 +2685,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
2138
2685
  }]
2139
2686
  }], ctorParameters: () => [{ type: i1$2.ToastrService }] });
2140
2687
 
2688
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07
2689
+ // - change: the date filter uses DateTimePickerService like the dialog. Tapping it opened the
2690
+ // calendar and the system number keyboard at the same time (seen on "Gebuchte Zeiterfassung")
2141
2691
  // Shuaib 2026-09-24
2142
2692
  // - new: renderFieldType maps legacy checkbox fields to boolean so existing cards continue to render.
2143
2693
  class CardComponent {
@@ -2151,6 +2701,9 @@ class CardComponent {
2151
2701
  notificationService;
2152
2702
  oauthService;
2153
2703
  requestService;
2704
+ /** Date and time picker behaviour shared by all containers. */
2705
+ picker = inject(DateTimePickerService);
2706
+ pickerHost = inject((ElementRef));
2154
2707
  /** Keep existing checkbox data objects usable; new objects use boolean. */
2155
2708
  renderFieldType(type) {
2156
2709
  return type === 'checkbox' ? 'boolean' : type;
@@ -2300,6 +2853,7 @@ class CardComponent {
2300
2853
  }
2301
2854
  ngOnDestroy() {
2302
2855
  this.destroyScrollObserver();
2856
+ this.picker.closeIfWithin(this.pickerHost.nativeElement);
2303
2857
  }
2304
2858
  ngAfterViewInit() {
2305
2859
  this.setupScrollObserver();
@@ -2859,7 +3413,7 @@ class CardComponent {
2859
3413
  return scope.substring(start, end);
2860
3414
  }
2861
3415
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: CardComponent, deps: [{ token: UtilityService }, { token: i1.Platform }, { token: i1$1.HttpClient }, { token: ConfigService }, { token: i0.ChangeDetectorRef }, { token: i0.NgZone }, { token: i5$1.NzModalService }, { token: NotificationService }, { token: i7.OAuthService }, { token: RequestService }], target: i0.ɵɵFactoryTarget.Component });
2862
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.2", type: CardComponent, isStandalone: true, selector: "vl-card", inputs: { itemInfo: "itemInfo", currentPage: "currentPage", pageSize: "pageSize", selectedLanguageInfo: "selectedLanguageInfo", mobileDisplay: "mobileDisplay", scrollPagination: "scrollPagination", pages: "pages", paginationMode: "paginationMode", paginationLoading: "paginationLoading" }, outputs: { cardButtonClick: "cardButtonClick", cardHeaderButtonClick: "cardHeaderButtonClick", linkDetails: "linkDetails", imageDetails: "imageDetails", onPageSizeChange: "onPageSizeChange", onPageChange: "onPageChange", cardDataChange: "cardDataChange" }, host: { listeners: { "window:resize": "onWindowResize()" } }, viewQueries: [{ propertyName: "scrollPaginationSentinel", first: true, predicate: ["scrollPaginationSentinel"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<!-- shuaib 2026-09-24\r\n - change: card field switches use renderFieldType so legacy checkbox fields render as boolean. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - new: a @contract comment naming the types this container does not render. It carries four\r\n ngSwitch blocks, so the gap belongs to the container and not to one of them -->\r\n\r\n<!-- @contract gap: dateRange, quantity, credit card\r\n reality: a card renders string, number, image, date, time, dropdown, boolean and\r\n autocomplete. Legacy checkbox fields normalize to boolean. dropdown and boolean arrived on 2026-09-18; before that a card\r\n dropped them silently.\r\n failure: a field of a missing type renders empty, with no error.\r\n instead: quantity is grid-only by design; for a dateRange or a credit card use a modal.\r\n seen: this file carries four switch blocks (lines 23, 94, 267, 488) and none of them has\r\n a case for those three - the gap belongs to the container, not to one block -->\r\n\r\n<!-- Loader Spinner -->\r\n\r\n<div *ngIf=\"isLoading\" class=\"loader-overlay\">\r\n <div class=\"spinner\"></div>\r\n</div>\r\n<div class=\"vl-card-runtime\">\r\n <!-- top pagination on card -->\r\n <div *ngIf=\"\r\n itemInfo?.layoutSchema?.pagination?.visible &&\r\n itemInfo?.layoutSchema?.pagination?.top\r\n \" class=\"card-pagination-wrapper\">\r\n <vl-pagination [itemInfo]=\"itemInfo\"\r\n [selectedLanguageInfo]=\"selectedLanguageInfo\" [pageSize]=\"pageSize\"\r\n [currentPage]=\"currentPage\" [pages]=\"pages\"\r\n (pageSizeChange)=\"invokeOnPageSizeChange($event)\"\r\n (onGoToPage)=\"invokeGoToPage($event)\">\r\n </vl-pagination>\r\n </div>\r\n <div *ngIf=\"itemInfo?.header?.elements?.length\" [ngStyle]=\"getHeaderStyle()\"\r\n [class]=\"getStyleWithoutClasses(\r\n itemInfo?.header?.style\r\n ).classes\">\r\n <div *ngFor=\"let element of itemInfo.header?.elements; let c = index\"\r\n [ngStyle]=\"getHeaderElementWrapperStyle(element)\"\r\n [class]=\"element.option?.innerAction && element.option?.innerAction === 'saveAll' && !displayedRecords?.length ? \r\n 'hidden' :getStyleWithoutClasses(element?.wrapperStyle).classes\">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(element.scope?.reference && itemInfo.propertySchema[getScope(element.scope?.reference)] ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.type : 'default')\">\r\n\r\n <!-- String Control -->\r\n <div *ngSwitchCase=\"'string'\"\r\n [class]=\"element.option?.visible ? 'card-field-wrapper' : 'hidden'\">\r\n\r\n <!-- LABEL -->\r\n <span *ngIf=\"element.label?.caption\"\r\n [style]=\"getStyleWithoutClasses(element.label?.style).style\"\r\n [class]=\"element.label?.style?.classes?.[0]\">\r\n {{ element.label.caption }}\r\n </span>\r\n </div>\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"mb-3\">\r\n <span *ngIf=\"element.option.protected\"\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">\r\n <vl-string [formField]=\"element\"\r\n [modalItemInfo]=\"itemInfo\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!element.option.protected\"\r\n [class]=\"element?.layout == 'horizontal' ? 'horizontal-layout' : ''\"\r\n [ngClass]=\"element?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(element?.style).style\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">{{\r\n element.label?.value\r\n ?\r\n element.label?.value : element.label?.caption ?\r\n element.label?.caption :\r\n itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <p-datepicker\r\n (ngModelChange)=\"dateChangeEvent(element, $event)\"\r\n [showIcon]=\"true\" type=\"number\"\r\n inputmode=\"numeric\"\r\n [dateFormat]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.format ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()].format : 'yy-mm-dd'\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()].value\"\r\n (onFocus)=\"forceNumericKeyboard($event)\"\r\n (onInput)=\"onDateTyping($event)\"\r\n [class]=\"element?.scope?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(element?.scope?.style).style\">\r\n </p-datepicker>\r\n </div>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\"\r\n [class]=\"element.option?.visible ? 'row' : 'hidden'\"\r\n (click)=\"invokeOnCardHeader(element)\">\r\n <button nz-button type=\"button\"\r\n [matTooltip]=\"element?.option?.tooltip || ''\"\r\n [matTooltipTouchGestures]=\"'auto'\"\r\n [ngStyle]=\"getStyleWithoutClasses(element.style).style\"\r\n [class]=\"element.option.protected ? 'disabled-button' : getStyleWithoutClasses(element.style).classes\">\r\n <ng-container\r\n *ngIf=\"!element.caption; else headerTextContent\">\r\n <i nz-icon [nzType]=\"element.icon\"\r\n nzTheme=\"outline\"></i>\r\n </ng-container>\r\n <ng-template #headerTextContent>{{ element.caption\r\n }}</ng-template>\r\n </button>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n <div class=\"card-responsive-grid\" [ngStyle]=\"getCardContainerStyle()\"\r\n [class]=\"getStyleWithoutClasses(\r\n itemInfo?.layoutSchema?.style\r\n ).classes\">\r\n <div *ngFor=\"let record of displayedRecords; let k = index\"\r\n class=\"card-grid-item\">\r\n <div [ngStyle]=\"getSingleCardStyle()\"\r\n [class]=\"getStyleWithoutClasses(itemInfo?.layoutSchema?.stylePerCard).classes\">\r\n <div *ngFor=\"let layout of itemInfo?.layoutSchema?.cards || []\"\r\n class=\"card-layout-item\" [ngStyle]=\"\r\n layout?.type === 'HorizontalLayout'\r\n ? {}\r\n : getStyleWithoutClasses(layout?.style).style\r\n \" [class]=\"\r\n layout?.type === 'HorizontalLayout'\r\n ? ''\r\n : getStyleWithoutClasses(layout?.style).classes\r\n \">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(layout?.scope?.reference && itemInfo.propertySchema[getScope(layout.scope?.reference)] ? itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.type : layout?.type == 'HorizontalLayout' ? 'HorizontalLayout' : 'default')\">\r\n <!-- Image Control -->\r\n <div *ngSwitchCase=\"'image'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <img\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [src]=\"layout?.option?.src ? layout?.option?.src : record[layout.scope?.reference.split('/').pop()]\"\r\n alt=\"{{ layout.label ? layout.label : getProperty(layout.scope?.reference).productSchema.caption }}\"\r\n (click)=\"showImageDetails(layout.option, record)\" />\r\n </div>\r\n\r\n <!-- String Control -->\r\n <div *ngSwitchCase=\"'string'\"\r\n [class]=\"layout.option?.visible ? 'card-field-wrapper' : 'hidden'\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(layout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(layout.label?.style).classes\">\r\n {{ getHorizontalLayoutCaption(layout) }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <p\r\n *ngIf=\"layout.option?.protected && layout.option?.visible\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n (click)=\"showLinkDetails(layout.option, record)\">\r\n {{\r\n record?.[layout.scope?.reference.split('/').pop()]\r\n ??\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)]?.[layout.scope?.reference.split('/').pop()]?.default\r\n ??\r\n '--'\r\n }}\r\n </p>\r\n </div>\r\n\r\n <!-- Date Control -->\r\n <div *ngSwitchCase=\"'date'\"\r\n [class]=\"layout.option?.visible ? 'card-field-wrapper' : 'hidden'\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <!-- LABEL -->\r\n <span *ngIf=\"layout?.label?.caption\"\r\n [class]=\"layout.label?.style?.classes?.[0]\">\r\n {{ layout.label.caption }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <p\r\n *ngIf=\"layout.option?.protected && layout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n (click)=\"showLinkDetails(layout.option, record)\">\r\n {{\r\n record?.[layout.scope?.reference.split('/').pop()]\r\n ?? itemInfo.propertySchema[\r\n getScope(layout.scope?.reference)\r\n ]?.[layout.scope?.reference.split('/').pop()]?.default\r\n ?? '--'\r\n }}\r\n </p>\r\n </div>\r\n\r\n <!-- Time Control -->\r\n <div *ngSwitchCase=\"'time'\"\r\n [class]=\"layout.option?.visible ? 'card-field-wrapper' : 'hidden'\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span *ngIf=\"layout?.label?.caption\"\r\n [class]=\"layout.label?.style?.classes?.[0]\">\r\n {{ layout.label.caption }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <p\r\n *ngIf=\"layout.option?.protected && layout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n (click)=\"showLinkDetails(layout.option, record)\">\r\n {{\r\n record?.[layout.scope?.reference.split('/').pop()]\r\n ?? itemInfo.propertySchema[\r\n getScope(layout.scope?.reference)\r\n ]?.[layout.scope?.reference.split('/').pop()]?.default\r\n ?? '--'\r\n }}\r\n </p>\r\n </div>\r\n\r\n <!-- number Control -->\r\n <div *ngSwitchCase=\"'number'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.format.currency\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n {{\r\n record[layout.scope?.reference.split('/').pop()]\r\n | currency: layout.option?.currency\r\n ?\r\n layout.option.currency\r\n :\r\n 'USD' }}\r\n </h3>\r\n </div>\r\n\r\n <!-- dropdown Control -->\r\n <div *ngSwitchCase=\"'dropdown'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.format.currency\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <select id=\"dynamic-dropdown\"\r\n #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(layout?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style)?.style\"\r\n [name]=\"layout.scope?.reference\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)][\r\n layout.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of itemInfo.propertySchema[getScope(layout.scope?.reference)]?.[\r\n layout.scope?.reference.split('/').pop()\r\n ]?.data\r\n \"\r\n [value]=\"item[layout?.option?.optionValue]\">\r\n {{ item[layout?.option?.optionLabel] }}\r\n </option>\r\n </select>\r\n </h3>\r\n </div>\r\n\r\n <!-- autocomplete Control -->\r\n <div *ngSwitchCase=\"'autocomplete'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.format.currency\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <p-autocomplete #autocompleteField=\"ngModel\"\r\n [required]=\"layout.option.required\"\r\n [name]=\"layout.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [class]=\"layout?.scope?.style?.classes ? layout?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"layout.option.autocomplete.response.label\"\r\n [placeholder]=\"itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.placeholder\"\r\n [vlCustomKeyboard]=\"layout?.option?.keyboardType\">\r\n </p-autocomplete>\r\n </h3>\r\n </div>\r\n\r\n <!-- checkbox Control -->\r\n <div *ngSwitchCase=\"'boolean'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <input type=\"checkbox\"\r\n class=\"form-check-input mr-2\" [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)][\r\n layout.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [name]=\"layout.scope?.reference\" />\r\n </h3>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <button\r\n [style]=\"getStyleWithoutClasses(layout?.style).style\"\r\n [class]=\"layout.option.protected ? 'disabled-button' : getStyleWithoutClasses(layout?.style).classes\"\r\n (click)=\"\r\n invokeButtonClick(\r\n layout,\r\n record,\r\n getRecordIndex(record)\r\n )\r\n\">\r\n <ng-container\r\n *ngIf=\"!layout.caption; else textContent\">\r\n <i nz-icon [nzType]=\"layout.icon\"\r\n nzTheme=\"outline\"></i>\r\n </ng-container>\r\n <ng-template #textContent>\r\n {{ layout.caption }}\r\n </ng-template>\r\n </button>\r\n </div>\r\n\r\n <!-- HorizontalLayout Control -->\r\n <div *ngSwitchCase=\"'HorizontalLayout'\">\r\n <div class=\"card-horizontal-layout\"\r\n [class]=\"getStyleWithoutClasses(\r\n layout?.style\r\n ).classes\" [ngStyle]=\"getHorizontalLayoutStyle(layout)\">\r\n <ng-container\r\n *ngFor=\"let innerLayout of layout.elements\">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(innerLayout.scope?.reference && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] ? itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.type : 'default')\">\r\n <!-- Image Control -->\r\n <div *ngSwitchCase=\"'image'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n <img\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n [src]=\"innerLayout?.option?.src ? innerLayout?.option?.src : record[innerLayout.scope?.reference.split('/').pop()]\"\r\n alt=\"{{ innerLayout.label ? innerLayout.label : getProperty(innerLayout.scope?.reference).productSchema.caption }}\"\r\n (click)=\"showImageDetails(innerLayout.option, record)\" />\r\n </div>\r\n\r\n <!-- String Control -->\r\n <div *ngSwitchCase=\"'string'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(innerLayout.label?.style).classes\">\r\n {{\r\n getHorizontalLayoutCaption(innerLayout)\r\n }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <h3\r\n *ngIf=\"innerLayout.option?.protected && innerLayout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n (click)=\"showLinkDetails(innerLayout.option, record)\">\r\n\r\n <!-- DATE FORMAT -->\r\n <ng-container\r\n *ngIf=\"innerLayout.option?.dateFormat; else checkInnerTime\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n | date:\r\n innerLayout.option.dateFormat)\r\n : '--' }}\r\n </ng-container>\r\n\r\n <!-- TIME FORMAT -->\r\n <ng-template #checkInnerTime>\r\n <ng-container\r\n *ngIf=\"innerLayout.option?.timeFormat; else normalInnerValue\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n |\r\n date:\r\n innerLayout.option.timeFormat)\r\n : '--' }}\r\n </ng-container>\r\n </ng-template>\r\n\r\n <!-- NORMAL STRING -->\r\n <ng-template #normalInnerValue>\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ??\r\n '--' }}\r\n </ng-template>\r\n </h3>\r\n </div>\r\n\r\n <!-- number Control -->\r\n <div *ngSwitchCase=\"'number'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.format?.currency\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n {{\r\n record[innerLayout.scope?.reference.split('/').pop()]\r\n | currency:\r\n innerLayout.option?.currency ?\r\n innerLayout.option.currency :\r\n 'USD'\r\n }}\r\n </h3>\r\n </div>\r\n\r\n <!-- Date Control -->\r\n <div *ngSwitchCase=\"'date'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(innerLayout.label?.style).classes\">\r\n {{\r\n getHorizontalLayoutCaption(innerLayout)\r\n }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <h3\r\n *ngIf=\"innerLayout.option?.protected && innerLayout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n (click)=\"showLinkDetails(innerLayout.option, record)\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n | date:\r\n innerLayout.option?.dateFormat ?\r\n innerLayout.option?.dateFormat :\r\n itemInfo.propertySchema[getScope(innerLayout.scope?.reference)]?.[innerLayout.scope?.reference.split('/').pop()]?.format\r\n || 'yyyy-MM-dd') : '--' }}\r\n </h3>\r\n </div>\r\n\r\n <!-- Time Control -->\r\n <div *ngSwitchCase=\"'time'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(innerLayout.label?.style).classes\">\r\n {{\r\n getHorizontalLayoutCaption(innerLayout)\r\n }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <h3\r\n *ngIf=\"innerLayout.option?.protected && innerLayout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n (click)=\"showLinkDetails(innerLayout.option, record)\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n | date:\r\n innerLayout.option?.timeFormat ?\r\n innerLayout.option?.timeFormat\r\n :itemInfo.propertySchema[getScope(innerLayout.scope?.reference)]?.[innerLayout.scope?.reference.split('/').pop()]?.format\r\n || 'HH:mm') : '--' }}\r\n </h3>\r\n </div>\r\n\r\n <!-- dropdown Control -->\r\n <div *ngSwitchCase=\"'dropdown'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.format.currency\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n <select id=\"dynamic-dropdown\"\r\n #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(innerLayout?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style)?.style\"\r\n [name]=\"innerLayout.scope?.reference\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][\r\n innerLayout.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of itemInfo.propertySchema[getScope(innerLayout.scope?.reference)]?.[\r\n innerLayout.scope?.reference.split('/').pop()\r\n ]?.data\r\n \"\r\n [value]=\"item[innerLayout?.option?.optionValue]\">\r\n {{\r\n item[innerLayout?.option?.optionLabel]\r\n }}\r\n </option>\r\n </select>\r\n </h3>\r\n </div>\r\n\r\n <!-- autocomplete Control -->\r\n <div *ngSwitchCase=\"'autocomplete'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.format.currency\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n <p-autocomplete\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"innerLayout.option.required\"\r\n [name]=\"innerLayout.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n [class]=\"innerLayout?.scope?.style?.classes ? innerLayout?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"innerLayout.option.autocomplete.response.label\"\r\n [placeholder]=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.placeholder\"\r\n [vlCustomKeyboard]=\"innerLayout?.option?.keyboardType\">\r\n </p-autocomplete>\r\n </h3>\r\n </div>\r\n\r\n <!-- checkbox Control -->\r\n <div *ngSwitchCase=\"'boolean'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n <input type=\"checkbox\"\r\n class=\"form-check-input mr-2\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][\r\n innerLayout.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [name]=\"innerLayout.scope?.reference\" />\r\n </h3>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button nz-button\r\n [matTooltip]=\"getHorizontalLayoutTooltip(innerLayout)\"\r\n [matTooltipTouchGestures]=\"'auto'\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.style).style\"\r\n [class]=\"innerLayout.option?.visible ? innerLayout.option.protected ? 'disabled-button' : getStyleWithoutClasses(innerLayout?.style).classes : 'hidden'\"\r\n (click)=\"\r\n invokeButtonClick(\r\n innerLayout,\r\n record,\r\n getRecordIndex(record)\r\n )\r\n\">\r\n <span\r\n class=\"schema-btn-content\">\r\n <i\r\n *ngIf=\"showButtonIcon(innerLayout)\"\r\n nz-icon\r\n [nzType]=\"innerLayout.icon\"\r\n nzTheme=\"outline\">\r\n </i>\r\n\r\n <span\r\n *ngIf=\"showButtonCaption(innerLayout)\">\r\n {{ innerLayout.caption\r\n }}\r\n </span>\r\n\r\n <span *ngIf=\"\r\n !showButtonIcon(innerLayout) &&\r\n !showButtonCaption(innerLayout)\r\n \">\r\n Button\r\n </span>\r\n </span>\r\n </button>\r\n </div>\r\n\r\n </ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <div\r\n *ngIf=\"itemInfo?.footer?.elements && itemInfo?.data && itemInfo?.data.length\"\r\n [class]=\"getStyleWithoutClasses(itemInfo?.footer?.style).classes\">\r\n <span *ngFor=\"let element of itemInfo.footer?.elements; let c = index\">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(element.scope?.reference && itemInfo.propertySchema[getScope(element.scope?.reference)] ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.type : 'default')\">\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"element.option.protected\"\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">\r\n <h5>{{\r\n itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.caption\r\n }}\r\n </h5>\r\n </span>\r\n <div *ngIf=\"!element.option.protected\">\r\n <div\r\n [class]=\"element?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">{{\r\n element.label?.value\r\n ?\r\n element.label?.value : element.label?.caption ?\r\n element.label?.caption :\r\n itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n\r\n <nz-date-picker [required]=\"element.option.required\"\r\n [name]=\"element.scope?.reference\"\r\n [nzDisabled]=\"element?.option?.disabled\"\r\n (ngModelChange)=\"dateChangeEvent(element, $event)\"\r\n [nzFormat]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.format ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.format : 'yyyy-MM-dd'\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(element?.style).style\"></nz-date-picker>\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\"\r\n [class]=\"element.option?.visible ? 'row' : 'hidden'\"\r\n (click)=\"invokeOnCardFooter(element)\">\r\n <button nz-button\r\n [style]=\"getStyleWithoutClasses(element.style).style\"\r\n [class]=\"element.option.protected ? 'disabled-button' : getStyleWithoutClasses(element.style).classes\">\r\n <ng-container\r\n *ngIf=\"!element.caption; else textContent\">\r\n <i nz-icon [nzType]=\"element.icon\"\r\n nzTheme=\"outline\"></i>\r\n </ng-container>\r\n <ng-template #textContent>\r\n {{ element.caption }}\r\n </ng-template>\r\n </button>\r\n </div>\r\n </ng-container>\r\n </span>\r\n </div>\r\n <!-- bottom pagination on card -->\r\n <div *ngIf=\"\r\n itemInfo?.data.length && \r\n itemInfo?.layoutSchema?.pagination?.visible &&\r\n !itemInfo?.layoutSchema?.pagination?.top &&\r\n !scrollPagination\r\n \" class=\"card-pagination-wrapper\">\r\n <vl-pagination [itemInfo]=\"itemInfo\"\r\n [selectedLanguageInfo]=\"selectedLanguageInfo\" [pageSize]=\"pageSize\"\r\n [currentPage]=\"currentPage\" [pages]=\"pages\"\r\n (pageSizeChange)=\"invokeOnPageSizeChange($event)\"\r\n (onGoToPage)=\"invokeGoToPage($event)\">\r\n </vl-pagination>\r\n </div>\r\n\r\n @if (scrollPagination) {\r\n <div #scrollPaginationSentinel class=\"scroll-pagination-sentinel\"\r\n aria-hidden=\"true\">\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block;width:100%;min-width:0}.vl-card-runtime{min-width:0;box-sizing:border-box}.card-responsive-grid{width:100%;min-width:0;box-sizing:border-box}.scroll-pagination-sentinel{width:100%;height:1px}.card-grid-item,.card-layout-item,.card-horizontal-layout,.card-field-wrapper{width:100%;min-width:0;box-sizing:border-box}.card-field-wrapper{overflow-wrap:anywhere}.card-field-wrapper p,.card-field-wrapper h3{margin:0;min-width:0}.card-layout-item img{display:block;max-width:100%;height:auto}.card-layout-item button{max-width:100%;box-sizing:border-box}.schema-btn-content{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:0}.hidden{display:none!important}.card-pagination-wrapper{width:100%;min-width:0;box-sizing:border-box;padding:0 10px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i5.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i5.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: NzIconModule }, { kind: "directive", type: i11.NzIconDirective, selector: "nz-icon,[nz-icon]", inputs: ["nzSpin", "nzRotate", "nzType", "nzTheme", "nzTwotoneColor", "nzIconfont"], exportAs: ["nzIcon"] }, { kind: "ngmodule", type: DatePickerModule }, { kind: "component", type: i12.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: NzButtonModule }, { kind: "component", type: i13.NzButtonComponent, selector: "button[nz-button], a[nz-button]", inputs: ["nzBlock", "nzGhost", "nzSearch", "nzLoading", "nzDanger", "disabled", "tabIndex", "nzType", "nzShape", "nzSize"], exportAs: ["nzButton"] }, { kind: "directive", type: i14.ɵNzTransitionPatchDirective, selector: "[nz-button], [nz-icon], nz-icon, [nz-menu-item], [nz-submenu], nz-select-top-control, nz-select-placeholder, nz-input-group", inputs: ["hidden"] }, { kind: "directive", type: i15.NzWaveDirective, selector: "[nz-wave],button[nz-button]:not([nzType=\"link\"]):not([nzType=\"text\"])", inputs: ["nzWaveExtraNode"], exportAs: ["nzWave"] }, { kind: "ngmodule", type: NzDatePickerModule }, { kind: "component", type: i4.NzDatePickerComponent, selector: "nz-date-picker,nz-week-picker,nz-month-picker,nz-quarter-picker,nz-year-picker,nz-range-picker", inputs: ["nzInline", "nzAllowClear", "nzAutoFocus", "nzDisabled", "nzInputReadOnly", "nzOpen", "nzDisabledDate", "nzLocale", "nzPlaceHolder", "nzPopupStyle", "nzDropdownClassName", "nzSize", "nzStatus", "nzFormat", "nzVariant", "nzDateRender", "nzDisabledTime", "nzRenderExtraFooter", "nzShowToday", "nzMode", "nzShowNow", "nzRanges", "nzDefaultPickerValue", "nzSeparator", "nzSuffixIcon", "nzBackdrop", "nzId", "nzPlacement", "nzShowWeekNumber", "nzShowTime"], outputs: ["nzOnPanelChange", "nzOnCalendarChange", "nzOnOk", "nzOnOpenChange"], exportAs: ["nzDatePicker"] }, { kind: "ngmodule", type: NzModalModule }, { kind: "component", type: PaginationComponent, selector: "vl-pagination", inputs: ["itemInfo", "pageSize", "currentPage", "selectedLanguageInfo", "pages"], outputs: ["pageSizeChange", "onGoToPage"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i17.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: String$1, selector: "vl-string", inputs: ["formField", "modalItemInfo"] }, { kind: "ngmodule", type: AutoCompleteModule }, { kind: "component", type: i18.AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "minQueryLength", "delay", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "addOnTab", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "addOnBlur", "separator", "appendTo", "motionOptions"], outputs: ["completeMethod", "onSelect", "onUnselect", "onAdd", "onFocus", "onBlur", "onDropdownClick", "onClear", "onInputKeydown", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: CustomKeyboardDirective, selector: "[vlCustomKeyboard]", inputs: ["vlCustomKeyboard"] }, { kind: "pipe", type: i5.CurrencyPipe, name: "currency" }, { kind: "pipe", type: i5.DatePipe, name: "date" }] });
3416
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.2", type: CardComponent, isStandalone: true, selector: "vl-card", inputs: { itemInfo: "itemInfo", currentPage: "currentPage", pageSize: "pageSize", selectedLanguageInfo: "selectedLanguageInfo", mobileDisplay: "mobileDisplay", scrollPagination: "scrollPagination", pages: "pages", paginationMode: "paginationMode", paginationLoading: "paginationLoading" }, outputs: { cardButtonClick: "cardButtonClick", cardHeaderButtonClick: "cardHeaderButtonClick", linkDetails: "linkDetails", imageDetails: "imageDetails", onPageSizeChange: "onPageSizeChange", onPageChange: "onPageChange", cardDataChange: "cardDataChange" }, host: { listeners: { "window:resize": "onWindowResize()" } }, viewQueries: [{ propertyName: "scrollPaginationSentinel", first: true, predicate: ["scrollPaginationSentinel"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\r\n - change: autocomplete fields take the toolkit string keyboard unless option.keyboardType\r\n names another one ('native' keeps the system keyboard)\r\n - change: the date filter uses the shared picker markup (picker-field, DateTimePickerService) -->\r\n\r\n<!-- shuaib 2026-09-24\r\n - change: card field switches use renderFieldType so legacy checkbox fields render as boolean. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - new: a @contract comment naming the types this container does not render. It carries four\r\n ngSwitch blocks, so the gap belongs to the container and not to one of them -->\r\n\r\n<!-- @contract gap: dateRange, quantity, credit card\r\n reality: a card renders string, number, image, date, time, dropdown, boolean and\r\n autocomplete. Legacy checkbox fields normalize to boolean. dropdown and boolean arrived on 2026-09-18; before that a card\r\n dropped them silently.\r\n failure: a field of a missing type renders empty, with no error.\r\n instead: quantity is grid-only by design; for a dateRange or a credit card use a modal.\r\n seen: this file carries four switch blocks (lines 23, 94, 267, 488) and none of them has\r\n a case for those three - the gap belongs to the container, not to one block -->\r\n\r\n<!-- Loader Spinner -->\r\n\r\n<div *ngIf=\"isLoading\" class=\"loader-overlay\">\r\n <div class=\"spinner\"></div>\r\n</div>\r\n<div class=\"vl-card-runtime\">\r\n <!-- top pagination on card -->\r\n <div *ngIf=\"\r\n itemInfo?.layoutSchema?.pagination?.visible &&\r\n itemInfo?.layoutSchema?.pagination?.top\r\n \" class=\"card-pagination-wrapper\">\r\n <vl-pagination [itemInfo]=\"itemInfo\"\r\n [selectedLanguageInfo]=\"selectedLanguageInfo\" [pageSize]=\"pageSize\"\r\n [currentPage]=\"currentPage\" [pages]=\"pages\"\r\n (pageSizeChange)=\"invokeOnPageSizeChange($event)\"\r\n (onGoToPage)=\"invokeGoToPage($event)\">\r\n </vl-pagination>\r\n </div>\r\n <div *ngIf=\"itemInfo?.header?.elements?.length\" [ngStyle]=\"getHeaderStyle()\"\r\n [class]=\"getStyleWithoutClasses(\r\n itemInfo?.header?.style\r\n ).classes\">\r\n <div *ngFor=\"let element of itemInfo.header?.elements; let c = index\"\r\n [ngStyle]=\"getHeaderElementWrapperStyle(element)\"\r\n [class]=\"element.option?.innerAction && element.option?.innerAction === 'saveAll' && !displayedRecords?.length ? \r\n 'hidden' :getStyleWithoutClasses(element?.wrapperStyle).classes\">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(element.scope?.reference && itemInfo.propertySchema[getScope(element.scope?.reference)] ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.type : 'default')\">\r\n\r\n <!-- String Control -->\r\n <div *ngSwitchCase=\"'string'\"\r\n [class]=\"element.option?.visible ? 'card-field-wrapper' : 'hidden'\">\r\n\r\n <!-- LABEL -->\r\n <span *ngIf=\"element.label?.caption\"\r\n [style]=\"getStyleWithoutClasses(element.label?.style).style\"\r\n [class]=\"element.label?.style?.classes?.[0]\">\r\n {{ element.label.caption }}\r\n </span>\r\n </div>\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"mb-3\">\r\n <span *ngIf=\"element.option.protected\"\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">\r\n <vl-string [formField]=\"element\"\r\n [modalItemInfo]=\"itemInfo\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!element.option.protected\"\r\n [class]=\"element?.layout == 'horizontal' ? 'horizontal-layout' : ''\"\r\n [ngClass]=\"element?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(element?.style).style\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">{{\r\n element.label?.value\r\n ?\r\n element.label?.value : element.label?.caption ?\r\n element.label?.caption :\r\n itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <div class=\"picker-field\">\r\n <p-datepicker #cardDatePicker\r\n (ngModelChange)=\"dateChangeEvent(element, $event)\"\r\n [dateFormat]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.format ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()].format : 'yy-mm-dd'\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()].value\"\r\n (onInput)=\"onDateTyping($event)\"\r\n [class]=\"element?.scope?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(element?.scope?.style).style\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\"\r\n appendTo=\"body\"\r\n [panelStyleClass]=\"picker.datePanelClass\"\r\n [motionOptions]=\"picker.dockedMotion\"\r\n [vlCustomKeyboard]=\"element?.option?.keyboardType ?? 'date'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, cardDatePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(cardDatePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\"\r\n aria-label=\"Open calendar\"\r\n (pointerdown)=\"picker.openFromIcon($event, cardDatePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-calendar\"></i>\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\"\r\n [class]=\"element.option?.visible ? 'row' : 'hidden'\"\r\n (click)=\"invokeOnCardHeader(element)\">\r\n <button nz-button type=\"button\"\r\n [matTooltip]=\"element?.option?.tooltip || ''\"\r\n [matTooltipTouchGestures]=\"'auto'\"\r\n [ngStyle]=\"getStyleWithoutClasses(element.style).style\"\r\n [class]=\"element.option.protected ? 'disabled-button' : getStyleWithoutClasses(element.style).classes\">\r\n <ng-container\r\n *ngIf=\"!element.caption; else headerTextContent\">\r\n <i nz-icon [nzType]=\"element.icon\"\r\n nzTheme=\"outline\"></i>\r\n </ng-container>\r\n <ng-template #headerTextContent>{{ element.caption\r\n }}</ng-template>\r\n </button>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n <div class=\"card-responsive-grid\" [ngStyle]=\"getCardContainerStyle()\"\r\n [class]=\"getStyleWithoutClasses(\r\n itemInfo?.layoutSchema?.style\r\n ).classes\">\r\n <div *ngFor=\"let record of displayedRecords; let k = index\"\r\n class=\"card-grid-item\">\r\n <div [ngStyle]=\"getSingleCardStyle()\"\r\n [class]=\"getStyleWithoutClasses(itemInfo?.layoutSchema?.stylePerCard).classes\">\r\n <div *ngFor=\"let layout of itemInfo?.layoutSchema?.cards || []\"\r\n class=\"card-layout-item\" [ngStyle]=\"\r\n layout?.type === 'HorizontalLayout'\r\n ? {}\r\n : getStyleWithoutClasses(layout?.style).style\r\n \" [class]=\"\r\n layout?.type === 'HorizontalLayout'\r\n ? ''\r\n : getStyleWithoutClasses(layout?.style).classes\r\n \">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(layout?.scope?.reference && itemInfo.propertySchema[getScope(layout.scope?.reference)] ? itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.type : layout?.type == 'HorizontalLayout' ? 'HorizontalLayout' : 'default')\">\r\n <!-- Image Control -->\r\n <div *ngSwitchCase=\"'image'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <img\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [src]=\"layout?.option?.src ? layout?.option?.src : record[layout.scope?.reference.split('/').pop()]\"\r\n alt=\"{{ layout.label ? layout.label : getProperty(layout.scope?.reference).productSchema.caption }}\"\r\n (click)=\"showImageDetails(layout.option, record)\" />\r\n </div>\r\n\r\n <!-- String Control -->\r\n <div *ngSwitchCase=\"'string'\"\r\n [class]=\"layout.option?.visible ? 'card-field-wrapper' : 'hidden'\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(layout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(layout.label?.style).classes\">\r\n {{ getHorizontalLayoutCaption(layout) }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n @if(layout.option && layout.option.control &&\r\n !layout.option.protected){\r\n @switch (layout.option.control) {\r\n @case ('textInput') {\r\n <p\r\n *ngIf=\"layout.option?.protected && layout.option?.visible\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n (click)=\"showLinkDetails(layout.option, record)\">\r\n {{\r\n record?.[layout.scope?.reference.split('/').pop()]\r\n ??\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)]?.[layout.scope?.reference.split('/').pop()]?.default\r\n ??\r\n '--'\r\n }}\r\n </p>\r\n }\r\n @case ('password') {\r\n <span>Switch not supported yet</span>\r\n\r\n }\r\n @case ('combobox') {\r\n <span>Combobox not supported yet</span>\r\n }\r\n @case('textArea'){\r\n <span>TextArea not supported yet</span>\r\n }\r\n @case ('switch') {\r\n <span>Switch not supported yet</span>\r\n }\r\n\r\n @case('radiobutton') {\r\n <span>Radiobutton not supported yet</span>\r\n }\r\n\r\n @case ('dropdown') {\r\n <select id=\"dynamic-dropdown\"\r\n #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(layout?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style)?.style\"\r\n [name]=\"layout.scope?.reference\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)][\r\n layout.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of itemInfo.propertySchema[getScope(layout.scope?.reference)]?.[\r\n layout.scope?.reference.split('/').pop()\r\n ]?.data\r\n \"\r\n [value]=\"item[layout?.option?.optionValue]\">\r\n {{ item[layout?.option?.optionLabel] }}\r\n </option>\r\n </select>\r\n }\r\n\r\n @case ('autocomplete') {\r\n <p-autocomplete #autocompleteField=\"ngModel\"\r\n [required]=\"layout.option.required\"\r\n [name]=\"layout.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [class]=\"layout?.scope?.style?.classes ? layout?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"layout.option.autocomplete.response.label\"\r\n [placeholder]=\"itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.placeholder\"\r\n [vlCustomKeyboard]=\"layout?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n }\r\n }\r\n }@else {\r\n <p\r\n *ngIf=\"layout.option?.protected && layout.option?.visible\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n (click)=\"showLinkDetails(layout.option, record)\">\r\n {{\r\n record?.[layout.scope?.reference.split('/').pop()]\r\n ??\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)]?.[layout.scope?.reference.split('/').pop()]?.default\r\n ??\r\n '--'\r\n }}\r\n </p>\r\n }\r\n\r\n </div>\r\n\r\n <!-- Date Control -->\r\n <div *ngSwitchCase=\"'date'\"\r\n [class]=\"layout.option?.visible ? 'card-field-wrapper' : 'hidden'\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <!-- LABEL -->\r\n <span *ngIf=\"layout?.label?.caption\"\r\n [class]=\"layout.label?.style?.classes?.[0]\">\r\n {{ layout.label.caption }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <p\r\n *ngIf=\"layout.option?.protected && layout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n (click)=\"showLinkDetails(layout.option, record)\">\r\n {{\r\n record?.[layout.scope?.reference.split('/').pop()]\r\n ?? itemInfo.propertySchema[\r\n getScope(layout.scope?.reference)\r\n ]?.[layout.scope?.reference.split('/').pop()]?.default\r\n ?? '--'\r\n }}\r\n </p>\r\n </div>\r\n\r\n <!-- Time Control -->\r\n <div *ngSwitchCase=\"'time'\"\r\n [class]=\"layout.option?.visible ? 'card-field-wrapper' : 'hidden'\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span *ngIf=\"layout?.label?.caption\"\r\n [class]=\"layout.label?.style?.classes?.[0]\">\r\n {{ layout.label.caption }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <p\r\n *ngIf=\"layout.option?.protected && layout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n (click)=\"showLinkDetails(layout.option, record)\">\r\n {{\r\n record?.[layout.scope?.reference.split('/').pop()]\r\n ?? itemInfo.propertySchema[\r\n getScope(layout.scope?.reference)\r\n ]?.[layout.scope?.reference.split('/').pop()]?.default\r\n ?? '--'\r\n }}\r\n </p>\r\n </div>\r\n\r\n <!-- number Control -->\r\n <div *ngSwitchCase=\"'number'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.format.currency\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n {{\r\n record[layout.scope?.reference.split('/').pop()]\r\n | currency: layout.option?.currency\r\n ?\r\n layout.option.currency\r\n :\r\n 'USD' }}\r\n </h3>\r\n </div>\r\n\r\n <!-- dropdown Control -->\r\n <div *ngSwitchCase=\"'dropdown'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.format.currency\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <select id=\"dynamic-dropdown\"\r\n #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(layout?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style)?.style\"\r\n [name]=\"layout.scope?.reference\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)][\r\n layout.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of itemInfo.propertySchema[getScope(layout.scope?.reference)]?.[\r\n layout.scope?.reference.split('/').pop()\r\n ]?.data\r\n \"\r\n [value]=\"item[layout?.option?.optionValue]\">\r\n {{ item[layout?.option?.optionLabel] }}\r\n </option>\r\n </select>\r\n </h3>\r\n </div>\r\n\r\n <!-- autocomplete Control -->\r\n <div *ngSwitchCase=\"'autocomplete'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.format.currency\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <p-autocomplete #autocompleteField=\"ngModel\"\r\n [required]=\"layout.option.required\"\r\n [name]=\"layout.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [class]=\"layout?.scope?.style?.classes ? layout?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"layout.option.autocomplete.response.label\"\r\n [placeholder]=\"itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.placeholder\"\r\n [vlCustomKeyboard]=\"layout?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n </h3>\r\n </div>\r\n\r\n <!-- checkbox Control -->\r\n <div *ngSwitchCase=\"'boolean'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <input type=\"checkbox\"\r\n class=\"form-check-input mr-2\" [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)][\r\n layout.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [name]=\"layout.scope?.reference\" />\r\n </h3>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <button\r\n [style]=\"getStyleWithoutClasses(layout?.style).style\"\r\n [class]=\"layout.option.protected ? 'disabled-button' : getStyleWithoutClasses(layout?.style).classes\"\r\n (click)=\"\r\n invokeButtonClick(\r\n layout,\r\n record,\r\n getRecordIndex(record)\r\n )\r\n\">\r\n <ng-container\r\n *ngIf=\"!layout.caption; else textContent\">\r\n <i nz-icon [nzType]=\"layout.icon\"\r\n nzTheme=\"outline\"></i>\r\n </ng-container>\r\n <ng-template #textContent>\r\n {{ layout.caption }}\r\n </ng-template>\r\n </button>\r\n </div>\r\n\r\n <!-- HorizontalLayout Control -->\r\n <div *ngSwitchCase=\"'HorizontalLayout'\">\r\n <div class=\"card-horizontal-layout\"\r\n [class]=\"getStyleWithoutClasses(\r\n layout?.style\r\n ).classes\" [ngStyle]=\"getHorizontalLayoutStyle(layout)\">\r\n <ng-container\r\n *ngFor=\"let innerLayout of layout.elements\">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(innerLayout.scope?.reference && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] ? itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.type : 'default')\">\r\n <!-- Image Control -->\r\n <div *ngSwitchCase=\"'image'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n <img\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n [src]=\"innerLayout?.option?.src ? innerLayout?.option?.src : record[innerLayout.scope?.reference.split('/').pop()]\"\r\n alt=\"{{ innerLayout.label ? innerLayout.label : getProperty(innerLayout.scope?.reference).productSchema.caption }}\"\r\n (click)=\"showImageDetails(innerLayout.option, record)\" />\r\n </div>\r\n\r\n <!-- String Control -->\r\n <div *ngSwitchCase=\"'string'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(innerLayout.label?.style).classes\">\r\n {{\r\n getHorizontalLayoutCaption(innerLayout)\r\n }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n @if(innerLayout.option &&\r\n innerLayout.option.control &&\r\n !innerLayout.option.protected){\r\n @switch (innerLayout.option.control)\r\n {\r\n @case ('textInput') {\r\n <h3\r\n *ngIf=\"innerLayout.option?.protected && innerLayout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n (click)=\"showLinkDetails(innerLayout.option, record)\">\r\n\r\n <!-- DATE FORMAT -->\r\n <ng-container\r\n *ngIf=\"innerLayout.option?.dateFormat; else checkInnerTime\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n | date:\r\n innerLayout.option.dateFormat)\r\n : '--' }}\r\n </ng-container>\r\n\r\n <!-- TIME FORMAT -->\r\n <ng-template #checkInnerTime>\r\n <ng-container\r\n *ngIf=\"innerLayout.option?.timeFormat; else normalInnerValue\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n |\r\n date:\r\n innerLayout.option.timeFormat)\r\n : '--' }}\r\n </ng-container>\r\n </ng-template>\r\n\r\n <!-- NORMAL STRING -->\r\n <ng-template #normalInnerValue>\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ??\r\n '--' }}\r\n </ng-template>\r\n </h3>\r\n }\r\n @case ('password') {\r\n <span>Switch not supported\r\n yet</span>\r\n\r\n }\r\n @case ('combobox') {\r\n <span>Combobox not supported\r\n yet</span>\r\n }\r\n @case('textArea'){\r\n <span>TextArea not supported\r\n yet</span>\r\n }\r\n @case ('switch') {\r\n <span>Switch not supported\r\n yet</span>\r\n }\r\n\r\n @case('radiobutton') {\r\n <span>Radiobutton not supported\r\n yet</span>\r\n }\r\n\r\n @case ('dropdown') {\r\n <select id=\"dynamic-dropdown\"\r\n #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(innerLayout?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style)?.style\"\r\n [name]=\"innerLayout.scope?.reference\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][\r\n innerLayout.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of itemInfo.propertySchema[getScope(innerLayout.scope?.reference)]?.[\r\n innerLayout.scope?.reference.split('/').pop()\r\n ]?.data\r\n \"\r\n [value]=\"item[innerLayout?.option?.optionValue]\">\r\n {{\r\n item[innerLayout?.option?.optionLabel]\r\n }}\r\n </option>\r\n </select>\r\n }\r\n\r\n @case ('autocomplete') {\r\n\r\n <p-autocomplete\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"innerLayout.option.required\"\r\n [name]=\"innerLayout.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n [class]=\"innerLayout?.scope?.style?.classes ? innerLayout?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"innerLayout.option.autocomplete.response.label\"\r\n [placeholder]=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.placeholder\"\r\n [vlCustomKeyboard]=\"innerLayout?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n }\r\n }\r\n }@else{\r\n\r\n <h3\r\n *ngIf=\"innerLayout.option?.protected && innerLayout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n (click)=\"showLinkDetails(innerLayout.option, record)\">\r\n\r\n <!-- DATE FORMAT -->\r\n <ng-container\r\n *ngIf=\"innerLayout.option?.dateFormat; else checkInnerTime\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n | date:\r\n innerLayout.option.dateFormat)\r\n : '--' }}\r\n </ng-container>\r\n\r\n <!-- TIME FORMAT -->\r\n <ng-template #checkInnerTime>\r\n <ng-container\r\n *ngIf=\"innerLayout.option?.timeFormat; else normalInnerValue\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n |\r\n date:\r\n innerLayout.option.timeFormat)\r\n : '--' }}\r\n </ng-container>\r\n </ng-template>\r\n\r\n <!-- NORMAL STRING -->\r\n <ng-template #normalInnerValue>\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ??\r\n '--' }}\r\n </ng-template>\r\n </h3>\r\n }\r\n\r\n </div>\r\n\r\n <!-- number Control -->\r\n <div *ngSwitchCase=\"'number'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.format?.currency\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n {{\r\n record[innerLayout.scope?.reference.split('/').pop()]\r\n | currency:\r\n innerLayout.option?.currency ?\r\n innerLayout.option.currency :\r\n 'USD'\r\n }}\r\n </h3>\r\n </div>\r\n\r\n <!-- Date Control -->\r\n <div *ngSwitchCase=\"'date'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(innerLayout.label?.style).classes\">\r\n {{\r\n getHorizontalLayoutCaption(innerLayout)\r\n }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <h3\r\n *ngIf=\"innerLayout.option?.protected && innerLayout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n (click)=\"showLinkDetails(innerLayout.option, record)\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n | date:\r\n innerLayout.option?.dateFormat ?\r\n innerLayout.option?.dateFormat :\r\n itemInfo.propertySchema[getScope(innerLayout.scope?.reference)]?.[innerLayout.scope?.reference.split('/').pop()]?.format\r\n || 'yyyy-MM-dd') : '--' }}\r\n </h3>\r\n </div>\r\n\r\n <!-- Time Control -->\r\n <div *ngSwitchCase=\"'time'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(innerLayout.label?.style).classes\">\r\n {{\r\n getHorizontalLayoutCaption(innerLayout)\r\n }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <h3\r\n *ngIf=\"innerLayout.option?.protected && innerLayout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n (click)=\"showLinkDetails(innerLayout.option, record)\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n | date:\r\n innerLayout.option?.timeFormat ?\r\n innerLayout.option?.timeFormat\r\n :itemInfo.propertySchema[getScope(innerLayout.scope?.reference)]?.[innerLayout.scope?.reference.split('/').pop()]?.format\r\n || 'HH:mm') : '--' }}\r\n </h3>\r\n </div>\r\n\r\n <!-- dropdown Control -->\r\n <div *ngSwitchCase=\"'dropdown'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.format.currency\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n <select id=\"dynamic-dropdown\"\r\n #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(innerLayout?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style)?.style\"\r\n [name]=\"innerLayout.scope?.reference\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][\r\n innerLayout.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of itemInfo.propertySchema[getScope(innerLayout.scope?.reference)]?.[\r\n innerLayout.scope?.reference.split('/').pop()\r\n ]?.data\r\n \"\r\n [value]=\"item[innerLayout?.option?.optionValue]\">\r\n {{\r\n item[innerLayout?.option?.optionLabel]\r\n }}\r\n </option>\r\n </select>\r\n </h3>\r\n </div>\r\n\r\n <!-- autocomplete Control -->\r\n <div *ngSwitchCase=\"'autocomplete'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.format.currency\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n <p-autocomplete\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"innerLayout.option.required\"\r\n [name]=\"innerLayout.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n [class]=\"innerLayout?.scope?.style?.classes ? innerLayout?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"innerLayout.option.autocomplete.response.label\"\r\n [placeholder]=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.placeholder\"\r\n [vlCustomKeyboard]=\"innerLayout?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n </h3>\r\n </div>\r\n\r\n <!-- checkbox Control -->\r\n <div *ngSwitchCase=\"'boolean'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n <input type=\"checkbox\"\r\n class=\"form-check-input mr-2\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][\r\n innerLayout.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [name]=\"innerLayout.scope?.reference\" />\r\n </h3>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button nz-button\r\n [matTooltip]=\"getHorizontalLayoutTooltip(innerLayout)\"\r\n [matTooltipTouchGestures]=\"'auto'\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.style).style\"\r\n [class]=\"innerLayout.option?.visible ? innerLayout.option.protected ? 'disabled-button' : getStyleWithoutClasses(innerLayout?.style).classes : 'hidden'\"\r\n (click)=\"\r\n invokeButtonClick(\r\n innerLayout,\r\n record,\r\n getRecordIndex(record)\r\n )\r\n\">\r\n <span\r\n class=\"schema-btn-content\">\r\n <i\r\n *ngIf=\"showButtonIcon(innerLayout)\"\r\n nz-icon\r\n [nzType]=\"innerLayout.icon\"\r\n nzTheme=\"outline\">\r\n </i>\r\n\r\n <span\r\n *ngIf=\"showButtonCaption(innerLayout)\">\r\n {{ innerLayout.caption\r\n }}\r\n </span>\r\n\r\n <span *ngIf=\"\r\n !showButtonIcon(innerLayout) &&\r\n !showButtonCaption(innerLayout)\r\n \">\r\n Button\r\n </span>\r\n </span>\r\n </button>\r\n </div>\r\n\r\n </ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <div\r\n *ngIf=\"itemInfo?.footer?.elements && itemInfo?.data && itemInfo?.data.length\"\r\n [class]=\"getStyleWithoutClasses(itemInfo?.footer?.style).classes\">\r\n <span *ngFor=\"let element of itemInfo.footer?.elements; let c = index\">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(element.scope?.reference && itemInfo.propertySchema[getScope(element.scope?.reference)] ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.type : 'default')\">\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"element.option.protected\"\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">\r\n <h5>{{\r\n itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.caption\r\n }}\r\n </h5>\r\n </span>\r\n <div *ngIf=\"!element.option.protected\">\r\n <div\r\n [class]=\"element?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">{{\r\n element.label?.value\r\n ?\r\n element.label?.value : element.label?.caption ?\r\n element.label?.caption :\r\n itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n\r\n <nz-date-picker [required]=\"element.option.required\"\r\n [name]=\"element.scope?.reference\"\r\n [nzDisabled]=\"element?.option?.disabled\"\r\n (ngModelChange)=\"dateChangeEvent(element, $event)\"\r\n [nzFormat]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.format ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.format : 'yyyy-MM-dd'\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(element?.style).style\"></nz-date-picker>\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\"\r\n [class]=\"element.option?.visible ? 'row' : 'hidden'\"\r\n (click)=\"invokeOnCardFooter(element)\">\r\n <button nz-button\r\n [style]=\"getStyleWithoutClasses(element.style).style\"\r\n [class]=\"element.option.protected ? 'disabled-button' : getStyleWithoutClasses(element.style).classes\">\r\n <ng-container\r\n *ngIf=\"!element.caption; else textContent\">\r\n <i nz-icon [nzType]=\"element.icon\"\r\n nzTheme=\"outline\"></i>\r\n </ng-container>\r\n <ng-template #textContent>\r\n {{ element.caption }}\r\n </ng-template>\r\n </button>\r\n </div>\r\n </ng-container>\r\n </span>\r\n </div>\r\n <!-- bottom pagination on card -->\r\n <div *ngIf=\"\r\n itemInfo?.data.length && \r\n itemInfo?.layoutSchema?.pagination?.visible &&\r\n !itemInfo?.layoutSchema?.pagination?.top &&\r\n !scrollPagination\r\n \" class=\"card-pagination-wrapper\">\r\n <vl-pagination [itemInfo]=\"itemInfo\"\r\n [selectedLanguageInfo]=\"selectedLanguageInfo\" [pageSize]=\"pageSize\"\r\n [currentPage]=\"currentPage\" [pages]=\"pages\"\r\n (pageSizeChange)=\"invokeOnPageSizeChange($event)\"\r\n (onGoToPage)=\"invokeGoToPage($event)\">\r\n </vl-pagination>\r\n </div>\r\n\r\n @if (scrollPagination) {\r\n <div #scrollPaginationSentinel class=\"scroll-pagination-sentinel\"\r\n aria-hidden=\"true\">\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block;width:100%;min-width:0}.vl-card-runtime{min-width:0;box-sizing:border-box}.card-responsive-grid{width:100%;min-width:0;box-sizing:border-box}.scroll-pagination-sentinel{width:100%;height:1px}.card-grid-item,.card-layout-item,.card-horizontal-layout,.card-field-wrapper{width:100%;min-width:0;box-sizing:border-box}.card-field-wrapper{overflow-wrap:anywhere}.card-field-wrapper p,.card-field-wrapper h3{margin:0;min-width:0}.card-layout-item img{display:block;max-width:100%;height:auto}.card-layout-item button{max-width:100%;box-sizing:border-box}.schema-btn-content{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:0}.hidden{display:none!important}.card-pagination-wrapper{width:100%;min-width:0;box-sizing:border-box;padding:0 10px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i5.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i5.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: NzIconModule }, { kind: "directive", type: i11.NzIconDirective, selector: "nz-icon,[nz-icon]", inputs: ["nzSpin", "nzRotate", "nzType", "nzTheme", "nzTwotoneColor", "nzIconfont"], exportAs: ["nzIcon"] }, { kind: "ngmodule", type: DatePickerModule }, { kind: "component", type: i12.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: NzButtonModule }, { kind: "component", type: i13.NzButtonComponent, selector: "button[nz-button], a[nz-button]", inputs: ["nzBlock", "nzGhost", "nzSearch", "nzLoading", "nzDanger", "disabled", "tabIndex", "nzType", "nzShape", "nzSize"], exportAs: ["nzButton"] }, { kind: "directive", type: i14.ɵNzTransitionPatchDirective, selector: "[nz-button], [nz-icon], nz-icon, [nz-menu-item], [nz-submenu], nz-select-top-control, nz-select-placeholder, nz-input-group", inputs: ["hidden"] }, { kind: "directive", type: i15.NzWaveDirective, selector: "[nz-wave],button[nz-button]:not([nzType=\"link\"]):not([nzType=\"text\"])", inputs: ["nzWaveExtraNode"], exportAs: ["nzWave"] }, { kind: "ngmodule", type: NzDatePickerModule }, { kind: "component", type: i4.NzDatePickerComponent, selector: "nz-date-picker,nz-week-picker,nz-month-picker,nz-quarter-picker,nz-year-picker,nz-range-picker", inputs: ["nzInline", "nzAllowClear", "nzAutoFocus", "nzDisabled", "nzInputReadOnly", "nzOpen", "nzDisabledDate", "nzLocale", "nzPlaceHolder", "nzPopupStyle", "nzDropdownClassName", "nzSize", "nzStatus", "nzFormat", "nzVariant", "nzDateRender", "nzDisabledTime", "nzRenderExtraFooter", "nzShowToday", "nzMode", "nzShowNow", "nzRanges", "nzDefaultPickerValue", "nzSeparator", "nzSuffixIcon", "nzBackdrop", "nzId", "nzPlacement", "nzShowWeekNumber", "nzShowTime"], outputs: ["nzOnPanelChange", "nzOnCalendarChange", "nzOnOk", "nzOnOpenChange"], exportAs: ["nzDatePicker"] }, { kind: "ngmodule", type: NzModalModule }, { kind: "component", type: PaginationComponent, selector: "vl-pagination", inputs: ["itemInfo", "pageSize", "currentPage", "selectedLanguageInfo", "pages"], outputs: ["pageSizeChange", "onGoToPage"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i17.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: String$1, selector: "vl-string", inputs: ["formField", "modalItemInfo"] }, { kind: "ngmodule", type: AutoCompleteModule }, { kind: "component", type: i18.AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "minQueryLength", "delay", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "addOnTab", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "addOnBlur", "separator", "appendTo", "motionOptions"], outputs: ["completeMethod", "onSelect", "onUnselect", "onAdd", "onFocus", "onBlur", "onDropdownClick", "onClear", "onInputKeydown", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: CustomKeyboardDirective, selector: "[vlCustomKeyboard]", inputs: ["vlCustomKeyboard"] }, { kind: "pipe", type: i5.CurrencyPipe, name: "currency" }, { kind: "pipe", type: i5.DatePipe, name: "date" }] });
2863
3417
  }
2864
3418
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: CardComponent, decorators: [{
2865
3419
  type: Component,
@@ -2877,7 +3431,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
2877
3431
  AutoCompleteModule,
2878
3432
  InputTextModule,
2879
3433
  CustomKeyboardDirective,
2880
- ], standalone: true, template: "<!-- shuaib 2026-09-24\r\n - change: card field switches use renderFieldType so legacy checkbox fields render as boolean. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - new: a @contract comment naming the types this container does not render. It carries four\r\n ngSwitch blocks, so the gap belongs to the container and not to one of them -->\r\n\r\n<!-- @contract gap: dateRange, quantity, credit card\r\n reality: a card renders string, number, image, date, time, dropdown, boolean and\r\n autocomplete. Legacy checkbox fields normalize to boolean. dropdown and boolean arrived on 2026-09-18; before that a card\r\n dropped them silently.\r\n failure: a field of a missing type renders empty, with no error.\r\n instead: quantity is grid-only by design; for a dateRange or a credit card use a modal.\r\n seen: this file carries four switch blocks (lines 23, 94, 267, 488) and none of them has\r\n a case for those three - the gap belongs to the container, not to one block -->\r\n\r\n<!-- Loader Spinner -->\r\n\r\n<div *ngIf=\"isLoading\" class=\"loader-overlay\">\r\n <div class=\"spinner\"></div>\r\n</div>\r\n<div class=\"vl-card-runtime\">\r\n <!-- top pagination on card -->\r\n <div *ngIf=\"\r\n itemInfo?.layoutSchema?.pagination?.visible &&\r\n itemInfo?.layoutSchema?.pagination?.top\r\n \" class=\"card-pagination-wrapper\">\r\n <vl-pagination [itemInfo]=\"itemInfo\"\r\n [selectedLanguageInfo]=\"selectedLanguageInfo\" [pageSize]=\"pageSize\"\r\n [currentPage]=\"currentPage\" [pages]=\"pages\"\r\n (pageSizeChange)=\"invokeOnPageSizeChange($event)\"\r\n (onGoToPage)=\"invokeGoToPage($event)\">\r\n </vl-pagination>\r\n </div>\r\n <div *ngIf=\"itemInfo?.header?.elements?.length\" [ngStyle]=\"getHeaderStyle()\"\r\n [class]=\"getStyleWithoutClasses(\r\n itemInfo?.header?.style\r\n ).classes\">\r\n <div *ngFor=\"let element of itemInfo.header?.elements; let c = index\"\r\n [ngStyle]=\"getHeaderElementWrapperStyle(element)\"\r\n [class]=\"element.option?.innerAction && element.option?.innerAction === 'saveAll' && !displayedRecords?.length ? \r\n 'hidden' :getStyleWithoutClasses(element?.wrapperStyle).classes\">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(element.scope?.reference && itemInfo.propertySchema[getScope(element.scope?.reference)] ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.type : 'default')\">\r\n\r\n <!-- String Control -->\r\n <div *ngSwitchCase=\"'string'\"\r\n [class]=\"element.option?.visible ? 'card-field-wrapper' : 'hidden'\">\r\n\r\n <!-- LABEL -->\r\n <span *ngIf=\"element.label?.caption\"\r\n [style]=\"getStyleWithoutClasses(element.label?.style).style\"\r\n [class]=\"element.label?.style?.classes?.[0]\">\r\n {{ element.label.caption }}\r\n </span>\r\n </div>\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"mb-3\">\r\n <span *ngIf=\"element.option.protected\"\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">\r\n <vl-string [formField]=\"element\"\r\n [modalItemInfo]=\"itemInfo\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!element.option.protected\"\r\n [class]=\"element?.layout == 'horizontal' ? 'horizontal-layout' : ''\"\r\n [ngClass]=\"element?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(element?.style).style\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">{{\r\n element.label?.value\r\n ?\r\n element.label?.value : element.label?.caption ?\r\n element.label?.caption :\r\n itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <p-datepicker\r\n (ngModelChange)=\"dateChangeEvent(element, $event)\"\r\n [showIcon]=\"true\" type=\"number\"\r\n inputmode=\"numeric\"\r\n [dateFormat]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.format ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()].format : 'yy-mm-dd'\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()].value\"\r\n (onFocus)=\"forceNumericKeyboard($event)\"\r\n (onInput)=\"onDateTyping($event)\"\r\n [class]=\"element?.scope?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(element?.scope?.style).style\">\r\n </p-datepicker>\r\n </div>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\"\r\n [class]=\"element.option?.visible ? 'row' : 'hidden'\"\r\n (click)=\"invokeOnCardHeader(element)\">\r\n <button nz-button type=\"button\"\r\n [matTooltip]=\"element?.option?.tooltip || ''\"\r\n [matTooltipTouchGestures]=\"'auto'\"\r\n [ngStyle]=\"getStyleWithoutClasses(element.style).style\"\r\n [class]=\"element.option.protected ? 'disabled-button' : getStyleWithoutClasses(element.style).classes\">\r\n <ng-container\r\n *ngIf=\"!element.caption; else headerTextContent\">\r\n <i nz-icon [nzType]=\"element.icon\"\r\n nzTheme=\"outline\"></i>\r\n </ng-container>\r\n <ng-template #headerTextContent>{{ element.caption\r\n }}</ng-template>\r\n </button>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n <div class=\"card-responsive-grid\" [ngStyle]=\"getCardContainerStyle()\"\r\n [class]=\"getStyleWithoutClasses(\r\n itemInfo?.layoutSchema?.style\r\n ).classes\">\r\n <div *ngFor=\"let record of displayedRecords; let k = index\"\r\n class=\"card-grid-item\">\r\n <div [ngStyle]=\"getSingleCardStyle()\"\r\n [class]=\"getStyleWithoutClasses(itemInfo?.layoutSchema?.stylePerCard).classes\">\r\n <div *ngFor=\"let layout of itemInfo?.layoutSchema?.cards || []\"\r\n class=\"card-layout-item\" [ngStyle]=\"\r\n layout?.type === 'HorizontalLayout'\r\n ? {}\r\n : getStyleWithoutClasses(layout?.style).style\r\n \" [class]=\"\r\n layout?.type === 'HorizontalLayout'\r\n ? ''\r\n : getStyleWithoutClasses(layout?.style).classes\r\n \">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(layout?.scope?.reference && itemInfo.propertySchema[getScope(layout.scope?.reference)] ? itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.type : layout?.type == 'HorizontalLayout' ? 'HorizontalLayout' : 'default')\">\r\n <!-- Image Control -->\r\n <div *ngSwitchCase=\"'image'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <img\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [src]=\"layout?.option?.src ? layout?.option?.src : record[layout.scope?.reference.split('/').pop()]\"\r\n alt=\"{{ layout.label ? layout.label : getProperty(layout.scope?.reference).productSchema.caption }}\"\r\n (click)=\"showImageDetails(layout.option, record)\" />\r\n </div>\r\n\r\n <!-- String Control -->\r\n <div *ngSwitchCase=\"'string'\"\r\n [class]=\"layout.option?.visible ? 'card-field-wrapper' : 'hidden'\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(layout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(layout.label?.style).classes\">\r\n {{ getHorizontalLayoutCaption(layout) }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <p\r\n *ngIf=\"layout.option?.protected && layout.option?.visible\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n (click)=\"showLinkDetails(layout.option, record)\">\r\n {{\r\n record?.[layout.scope?.reference.split('/').pop()]\r\n ??\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)]?.[layout.scope?.reference.split('/').pop()]?.default\r\n ??\r\n '--'\r\n }}\r\n </p>\r\n </div>\r\n\r\n <!-- Date Control -->\r\n <div *ngSwitchCase=\"'date'\"\r\n [class]=\"layout.option?.visible ? 'card-field-wrapper' : 'hidden'\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <!-- LABEL -->\r\n <span *ngIf=\"layout?.label?.caption\"\r\n [class]=\"layout.label?.style?.classes?.[0]\">\r\n {{ layout.label.caption }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <p\r\n *ngIf=\"layout.option?.protected && layout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n (click)=\"showLinkDetails(layout.option, record)\">\r\n {{\r\n record?.[layout.scope?.reference.split('/').pop()]\r\n ?? itemInfo.propertySchema[\r\n getScope(layout.scope?.reference)\r\n ]?.[layout.scope?.reference.split('/').pop()]?.default\r\n ?? '--'\r\n }}\r\n </p>\r\n </div>\r\n\r\n <!-- Time Control -->\r\n <div *ngSwitchCase=\"'time'\"\r\n [class]=\"layout.option?.visible ? 'card-field-wrapper' : 'hidden'\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span *ngIf=\"layout?.label?.caption\"\r\n [class]=\"layout.label?.style?.classes?.[0]\">\r\n {{ layout.label.caption }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <p\r\n *ngIf=\"layout.option?.protected && layout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n (click)=\"showLinkDetails(layout.option, record)\">\r\n {{\r\n record?.[layout.scope?.reference.split('/').pop()]\r\n ?? itemInfo.propertySchema[\r\n getScope(layout.scope?.reference)\r\n ]?.[layout.scope?.reference.split('/').pop()]?.default\r\n ?? '--'\r\n }}\r\n </p>\r\n </div>\r\n\r\n <!-- number Control -->\r\n <div *ngSwitchCase=\"'number'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.format.currency\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n {{\r\n record[layout.scope?.reference.split('/').pop()]\r\n | currency: layout.option?.currency\r\n ?\r\n layout.option.currency\r\n :\r\n 'USD' }}\r\n </h3>\r\n </div>\r\n\r\n <!-- dropdown Control -->\r\n <div *ngSwitchCase=\"'dropdown'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.format.currency\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <select id=\"dynamic-dropdown\"\r\n #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(layout?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style)?.style\"\r\n [name]=\"layout.scope?.reference\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)][\r\n layout.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of itemInfo.propertySchema[getScope(layout.scope?.reference)]?.[\r\n layout.scope?.reference.split('/').pop()\r\n ]?.data\r\n \"\r\n [value]=\"item[layout?.option?.optionValue]\">\r\n {{ item[layout?.option?.optionLabel] }}\r\n </option>\r\n </select>\r\n </h3>\r\n </div>\r\n\r\n <!-- autocomplete Control -->\r\n <div *ngSwitchCase=\"'autocomplete'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.format.currency\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <p-autocomplete #autocompleteField=\"ngModel\"\r\n [required]=\"layout.option.required\"\r\n [name]=\"layout.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [class]=\"layout?.scope?.style?.classes ? layout?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"layout.option.autocomplete.response.label\"\r\n [placeholder]=\"itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.placeholder\"\r\n [vlCustomKeyboard]=\"layout?.option?.keyboardType\">\r\n </p-autocomplete>\r\n </h3>\r\n </div>\r\n\r\n <!-- checkbox Control -->\r\n <div *ngSwitchCase=\"'boolean'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <input type=\"checkbox\"\r\n class=\"form-check-input mr-2\" [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)][\r\n layout.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [name]=\"layout.scope?.reference\" />\r\n </h3>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <button\r\n [style]=\"getStyleWithoutClasses(layout?.style).style\"\r\n [class]=\"layout.option.protected ? 'disabled-button' : getStyleWithoutClasses(layout?.style).classes\"\r\n (click)=\"\r\n invokeButtonClick(\r\n layout,\r\n record,\r\n getRecordIndex(record)\r\n )\r\n\">\r\n <ng-container\r\n *ngIf=\"!layout.caption; else textContent\">\r\n <i nz-icon [nzType]=\"layout.icon\"\r\n nzTheme=\"outline\"></i>\r\n </ng-container>\r\n <ng-template #textContent>\r\n {{ layout.caption }}\r\n </ng-template>\r\n </button>\r\n </div>\r\n\r\n <!-- HorizontalLayout Control -->\r\n <div *ngSwitchCase=\"'HorizontalLayout'\">\r\n <div class=\"card-horizontal-layout\"\r\n [class]=\"getStyleWithoutClasses(\r\n layout?.style\r\n ).classes\" [ngStyle]=\"getHorizontalLayoutStyle(layout)\">\r\n <ng-container\r\n *ngFor=\"let innerLayout of layout.elements\">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(innerLayout.scope?.reference && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] ? itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.type : 'default')\">\r\n <!-- Image Control -->\r\n <div *ngSwitchCase=\"'image'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n <img\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n [src]=\"innerLayout?.option?.src ? innerLayout?.option?.src : record[innerLayout.scope?.reference.split('/').pop()]\"\r\n alt=\"{{ innerLayout.label ? innerLayout.label : getProperty(innerLayout.scope?.reference).productSchema.caption }}\"\r\n (click)=\"showImageDetails(innerLayout.option, record)\" />\r\n </div>\r\n\r\n <!-- String Control -->\r\n <div *ngSwitchCase=\"'string'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(innerLayout.label?.style).classes\">\r\n {{\r\n getHorizontalLayoutCaption(innerLayout)\r\n }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <h3\r\n *ngIf=\"innerLayout.option?.protected && innerLayout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n (click)=\"showLinkDetails(innerLayout.option, record)\">\r\n\r\n <!-- DATE FORMAT -->\r\n <ng-container\r\n *ngIf=\"innerLayout.option?.dateFormat; else checkInnerTime\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n | date:\r\n innerLayout.option.dateFormat)\r\n : '--' }}\r\n </ng-container>\r\n\r\n <!-- TIME FORMAT -->\r\n <ng-template #checkInnerTime>\r\n <ng-container\r\n *ngIf=\"innerLayout.option?.timeFormat; else normalInnerValue\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n |\r\n date:\r\n innerLayout.option.timeFormat)\r\n : '--' }}\r\n </ng-container>\r\n </ng-template>\r\n\r\n <!-- NORMAL STRING -->\r\n <ng-template #normalInnerValue>\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ??\r\n '--' }}\r\n </ng-template>\r\n </h3>\r\n </div>\r\n\r\n <!-- number Control -->\r\n <div *ngSwitchCase=\"'number'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.format?.currency\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n {{\r\n record[innerLayout.scope?.reference.split('/').pop()]\r\n | currency:\r\n innerLayout.option?.currency ?\r\n innerLayout.option.currency :\r\n 'USD'\r\n }}\r\n </h3>\r\n </div>\r\n\r\n <!-- Date Control -->\r\n <div *ngSwitchCase=\"'date'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(innerLayout.label?.style).classes\">\r\n {{\r\n getHorizontalLayoutCaption(innerLayout)\r\n }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <h3\r\n *ngIf=\"innerLayout.option?.protected && innerLayout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n (click)=\"showLinkDetails(innerLayout.option, record)\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n | date:\r\n innerLayout.option?.dateFormat ?\r\n innerLayout.option?.dateFormat :\r\n itemInfo.propertySchema[getScope(innerLayout.scope?.reference)]?.[innerLayout.scope?.reference.split('/').pop()]?.format\r\n || 'yyyy-MM-dd') : '--' }}\r\n </h3>\r\n </div>\r\n\r\n <!-- Time Control -->\r\n <div *ngSwitchCase=\"'time'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(innerLayout.label?.style).classes\">\r\n {{\r\n getHorizontalLayoutCaption(innerLayout)\r\n }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <h3\r\n *ngIf=\"innerLayout.option?.protected && innerLayout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n (click)=\"showLinkDetails(innerLayout.option, record)\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n | date:\r\n innerLayout.option?.timeFormat ?\r\n innerLayout.option?.timeFormat\r\n :itemInfo.propertySchema[getScope(innerLayout.scope?.reference)]?.[innerLayout.scope?.reference.split('/').pop()]?.format\r\n || 'HH:mm') : '--' }}\r\n </h3>\r\n </div>\r\n\r\n <!-- dropdown Control -->\r\n <div *ngSwitchCase=\"'dropdown'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.format.currency\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n <select id=\"dynamic-dropdown\"\r\n #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(innerLayout?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style)?.style\"\r\n [name]=\"innerLayout.scope?.reference\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][\r\n innerLayout.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of itemInfo.propertySchema[getScope(innerLayout.scope?.reference)]?.[\r\n innerLayout.scope?.reference.split('/').pop()\r\n ]?.data\r\n \"\r\n [value]=\"item[innerLayout?.option?.optionValue]\">\r\n {{\r\n item[innerLayout?.option?.optionLabel]\r\n }}\r\n </option>\r\n </select>\r\n </h3>\r\n </div>\r\n\r\n <!-- autocomplete Control -->\r\n <div *ngSwitchCase=\"'autocomplete'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.format.currency\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n <p-autocomplete\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"innerLayout.option.required\"\r\n [name]=\"innerLayout.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n [class]=\"innerLayout?.scope?.style?.classes ? innerLayout?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"innerLayout.option.autocomplete.response.label\"\r\n [placeholder]=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.placeholder\"\r\n [vlCustomKeyboard]=\"innerLayout?.option?.keyboardType\">\r\n </p-autocomplete>\r\n </h3>\r\n </div>\r\n\r\n <!-- checkbox Control -->\r\n <div *ngSwitchCase=\"'boolean'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n <input type=\"checkbox\"\r\n class=\"form-check-input mr-2\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][\r\n innerLayout.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [name]=\"innerLayout.scope?.reference\" />\r\n </h3>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button nz-button\r\n [matTooltip]=\"getHorizontalLayoutTooltip(innerLayout)\"\r\n [matTooltipTouchGestures]=\"'auto'\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.style).style\"\r\n [class]=\"innerLayout.option?.visible ? innerLayout.option.protected ? 'disabled-button' : getStyleWithoutClasses(innerLayout?.style).classes : 'hidden'\"\r\n (click)=\"\r\n invokeButtonClick(\r\n innerLayout,\r\n record,\r\n getRecordIndex(record)\r\n )\r\n\">\r\n <span\r\n class=\"schema-btn-content\">\r\n <i\r\n *ngIf=\"showButtonIcon(innerLayout)\"\r\n nz-icon\r\n [nzType]=\"innerLayout.icon\"\r\n nzTheme=\"outline\">\r\n </i>\r\n\r\n <span\r\n *ngIf=\"showButtonCaption(innerLayout)\">\r\n {{ innerLayout.caption\r\n }}\r\n </span>\r\n\r\n <span *ngIf=\"\r\n !showButtonIcon(innerLayout) &&\r\n !showButtonCaption(innerLayout)\r\n \">\r\n Button\r\n </span>\r\n </span>\r\n </button>\r\n </div>\r\n\r\n </ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <div\r\n *ngIf=\"itemInfo?.footer?.elements && itemInfo?.data && itemInfo?.data.length\"\r\n [class]=\"getStyleWithoutClasses(itemInfo?.footer?.style).classes\">\r\n <span *ngFor=\"let element of itemInfo.footer?.elements; let c = index\">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(element.scope?.reference && itemInfo.propertySchema[getScope(element.scope?.reference)] ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.type : 'default')\">\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"element.option.protected\"\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">\r\n <h5>{{\r\n itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.caption\r\n }}\r\n </h5>\r\n </span>\r\n <div *ngIf=\"!element.option.protected\">\r\n <div\r\n [class]=\"element?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">{{\r\n element.label?.value\r\n ?\r\n element.label?.value : element.label?.caption ?\r\n element.label?.caption :\r\n itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n\r\n <nz-date-picker [required]=\"element.option.required\"\r\n [name]=\"element.scope?.reference\"\r\n [nzDisabled]=\"element?.option?.disabled\"\r\n (ngModelChange)=\"dateChangeEvent(element, $event)\"\r\n [nzFormat]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.format ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.format : 'yyyy-MM-dd'\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(element?.style).style\"></nz-date-picker>\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\"\r\n [class]=\"element.option?.visible ? 'row' : 'hidden'\"\r\n (click)=\"invokeOnCardFooter(element)\">\r\n <button nz-button\r\n [style]=\"getStyleWithoutClasses(element.style).style\"\r\n [class]=\"element.option.protected ? 'disabled-button' : getStyleWithoutClasses(element.style).classes\">\r\n <ng-container\r\n *ngIf=\"!element.caption; else textContent\">\r\n <i nz-icon [nzType]=\"element.icon\"\r\n nzTheme=\"outline\"></i>\r\n </ng-container>\r\n <ng-template #textContent>\r\n {{ element.caption }}\r\n </ng-template>\r\n </button>\r\n </div>\r\n </ng-container>\r\n </span>\r\n </div>\r\n <!-- bottom pagination on card -->\r\n <div *ngIf=\"\r\n itemInfo?.data.length && \r\n itemInfo?.layoutSchema?.pagination?.visible &&\r\n !itemInfo?.layoutSchema?.pagination?.top &&\r\n !scrollPagination\r\n \" class=\"card-pagination-wrapper\">\r\n <vl-pagination [itemInfo]=\"itemInfo\"\r\n [selectedLanguageInfo]=\"selectedLanguageInfo\" [pageSize]=\"pageSize\"\r\n [currentPage]=\"currentPage\" [pages]=\"pages\"\r\n (pageSizeChange)=\"invokeOnPageSizeChange($event)\"\r\n (onGoToPage)=\"invokeGoToPage($event)\">\r\n </vl-pagination>\r\n </div>\r\n\r\n @if (scrollPagination) {\r\n <div #scrollPaginationSentinel class=\"scroll-pagination-sentinel\"\r\n aria-hidden=\"true\">\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block;width:100%;min-width:0}.vl-card-runtime{min-width:0;box-sizing:border-box}.card-responsive-grid{width:100%;min-width:0;box-sizing:border-box}.scroll-pagination-sentinel{width:100%;height:1px}.card-grid-item,.card-layout-item,.card-horizontal-layout,.card-field-wrapper{width:100%;min-width:0;box-sizing:border-box}.card-field-wrapper{overflow-wrap:anywhere}.card-field-wrapper p,.card-field-wrapper h3{margin:0;min-width:0}.card-layout-item img{display:block;max-width:100%;height:auto}.card-layout-item button{max-width:100%;box-sizing:border-box}.schema-btn-content{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:0}.hidden{display:none!important}.card-pagination-wrapper{width:100%;min-width:0;box-sizing:border-box;padding:0 10px}\n"] }]
3434
+ ], standalone: true, template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\r\n - change: autocomplete fields take the toolkit string keyboard unless option.keyboardType\r\n names another one ('native' keeps the system keyboard)\r\n - change: the date filter uses the shared picker markup (picker-field, DateTimePickerService) -->\r\n\r\n<!-- shuaib 2026-09-24\r\n - change: card field switches use renderFieldType so legacy checkbox fields render as boolean. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - new: a @contract comment naming the types this container does not render. It carries four\r\n ngSwitch blocks, so the gap belongs to the container and not to one of them -->\r\n\r\n<!-- @contract gap: dateRange, quantity, credit card\r\n reality: a card renders string, number, image, date, time, dropdown, boolean and\r\n autocomplete. Legacy checkbox fields normalize to boolean. dropdown and boolean arrived on 2026-09-18; before that a card\r\n dropped them silently.\r\n failure: a field of a missing type renders empty, with no error.\r\n instead: quantity is grid-only by design; for a dateRange or a credit card use a modal.\r\n seen: this file carries four switch blocks (lines 23, 94, 267, 488) and none of them has\r\n a case for those three - the gap belongs to the container, not to one block -->\r\n\r\n<!-- Loader Spinner -->\r\n\r\n<div *ngIf=\"isLoading\" class=\"loader-overlay\">\r\n <div class=\"spinner\"></div>\r\n</div>\r\n<div class=\"vl-card-runtime\">\r\n <!-- top pagination on card -->\r\n <div *ngIf=\"\r\n itemInfo?.layoutSchema?.pagination?.visible &&\r\n itemInfo?.layoutSchema?.pagination?.top\r\n \" class=\"card-pagination-wrapper\">\r\n <vl-pagination [itemInfo]=\"itemInfo\"\r\n [selectedLanguageInfo]=\"selectedLanguageInfo\" [pageSize]=\"pageSize\"\r\n [currentPage]=\"currentPage\" [pages]=\"pages\"\r\n (pageSizeChange)=\"invokeOnPageSizeChange($event)\"\r\n (onGoToPage)=\"invokeGoToPage($event)\">\r\n </vl-pagination>\r\n </div>\r\n <div *ngIf=\"itemInfo?.header?.elements?.length\" [ngStyle]=\"getHeaderStyle()\"\r\n [class]=\"getStyleWithoutClasses(\r\n itemInfo?.header?.style\r\n ).classes\">\r\n <div *ngFor=\"let element of itemInfo.header?.elements; let c = index\"\r\n [ngStyle]=\"getHeaderElementWrapperStyle(element)\"\r\n [class]=\"element.option?.innerAction && element.option?.innerAction === 'saveAll' && !displayedRecords?.length ? \r\n 'hidden' :getStyleWithoutClasses(element?.wrapperStyle).classes\">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(element.scope?.reference && itemInfo.propertySchema[getScope(element.scope?.reference)] ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.type : 'default')\">\r\n\r\n <!-- String Control -->\r\n <div *ngSwitchCase=\"'string'\"\r\n [class]=\"element.option?.visible ? 'card-field-wrapper' : 'hidden'\">\r\n\r\n <!-- LABEL -->\r\n <span *ngIf=\"element.label?.caption\"\r\n [style]=\"getStyleWithoutClasses(element.label?.style).style\"\r\n [class]=\"element.label?.style?.classes?.[0]\">\r\n {{ element.label.caption }}\r\n </span>\r\n </div>\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"mb-3\">\r\n <span *ngIf=\"element.option.protected\"\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">\r\n <vl-string [formField]=\"element\"\r\n [modalItemInfo]=\"itemInfo\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!element.option.protected\"\r\n [class]=\"element?.layout == 'horizontal' ? 'horizontal-layout' : ''\"\r\n [ngClass]=\"element?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(element?.style).style\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">{{\r\n element.label?.value\r\n ?\r\n element.label?.value : element.label?.caption ?\r\n element.label?.caption :\r\n itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <div class=\"picker-field\">\r\n <p-datepicker #cardDatePicker\r\n (ngModelChange)=\"dateChangeEvent(element, $event)\"\r\n [dateFormat]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.format ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()].format : 'yy-mm-dd'\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()].value\"\r\n (onInput)=\"onDateTyping($event)\"\r\n [class]=\"element?.scope?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(element?.scope?.style).style\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\"\r\n appendTo=\"body\"\r\n [panelStyleClass]=\"picker.datePanelClass\"\r\n [motionOptions]=\"picker.dockedMotion\"\r\n [vlCustomKeyboard]=\"element?.option?.keyboardType ?? 'date'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, cardDatePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(cardDatePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\"\r\n aria-label=\"Open calendar\"\r\n (pointerdown)=\"picker.openFromIcon($event, cardDatePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-calendar\"></i>\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\"\r\n [class]=\"element.option?.visible ? 'row' : 'hidden'\"\r\n (click)=\"invokeOnCardHeader(element)\">\r\n <button nz-button type=\"button\"\r\n [matTooltip]=\"element?.option?.tooltip || ''\"\r\n [matTooltipTouchGestures]=\"'auto'\"\r\n [ngStyle]=\"getStyleWithoutClasses(element.style).style\"\r\n [class]=\"element.option.protected ? 'disabled-button' : getStyleWithoutClasses(element.style).classes\">\r\n <ng-container\r\n *ngIf=\"!element.caption; else headerTextContent\">\r\n <i nz-icon [nzType]=\"element.icon\"\r\n nzTheme=\"outline\"></i>\r\n </ng-container>\r\n <ng-template #headerTextContent>{{ element.caption\r\n }}</ng-template>\r\n </button>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n <div class=\"card-responsive-grid\" [ngStyle]=\"getCardContainerStyle()\"\r\n [class]=\"getStyleWithoutClasses(\r\n itemInfo?.layoutSchema?.style\r\n ).classes\">\r\n <div *ngFor=\"let record of displayedRecords; let k = index\"\r\n class=\"card-grid-item\">\r\n <div [ngStyle]=\"getSingleCardStyle()\"\r\n [class]=\"getStyleWithoutClasses(itemInfo?.layoutSchema?.stylePerCard).classes\">\r\n <div *ngFor=\"let layout of itemInfo?.layoutSchema?.cards || []\"\r\n class=\"card-layout-item\" [ngStyle]=\"\r\n layout?.type === 'HorizontalLayout'\r\n ? {}\r\n : getStyleWithoutClasses(layout?.style).style\r\n \" [class]=\"\r\n layout?.type === 'HorizontalLayout'\r\n ? ''\r\n : getStyleWithoutClasses(layout?.style).classes\r\n \">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(layout?.scope?.reference && itemInfo.propertySchema[getScope(layout.scope?.reference)] ? itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.type : layout?.type == 'HorizontalLayout' ? 'HorizontalLayout' : 'default')\">\r\n <!-- Image Control -->\r\n <div *ngSwitchCase=\"'image'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <img\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [src]=\"layout?.option?.src ? layout?.option?.src : record[layout.scope?.reference.split('/').pop()]\"\r\n alt=\"{{ layout.label ? layout.label : getProperty(layout.scope?.reference).productSchema.caption }}\"\r\n (click)=\"showImageDetails(layout.option, record)\" />\r\n </div>\r\n\r\n <!-- String Control -->\r\n <div *ngSwitchCase=\"'string'\"\r\n [class]=\"layout.option?.visible ? 'card-field-wrapper' : 'hidden'\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(layout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(layout.label?.style).classes\">\r\n {{ getHorizontalLayoutCaption(layout) }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n @if(layout.option && layout.option.control &&\r\n !layout.option.protected){\r\n @switch (layout.option.control) {\r\n @case ('textInput') {\r\n <p\r\n *ngIf=\"layout.option?.protected && layout.option?.visible\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n (click)=\"showLinkDetails(layout.option, record)\">\r\n {{\r\n record?.[layout.scope?.reference.split('/').pop()]\r\n ??\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)]?.[layout.scope?.reference.split('/').pop()]?.default\r\n ??\r\n '--'\r\n }}\r\n </p>\r\n }\r\n @case ('password') {\r\n <span>Switch not supported yet</span>\r\n\r\n }\r\n @case ('combobox') {\r\n <span>Combobox not supported yet</span>\r\n }\r\n @case('textArea'){\r\n <span>TextArea not supported yet</span>\r\n }\r\n @case ('switch') {\r\n <span>Switch not supported yet</span>\r\n }\r\n\r\n @case('radiobutton') {\r\n <span>Radiobutton not supported yet</span>\r\n }\r\n\r\n @case ('dropdown') {\r\n <select id=\"dynamic-dropdown\"\r\n #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(layout?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style)?.style\"\r\n [name]=\"layout.scope?.reference\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)][\r\n layout.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of itemInfo.propertySchema[getScope(layout.scope?.reference)]?.[\r\n layout.scope?.reference.split('/').pop()\r\n ]?.data\r\n \"\r\n [value]=\"item[layout?.option?.optionValue]\">\r\n {{ item[layout?.option?.optionLabel] }}\r\n </option>\r\n </select>\r\n }\r\n\r\n @case ('autocomplete') {\r\n <p-autocomplete #autocompleteField=\"ngModel\"\r\n [required]=\"layout.option.required\"\r\n [name]=\"layout.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [class]=\"layout?.scope?.style?.classes ? layout?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"layout.option.autocomplete.response.label\"\r\n [placeholder]=\"itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.placeholder\"\r\n [vlCustomKeyboard]=\"layout?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n }\r\n }\r\n }@else {\r\n <p\r\n *ngIf=\"layout.option?.protected && layout.option?.visible\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n (click)=\"showLinkDetails(layout.option, record)\">\r\n {{\r\n record?.[layout.scope?.reference.split('/').pop()]\r\n ??\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)]?.[layout.scope?.reference.split('/').pop()]?.default\r\n ??\r\n '--'\r\n }}\r\n </p>\r\n }\r\n\r\n </div>\r\n\r\n <!-- Date Control -->\r\n <div *ngSwitchCase=\"'date'\"\r\n [class]=\"layout.option?.visible ? 'card-field-wrapper' : 'hidden'\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <!-- LABEL -->\r\n <span *ngIf=\"layout?.label?.caption\"\r\n [class]=\"layout.label?.style?.classes?.[0]\">\r\n {{ layout.label.caption }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <p\r\n *ngIf=\"layout.option?.protected && layout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n (click)=\"showLinkDetails(layout.option, record)\">\r\n {{\r\n record?.[layout.scope?.reference.split('/').pop()]\r\n ?? itemInfo.propertySchema[\r\n getScope(layout.scope?.reference)\r\n ]?.[layout.scope?.reference.split('/').pop()]?.default\r\n ?? '--'\r\n }}\r\n </p>\r\n </div>\r\n\r\n <!-- Time Control -->\r\n <div *ngSwitchCase=\"'time'\"\r\n [class]=\"layout.option?.visible ? 'card-field-wrapper' : 'hidden'\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span *ngIf=\"layout?.label?.caption\"\r\n [class]=\"layout.label?.style?.classes?.[0]\">\r\n {{ layout.label.caption }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <p\r\n *ngIf=\"layout.option?.protected && layout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n (click)=\"showLinkDetails(layout.option, record)\">\r\n {{\r\n record?.[layout.scope?.reference.split('/').pop()]\r\n ?? itemInfo.propertySchema[\r\n getScope(layout.scope?.reference)\r\n ]?.[layout.scope?.reference.split('/').pop()]?.default\r\n ?? '--'\r\n }}\r\n </p>\r\n </div>\r\n\r\n <!-- number Control -->\r\n <div *ngSwitchCase=\"'number'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.format.currency\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n {{\r\n record[layout.scope?.reference.split('/').pop()]\r\n | currency: layout.option?.currency\r\n ?\r\n layout.option.currency\r\n :\r\n 'USD' }}\r\n </h3>\r\n </div>\r\n\r\n <!-- dropdown Control -->\r\n <div *ngSwitchCase=\"'dropdown'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.format.currency\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <select id=\"dynamic-dropdown\"\r\n #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(layout?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(layout?.scope?.style)?.style\"\r\n [name]=\"layout.scope?.reference\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)][\r\n layout.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of itemInfo.propertySchema[getScope(layout.scope?.reference)]?.[\r\n layout.scope?.reference.split('/').pop()\r\n ]?.data\r\n \"\r\n [value]=\"item[layout?.option?.optionValue]\">\r\n {{ item[layout?.option?.optionLabel] }}\r\n </option>\r\n </select>\r\n </h3>\r\n </div>\r\n\r\n <!-- autocomplete Control -->\r\n <div *ngSwitchCase=\"'autocomplete'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.format.currency\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <p-autocomplete #autocompleteField=\"ngModel\"\r\n [required]=\"layout.option.required\"\r\n [name]=\"layout.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\"\r\n [class]=\"layout?.scope?.style?.classes ? layout?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"layout.option.autocomplete.response.label\"\r\n [placeholder]=\"itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]?.placeholder\"\r\n [vlCustomKeyboard]=\"layout?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n </h3>\r\n </div>\r\n\r\n <!-- checkbox Control -->\r\n <div *ngSwitchCase=\"'boolean'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(layout.scope?.reference)] && itemInfo.propertySchema[getScope(layout.scope?.reference)][layout.scope?.reference.split('/').pop()]\"\r\n [class]=\"getStyleWithoutClasses(layout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(layout?.scope?.style).style\">\r\n <input type=\"checkbox\"\r\n class=\"form-check-input mr-2\" [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(layout.scope?.reference)][\r\n layout.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [name]=\"layout.scope?.reference\" />\r\n </h3>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\"\r\n [class]=\"layout.option?.visible ? 'row' : 'hidden'\">\r\n <button\r\n [style]=\"getStyleWithoutClasses(layout?.style).style\"\r\n [class]=\"layout.option.protected ? 'disabled-button' : getStyleWithoutClasses(layout?.style).classes\"\r\n (click)=\"\r\n invokeButtonClick(\r\n layout,\r\n record,\r\n getRecordIndex(record)\r\n )\r\n\">\r\n <ng-container\r\n *ngIf=\"!layout.caption; else textContent\">\r\n <i nz-icon [nzType]=\"layout.icon\"\r\n nzTheme=\"outline\"></i>\r\n </ng-container>\r\n <ng-template #textContent>\r\n {{ layout.caption }}\r\n </ng-template>\r\n </button>\r\n </div>\r\n\r\n <!-- HorizontalLayout Control -->\r\n <div *ngSwitchCase=\"'HorizontalLayout'\">\r\n <div class=\"card-horizontal-layout\"\r\n [class]=\"getStyleWithoutClasses(\r\n layout?.style\r\n ).classes\" [ngStyle]=\"getHorizontalLayoutStyle(layout)\">\r\n <ng-container\r\n *ngFor=\"let innerLayout of layout.elements\">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(innerLayout.scope?.reference && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] ? itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.type : 'default')\">\r\n <!-- Image Control -->\r\n <div *ngSwitchCase=\"'image'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n <img\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n [src]=\"innerLayout?.option?.src ? innerLayout?.option?.src : record[innerLayout.scope?.reference.split('/').pop()]\"\r\n alt=\"{{ innerLayout.label ? innerLayout.label : getProperty(innerLayout.scope?.reference).productSchema.caption }}\"\r\n (click)=\"showImageDetails(innerLayout.option, record)\" />\r\n </div>\r\n\r\n <!-- String Control -->\r\n <div *ngSwitchCase=\"'string'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(innerLayout.label?.style).classes\">\r\n {{\r\n getHorizontalLayoutCaption(innerLayout)\r\n }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n @if(innerLayout.option &&\r\n innerLayout.option.control &&\r\n !innerLayout.option.protected){\r\n @switch (innerLayout.option.control)\r\n {\r\n @case ('textInput') {\r\n <h3\r\n *ngIf=\"innerLayout.option?.protected && innerLayout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n (click)=\"showLinkDetails(innerLayout.option, record)\">\r\n\r\n <!-- DATE FORMAT -->\r\n <ng-container\r\n *ngIf=\"innerLayout.option?.dateFormat; else checkInnerTime\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n | date:\r\n innerLayout.option.dateFormat)\r\n : '--' }}\r\n </ng-container>\r\n\r\n <!-- TIME FORMAT -->\r\n <ng-template #checkInnerTime>\r\n <ng-container\r\n *ngIf=\"innerLayout.option?.timeFormat; else normalInnerValue\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n |\r\n date:\r\n innerLayout.option.timeFormat)\r\n : '--' }}\r\n </ng-container>\r\n </ng-template>\r\n\r\n <!-- NORMAL STRING -->\r\n <ng-template #normalInnerValue>\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ??\r\n '--' }}\r\n </ng-template>\r\n </h3>\r\n }\r\n @case ('password') {\r\n <span>Switch not supported\r\n yet</span>\r\n\r\n }\r\n @case ('combobox') {\r\n <span>Combobox not supported\r\n yet</span>\r\n }\r\n @case('textArea'){\r\n <span>TextArea not supported\r\n yet</span>\r\n }\r\n @case ('switch') {\r\n <span>Switch not supported\r\n yet</span>\r\n }\r\n\r\n @case('radiobutton') {\r\n <span>Radiobutton not supported\r\n yet</span>\r\n }\r\n\r\n @case ('dropdown') {\r\n <select id=\"dynamic-dropdown\"\r\n #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(innerLayout?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style)?.style\"\r\n [name]=\"innerLayout.scope?.reference\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][\r\n innerLayout.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of itemInfo.propertySchema[getScope(innerLayout.scope?.reference)]?.[\r\n innerLayout.scope?.reference.split('/').pop()\r\n ]?.data\r\n \"\r\n [value]=\"item[innerLayout?.option?.optionValue]\">\r\n {{\r\n item[innerLayout?.option?.optionLabel]\r\n }}\r\n </option>\r\n </select>\r\n }\r\n\r\n @case ('autocomplete') {\r\n\r\n <p-autocomplete\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"innerLayout.option.required\"\r\n [name]=\"innerLayout.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n [class]=\"innerLayout?.scope?.style?.classes ? innerLayout?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"innerLayout.option.autocomplete.response.label\"\r\n [placeholder]=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.placeholder\"\r\n [vlCustomKeyboard]=\"innerLayout?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n }\r\n }\r\n }@else{\r\n\r\n <h3\r\n *ngIf=\"innerLayout.option?.protected && innerLayout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n (click)=\"showLinkDetails(innerLayout.option, record)\">\r\n\r\n <!-- DATE FORMAT -->\r\n <ng-container\r\n *ngIf=\"innerLayout.option?.dateFormat; else checkInnerTime\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n | date:\r\n innerLayout.option.dateFormat)\r\n : '--' }}\r\n </ng-container>\r\n\r\n <!-- TIME FORMAT -->\r\n <ng-template #checkInnerTime>\r\n <ng-container\r\n *ngIf=\"innerLayout.option?.timeFormat; else normalInnerValue\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n |\r\n date:\r\n innerLayout.option.timeFormat)\r\n : '--' }}\r\n </ng-container>\r\n </ng-template>\r\n\r\n <!-- NORMAL STRING -->\r\n <ng-template #normalInnerValue>\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ??\r\n '--' }}\r\n </ng-template>\r\n </h3>\r\n }\r\n\r\n </div>\r\n\r\n <!-- number Control -->\r\n <div *ngSwitchCase=\"'number'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.format?.currency\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n {{\r\n record[innerLayout.scope?.reference.split('/').pop()]\r\n | currency:\r\n innerLayout.option?.currency ?\r\n innerLayout.option.currency :\r\n 'USD'\r\n }}\r\n </h3>\r\n </div>\r\n\r\n <!-- Date Control -->\r\n <div *ngSwitchCase=\"'date'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(innerLayout.label?.style).classes\">\r\n {{\r\n getHorizontalLayoutCaption(innerLayout)\r\n }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <h3\r\n *ngIf=\"innerLayout.option?.protected && innerLayout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n (click)=\"showLinkDetails(innerLayout.option, record)\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n | date:\r\n innerLayout.option?.dateFormat ?\r\n innerLayout.option?.dateFormat :\r\n itemInfo.propertySchema[getScope(innerLayout.scope?.reference)]?.[innerLayout.scope?.reference.split('/').pop()]?.format\r\n || 'yyyy-MM-dd') : '--' }}\r\n </h3>\r\n </div>\r\n\r\n <!-- Time Control -->\r\n <div *ngSwitchCase=\"'time'\"\r\n [ngClass]=\"[innerLayout?.option?.visible ? 'card-field-wrapper' : 'hidden',getStyleWithoutClasses(innerLayout?.style).classes]\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.style).style\">\r\n\r\n <!-- LABEL -->\r\n <span\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout.label?.style).style\"\r\n [class]=\"getStyleWithoutClasses(innerLayout.label?.style).classes\">\r\n {{\r\n getHorizontalLayoutCaption(innerLayout)\r\n }}\r\n </span>\r\n\r\n <!-- VALUE -->\r\n <h3\r\n *ngIf=\"innerLayout.option?.protected && innerLayout.option?.visible\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n (click)=\"showLinkDetails(innerLayout.option, record)\">\r\n {{\r\n record?.[innerLayout.scope?.reference.split('/').pop()]\r\n ?\r\n (record[innerLayout.scope?.reference.split('/').pop()]\r\n | date:\r\n innerLayout.option?.timeFormat ?\r\n innerLayout.option?.timeFormat\r\n :itemInfo.propertySchema[getScope(innerLayout.scope?.reference)]?.[innerLayout.scope?.reference.split('/').pop()]?.format\r\n || 'HH:mm') : '--' }}\r\n </h3>\r\n </div>\r\n\r\n <!-- dropdown Control -->\r\n <div *ngSwitchCase=\"'dropdown'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.format.currency\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n <select id=\"dynamic-dropdown\"\r\n #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(innerLayout?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(innerLayout?.scope?.style)?.style\"\r\n [name]=\"innerLayout.scope?.reference\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][\r\n innerLayout.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of itemInfo.propertySchema[getScope(innerLayout.scope?.reference)]?.[\r\n innerLayout.scope?.reference.split('/').pop()\r\n ]?.data\r\n \"\r\n [value]=\"item[innerLayout?.option?.optionValue]\">\r\n {{\r\n item[innerLayout?.option?.optionLabel]\r\n }}\r\n </option>\r\n </select>\r\n </h3>\r\n </div>\r\n\r\n <!-- autocomplete Control -->\r\n <div *ngSwitchCase=\"'autocomplete'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.format.currency\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n <p-autocomplete\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"innerLayout.option.required\"\r\n [name]=\"innerLayout.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\"\r\n [class]=\"innerLayout?.scope?.style?.classes ? innerLayout?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"innerLayout.option.autocomplete.response.label\"\r\n [placeholder]=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]?.placeholder\"\r\n [vlCustomKeyboard]=\"innerLayout?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n </h3>\r\n </div>\r\n\r\n <!-- checkbox Control -->\r\n <div *ngSwitchCase=\"'boolean'\"\r\n [class]=\"innerLayout.option?.visible ? 'row' : 'hidden'\">\r\n <h3\r\n *ngIf=\"itemInfo.propertySchema[getScope(innerLayout.scope?.reference)] && itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][innerLayout.scope?.reference.split('/').pop()]\"\r\n [class]=\"getStyleWithoutClasses(innerLayout?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.scope?.style).style\">\r\n <input type=\"checkbox\"\r\n class=\"form-check-input mr-2\"\r\n [(ngModel)]=\"\r\n itemInfo.propertySchema[getScope(innerLayout.scope?.reference)][\r\n innerLayout.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [name]=\"innerLayout.scope?.reference\" />\r\n </h3>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button nz-button\r\n [matTooltip]=\"getHorizontalLayoutTooltip(innerLayout)\"\r\n [matTooltipTouchGestures]=\"'auto'\"\r\n [style]=\"getStyleWithoutClasses(innerLayout?.style).style\"\r\n [class]=\"innerLayout.option?.visible ? innerLayout.option.protected ? 'disabled-button' : getStyleWithoutClasses(innerLayout?.style).classes : 'hidden'\"\r\n (click)=\"\r\n invokeButtonClick(\r\n innerLayout,\r\n record,\r\n getRecordIndex(record)\r\n )\r\n\">\r\n <span\r\n class=\"schema-btn-content\">\r\n <i\r\n *ngIf=\"showButtonIcon(innerLayout)\"\r\n nz-icon\r\n [nzType]=\"innerLayout.icon\"\r\n nzTheme=\"outline\">\r\n </i>\r\n\r\n <span\r\n *ngIf=\"showButtonCaption(innerLayout)\">\r\n {{ innerLayout.caption\r\n }}\r\n </span>\r\n\r\n <span *ngIf=\"\r\n !showButtonIcon(innerLayout) &&\r\n !showButtonCaption(innerLayout)\r\n \">\r\n Button\r\n </span>\r\n </span>\r\n </button>\r\n </div>\r\n\r\n </ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <div\r\n *ngIf=\"itemInfo?.footer?.elements && itemInfo?.data && itemInfo?.data.length\"\r\n [class]=\"getStyleWithoutClasses(itemInfo?.footer?.style).classes\">\r\n <span *ngFor=\"let element of itemInfo.footer?.elements; let c = index\">\r\n <ng-container\r\n [ngSwitch]=\"renderFieldType(element.scope?.reference && itemInfo.propertySchema[getScope(element.scope?.reference)] ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.type : 'default')\">\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"element.option.protected\"\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">\r\n <h5>{{\r\n itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.caption\r\n }}\r\n </h5>\r\n </span>\r\n <div *ngIf=\"!element.option.protected\">\r\n <div\r\n [class]=\"element?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(element?.label?.style).style\"\r\n [class]=\"element?.label?.style?.classes[0]\">{{\r\n element.label?.value\r\n ?\r\n element.label?.value : element.label?.caption ?\r\n element.label?.caption :\r\n itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n\r\n <nz-date-picker [required]=\"element.option.required\"\r\n [name]=\"element.scope?.reference\"\r\n [nzDisabled]=\"element?.option?.disabled\"\r\n (ngModelChange)=\"dateChangeEvent(element, $event)\"\r\n [nzFormat]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.format ? itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()]?.format : 'yyyy-MM-dd'\"\r\n [(ngModel)]=\"itemInfo.propertySchema[getScope(element.scope?.reference)][element.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(element?.style).style\"></nz-date-picker>\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Button Control -->\r\n <div *ngSwitchCase=\"'default'\"\r\n [class]=\"element.option?.visible ? 'row' : 'hidden'\"\r\n (click)=\"invokeOnCardFooter(element)\">\r\n <button nz-button\r\n [style]=\"getStyleWithoutClasses(element.style).style\"\r\n [class]=\"element.option.protected ? 'disabled-button' : getStyleWithoutClasses(element.style).classes\">\r\n <ng-container\r\n *ngIf=\"!element.caption; else textContent\">\r\n <i nz-icon [nzType]=\"element.icon\"\r\n nzTheme=\"outline\"></i>\r\n </ng-container>\r\n <ng-template #textContent>\r\n {{ element.caption }}\r\n </ng-template>\r\n </button>\r\n </div>\r\n </ng-container>\r\n </span>\r\n </div>\r\n <!-- bottom pagination on card -->\r\n <div *ngIf=\"\r\n itemInfo?.data.length && \r\n itemInfo?.layoutSchema?.pagination?.visible &&\r\n !itemInfo?.layoutSchema?.pagination?.top &&\r\n !scrollPagination\r\n \" class=\"card-pagination-wrapper\">\r\n <vl-pagination [itemInfo]=\"itemInfo\"\r\n [selectedLanguageInfo]=\"selectedLanguageInfo\" [pageSize]=\"pageSize\"\r\n [currentPage]=\"currentPage\" [pages]=\"pages\"\r\n (pageSizeChange)=\"invokeOnPageSizeChange($event)\"\r\n (onGoToPage)=\"invokeGoToPage($event)\">\r\n </vl-pagination>\r\n </div>\r\n\r\n @if (scrollPagination) {\r\n <div #scrollPaginationSentinel class=\"scroll-pagination-sentinel\"\r\n aria-hidden=\"true\">\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block;width:100%;min-width:0}.vl-card-runtime{min-width:0;box-sizing:border-box}.card-responsive-grid{width:100%;min-width:0;box-sizing:border-box}.scroll-pagination-sentinel{width:100%;height:1px}.card-grid-item,.card-layout-item,.card-horizontal-layout,.card-field-wrapper{width:100%;min-width:0;box-sizing:border-box}.card-field-wrapper{overflow-wrap:anywhere}.card-field-wrapper p,.card-field-wrapper h3{margin:0;min-width:0}.card-layout-item img{display:block;max-width:100%;height:auto}.card-layout-item button{max-width:100%;box-sizing:border-box}.schema-btn-content{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:0}.hidden{display:none!important}.card-pagination-wrapper{width:100%;min-width:0;box-sizing:border-box;padding:0 10px}\n"] }]
2881
3435
  }], ctorParameters: () => [{ type: UtilityService }, { type: i1.Platform }, { type: i1$1.HttpClient }, { type: ConfigService }, { type: i0.ChangeDetectorRef }, { type: i0.NgZone }, { type: i5$1.NzModalService }, { type: NotificationService }, { type: i7.OAuthService }, { type: RequestService }], propDecorators: { itemInfo: [{
2882
3436
  type: Input
2883
3437
  }], currentPage: [{
@@ -2971,9 +3525,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
2971
3525
  type: Input
2972
3526
  }] } });
2973
3527
 
3528
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07
3529
+ // - change: date and time fields use DateTimePickerService like the dialog - toolkit keyboard in
3530
+ // the field, calendar docked at the bottom from the icon, time picker at its field. The empty
3531
+ // picker handlers they called before are gone
2974
3532
  // Shuaib 2026-09-24
2975
3533
  // - new: renderFieldType maps legacy checkbox fields to boolean so existing forms continue to render.
2976
3534
  class FormComponent {
3535
+ /** Date and time picker behaviour shared by all containers. */
3536
+ picker = inject(DateTimePickerService);
3537
+ host = inject((ElementRef));
3538
+ ngOnDestroy() {
3539
+ this.picker.closeIfWithin(this.host.nativeElement);
3540
+ }
2977
3541
  /** Keep existing checkbox data objects usable; new objects use boolean. */
2978
3542
  renderFieldType(type) {
2979
3543
  return type === 'checkbox' ? 'boolean' : type;
@@ -3219,21 +3783,14 @@ class FormComponent {
3219
3783
  const translation = this.selectedLanguageInfo?.selectedLanguage;
3220
3784
  return translation && translation[caption] ? translation[caption] : caption;
3221
3785
  }
3222
- onPickerContainerPointerDown(event, datePicker) { }
3223
- onPickerInputFocus(event, datePicker) { }
3224
- openPickerFromIcon(event, datePicker) { }
3225
- onPickerShown() { }
3226
- onPickerClose() { }
3227
3786
  dateChangeEvent(formField, event) { }
3228
3787
  onDateTyping(event) { }
3229
3788
  onTimeTyping(event) { }
3230
- onTimePanelShow(formField) { }
3231
- blockPickerIconClick(event) { }
3232
3789
  timeChangeEvent(formField, event) { }
3233
3790
  autocompChangeEvent(event, formfield) { }
3234
3791
  autocompFocusOutEvent(event, formfield) { }
3235
3792
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: FormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3236
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.2", type: FormComponent, isStandalone: true, selector: "vl-form", inputs: { errorMessages: "errorMessages", creditCardError: "creditCardError", form: "form", selectedLanguageInfo: "selectedLanguageInfo" }, outputs: { formButtonClick: "formButtonClick", checkBoxChangeEvent: "checkBoxChangeEvent" }, usesOnChanges: true, ngImport: i0, template: "<!-- Shuaib 2026-09-24\r\n - change: the form field switch uses renderFieldType so legacy checkbox fields render as boolean. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - change: the credit card type is spelled creditCard. The rename of 2026-09-15 reached\r\n model.component.html and the whole Designer but not this file, so one data object rendered\r\n in a modal and stayed empty here\r\n - new: a @contract comment on the type switch and on the credit card case, so the rule sits\r\n next to the code that implements it -->\r\n<div class=\"form-renderer\"\r\n [ngClass]=\"getStyleWithoutClasses(form?.layoutSchema?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(form?.layoutSchema?.style)?.style\">\r\n <form *ngIf=\"form?.layoutSchema?.formSections?.length > 0\">\r\n <div\r\n *ngFor=\"let formSection of form?.layoutSchema?.formSections; let i = index\"\r\n class=\"section-card\">\r\n <div *ngIf=\"formSection?.label?.caption\" class=\"section-header\">\r\n <div class=\"section-title\"\r\n [ngClass]=\"getStyleWithoutClasses(formSection?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formSection?.label?.style)?.style\">\r\n {{ formSection.label.caption }}\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"formSection?.formFields?.length > 0\" class=\"section-body\"\r\n [ngClass]=\"getStyleWithoutClasses(formSection?.style)?.classes\"\r\n [ngStyle]=\"getSectionBodyStyle(formSection)\">\r\n <div *ngFor=\"let formField of formSection.formFields\" class=\"field-row\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.style)?.classes\"\r\n [ngStyle]=\"getFieldWrapperStyle(formField)\">\r\n <!-- @contract decision: this switch decides whether a field appears at all\r\n reality: renders string, number, image, date, time, dropdown, boolean,\r\n autocomplete, dateRange and the credit card type. Everything else\r\n falls into the default branch and the field renders empty - no error,\r\n nothing in the console either. Existing checkbox fields are normalized\r\n to boolean before this switch; new fields should use boolean.\r\n seen: measured 2026-09-19 with viewdefinition-contract/derive-capabilities.sh\r\n against this file; date, time, dropdown, boolean, autocomplete and\r\n dateRange arrived on 2026-09-18 -->\r\n <div [ngSwitch]=\"renderFieldType(\r\n formField?.scope?.reference &&\r\n form?.propertySchema?.[getScope(formField.scope?.reference)]\r\n ? form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.type\r\n : 'default'\r\n )\">\r\n <!-- image control -->\r\n <div *ngSwitchCase=\"'image'\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <img [src]=\"formField?.option?.src\" [alt]=\"'img'\" />\r\n </div>\r\n\r\n <!-- credit card control -->\r\n <!-- @contract decision: the credit card type is spelled creditCard\r\n reality: it was carditCard here until 2026-09-19. The rename of 2026-09-15\r\n reached model.component.html and the whole Designer, but not this\r\n file and not utility.service.ts, so the same data object rendered in\r\n one container and stayed empty in the other. All four places agree now.\r\n failure: a data object still declaring carditCard renders empty everywhere -\r\n that spelling is read nowhere any more.\r\n instead: declare the type as creditCard. -->\r\n <div *ngSwitchCase=\"'creditCard'\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <div id=\"card-element\"></div>\r\n <div id=\"card-errors\"\r\n [class]=\"creditCardError?.textContent !== '' ? 'text-red-400 m-0' : ''\"></div>\r\n </div>\r\n\r\n <!-- string control -->\r\n <span *ngSwitchCase=\"'string'\" class=\"form-group\">\r\n <span *ngIf=\"formField?.option?.protected\">\r\n <h5 style=\"display: flex; justify-content: space-between\">\r\n <span\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </span>\r\n\r\n <span>\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.value\r\n }}\r\n </span>\r\n </h5>\r\n </span>\r\n\r\n <span *ngIf=\"!formField?.option?.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n formField?.label?.value\r\n ? formField.label.value\r\n : formField?.label?.caption\r\n ? formField.label.caption\r\n :\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n\r\n <div [ngStyle]=\"\r\n formField?.option?.password\r\n ? {\r\n position: 'relative',\r\n display: 'flex',\r\n alignItems: 'center'\r\n }\r\n : {}\r\n \">\r\n <input [type]=\"\r\n formField?.option?.password\r\n ? !showPassword\r\n ? 'password'\r\n : 'text'\r\n : 'text'\r\n \" #inputField=\"ngModel\" [required]=\"formField?.required\"\r\n [attr.inputmode]=\"\r\n formField?.option?.email\r\n ? 'email'\r\n : formField?.option?.password\r\n ? 'numeric'\r\n : 'text'\r\n \" [pattern]=\"formField?.email ? customEmailPattern : ''\"\r\n [email]=\"formField?.email\"\r\n [minlength]=\"formField?.minlength\"\r\n [maxlength]=\"formField?.maxlength\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\" [placeholder]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.placeholder\r\n \" />\r\n\r\n <i *ngIf=\"formField?.option?.password\" class=\"fa\"\r\n [ngClass]=\"showPassword ? 'fa-eye' : 'fa-eye-slash'\"\r\n (click)=\"togglePasswordVisibility()\"\r\n style=\"position: absolute; right: 10px; cursor: pointer\"></i>\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"inputField?.touched && inputField?.invalid\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.validation?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.validation?.style)?.style\">\r\n <p [class]=\"\r\n formField?.layout === 'horizontal'\r\n ? 'text-red-400 m-0 horizontal-error'\r\n : 'text-red-400 m-0'\r\n \" *ngIf=\"inputField?.errors\">\r\n {{ setErroMessage(formField, errorMessages, inputField) }}\r\n </p>\r\n </div>\r\n </span>\r\n </span>\r\n\r\n <!-- number control -->\r\n <div *ngSwitchCase=\"'number'\" class=\"form-group\">\r\n <span *ngIf=\"formField?.option?.protected\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n <h5>\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.value\r\n | currency\r\n : formField?.option?.currency\r\n : true\r\n : formField?.option?.decimalPoint\r\n }}\r\n </h5>\r\n </span>\r\n\r\n <span *ngIf=\"!formField?.option?.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n formField?.label?.value\r\n ? formField.label.value\r\n :\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n\r\n <input type=\"number\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\" [placeholder]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.placeholder\r\n \" />\r\n </div>\r\n </span>\r\n </div>\r\n\r\n <!-- checkbox control -->\r\n <div *ngSwitchCase=\"'boolean'\" class=\"form-group\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <span *ngIf=\"formField?.option?.postfixCaption\">\r\n <label class=\"form-check-label\">\r\n <input type=\"checkbox\" class=\"form-check-input mr-2\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\"\r\n (ngModelChange)=\"setformData()\" />\r\n\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n </span>\r\n\r\n <div *ngIf=\"!formField?.option?.postfixCaption\"\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label class=\"form-check-label\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n\r\n <input type=\"checkbox\" class=\"form-check-input\" [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\"\r\n (ngModelChange)=\"setformData()\" />\r\n </label>\r\n </div>\r\n </div>\r\n\r\n <!-- DateRange control -->\r\n <div *ngSwitchCase=\"'dateRange'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{\r\n formField.label?.value\r\n ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <nz-range-picker #dateRangeField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [(ngModel)]=\"form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"></nz-range-picker>\r\n </div>\r\n <!-- Error Message -->\r\n <div *ngIf=\"dateRangeField?.touched && dateRangeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['required']\">\r\n {{formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{getLabel(formField)}}</label>\r\n\r\n <span>\r\n <p-datepicker #datePicker #dateField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [disabled]=\"formField?.option?.disabled\" [showIcon]=\"true\"\r\n [showOnFocus]=\"false\" appendTo=\"body\"\r\n [autoZIndex]=\"true\" [baseZIndex]=\"10000\"\r\n panelStyleClass=\"model-datepicker-panel\"\r\n (pointerdown)=\"onPickerContainerPointerDown($event, datePicker)\"\r\n (onFocus)=\"onPickerInputFocus($event, datePicker)\"\r\n (onShow)=\"onPickerShown()\"\r\n (onClose)=\"onPickerClose()\"\r\n (ngModelChange)=\"dateChangeEvent(formField, $event)\"\r\n (onInput)=\"onDateTyping($event)\" [dateFormat]=\"\r\n form.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ]?.format || 'yy-mm-dd'\r\n \" [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [class]=\"formField?.scope?.style?.classes?.[0] || ''\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\">\r\n <ng-template #inputicon>\r\n <i class=\"pi pi-calendar\" aria-label=\"Open calendar\"\r\n (pointerdown)=\"openPickerFromIcon($event, datePicker)\"\r\n (click)=\"blockPickerIconClick($event)\">\r\n </i>\r\n </ng-template>\r\n </p-datepicker>\r\n <!-- Error Message -->\r\n <div *ngIf=\"dateField?.touched && dateField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages,\r\n \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Time control -->\r\n <div *ngSwitchCase=\"'time'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{getLabel(formField)}}</label>\r\n <span>\r\n <p-datepicker #timePicker #timeField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\" [timeOnly]=\"true\"\r\n hourFormat=\"24\" [disabled]=\"formField?.option?.disabled\"\r\n [showIcon]=\"true\" [showOnFocus]=\"false\"\r\n appendTo=\"body\" [autoZIndex]=\"true\" [baseZIndex]=\"10000\"\r\n panelStyleClass=\"model-datepicker-panel\"\r\n (pointerdown)=\"onPickerContainerPointerDown($event, timePicker)\"\r\n (onFocus)=\"onPickerInputFocus($event, timePicker)\"\r\n (onShow)=\"onTimePanelShow(formField)\"\r\n (onClose)=\"onPickerClose()\"\r\n (onInput)=\"onTimeTyping($event)\"\r\n (ngModelChange)=\"timeChangeEvent(formField, $event)\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"formField?.scope?.style?.classes?.[0] || ''\">\r\n\r\n <ng-template #inputicon>\r\n <i class=\"pi pi-clock\" aria-label=\"Open time picker\"\r\n (pointerdown)=\"openPickerFromIcon($event, timePicker)\"\r\n (click)=\"blockPickerIconClick($event)\">\r\n </i>\r\n </ng-template>\r\n </p-datepicker>\r\n\r\n <!-- Error Message -->\r\n <div *ngIf=\"timeField?.touched && timeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages,\r\n \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Autocomplete control -->\r\n <div *ngSwitchCase=\"'autocomplete'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-label>\r\n\r\n <span>\r\n <p-autocomplete #autocompleteField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [class]=\"formField?.scope?.style?.classes ? formField?.scope?.style?.classes[0] : ''\"\r\n [suggestions]=\"matchData\" [dropdown]=\"true\"\r\n [optionLabel]=\"formField.option.autocomplete.response.label\"\r\n [placeholder]=\"form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.placeholder\"\r\n (completeMethod)=\"autocompChangeEvent($event, formField)\"\r\n (ngModelChange)=\"autocompFocusOutEvent($event, formField)\"\r\n [vlCustomKeyboard]=\"formField?.option?.keyboardType\">\r\n </p-autocomplete>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"autocompleteField?.touched && autocompleteField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages,\r\n \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- dropdown control -->\r\n <div *ngSwitchCase=\"'dropdown'\" class=\"form-group\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <span *ngIf=\"formField?.option?.postfixCaption\">\r\n <select id=\"dynamic-dropdown\" #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.data\r\n \" [value]=\"item[formField?.option?.optionValue]\">\r\n {{ item[formField?.option?.optionLabel] }}\r\n </option>\r\n </select>\r\n\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n </span>\r\n\r\n <div *ngIf=\"!formField?.option?.postfixCaption\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n\r\n <nz-select nzShowSearch nzAllowClear nzPlaceHolder=\"Select\"\r\n #dropdownField=\"ngModel\"\r\n [required]=\"formField?.required\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [name]=\"formField.scope?.reference\" [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <nz-option *ngFor=\"\r\n let item of form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.data\r\n \" [nzLabel]=\"item[formField?.option?.optionLabel]\"\r\n [nzValue]=\"item[formField?.option?.optionValue]\"></nz-option>\r\n </nz-select>\r\n </div>\r\n\r\n <div *ngIf=\"dropdownField?.touched && dropdownField?.invalid\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.validation?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.validation?.style)?.style\">\r\n <p [class]=\"\r\n formField?.layout === 'horizontal'\r\n ? 'text-red-400 m-0 horizontal-error'\r\n : 'text-red-400 m-0'\r\n \" *ngIf=\"dropdownField?.errors\">\r\n {{ setErroMessage(formField, errorMessages, dropdownField)\r\n }}\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- button/default control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button type=\"submit\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.style)?.style\"\r\n (click)=\"invokeFormBtnClick(formField?.option, form.propertySchema)\">\r\n {{ formField?.label?.caption }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </form>\r\n</div>\r\n", styles: [":host{display:block;width:100%;height:100%;min-height:0}.form-renderer{width:100%;height:100%;min-height:0;overflow-y:auto;overflow-x:hidden;box-sizing:border-box;padding:12px}.section-card{border:1px solid #e5e7eb;border-radius:var(--radius-sm, 8px);padding:12px;margin-bottom:12px;background:#fff;box-sizing:border-box}.section-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;font-weight:600}.section-title{font-size:14px;color:#0f172a}.section-body{position:relative;width:100%;min-height:78px;box-sizing:border-box}.field-row{position:relative;min-width:0;width:100%;box-sizing:border-box}.field-row input,.field-row select,.field-row nz-select,.field-row textarea,.field-row button{max-width:100%;box-sizing:border-box}@media(max-width:768px){.section-body{grid-template-columns:repeat(1,minmax(0,1fr))!important}.field-row{grid-column:span 1!important}}@media(min-width:769px)and (max-width:1024px){.section-body{grid-template-columns:repeat(6,minmax(0,1fr))!important}.field-row{grid-column:span 6!important}}:host-context(.mobile-frame) .section-body,:host-context(.device-mobile) .section-body{grid-template-columns:repeat(1,minmax(0,1fr))!important}:host-context(.mobile-frame) .field-row,:host-context(.device-mobile) .field-row{grid-column:span 1!important}:host-context(.tablet-frame) .section-body,:host-context(.device-tablet) .section-body{grid-template-columns:repeat(6,minmax(0,1fr))!important}:host-context(.tablet-frame) .field-row,:host-context(.device-tablet) .field-row{grid-column:span 6!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i5.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i5.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.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: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i2.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i2.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i2.EmailValidator, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: ["email"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: NzSelectModule }, { kind: "component", type: i3.NzOptionComponent, selector: "nz-option", inputs: ["nzTitle", "nzLabel", "nzValue", "nzKey", "nzDisabled", "nzHide", "nzCustomContent"], exportAs: ["nzOption"] }, { kind: "component", type: i3.NzSelectComponent, selector: "nz-select", inputs: ["nzId", "nzSize", "nzStatus", "nzVariant", "nzOptionHeightPx", "nzOptionOverflowSize", "nzDropdownClassName", "nzDropdownMatchSelectWidth", "nzDropdownStyle", "nzNotFoundContent", "nzPlaceHolder", "nzPlacement", "nzMaxTagCount", "nzDropdownRender", "nzCustomTemplate", "nzPrefix", "nzSuffixIcon", "nzClearIcon", "nzRemoveIcon", "nzMenuItemSelectedIcon", "nzTokenSeparators", "nzMaxTagPlaceholder", "nzMaxMultipleCount", "nzMode", "nzFilterOption", "compareWith", "nzAllowClear", "nzShowSearch", "nzLoading", "nzAutoFocus", "nzAutoClearSearchValue", "nzServerSearch", "nzDisabled", "nzOpen", "nzSelectOnTab", "nzBackdrop", "nzOptions", "nzShowArrow"], outputs: ["nzOnSearch", "nzScrollToBottom", "nzOpenChange", "nzBlur", "nzFocus", "nzOnClear"], exportAs: ["nzSelect"] }, { kind: "component", type: String$1, selector: "vl-string", inputs: ["formField", "modalItemInfo"] }, { kind: "component", type: Label, selector: "vl-label", inputs: ["formField", "modalItemInfo"] }, { kind: "ngmodule", type: NzDatePickerModule }, { kind: "component", type: i4.NzDatePickerComponent, selector: "nz-date-picker,nz-week-picker,nz-month-picker,nz-quarter-picker,nz-year-picker,nz-range-picker", inputs: ["nzInline", "nzAllowClear", "nzAutoFocus", "nzDisabled", "nzInputReadOnly", "nzOpen", "nzDisabledDate", "nzLocale", "nzPlaceHolder", "nzPopupStyle", "nzDropdownClassName", "nzSize", "nzStatus", "nzFormat", "nzVariant", "nzDateRender", "nzDisabledTime", "nzRenderExtraFooter", "nzShowToday", "nzMode", "nzShowNow", "nzRanges", "nzDefaultPickerValue", "nzSeparator", "nzSuffixIcon", "nzBackdrop", "nzId", "nzPlacement", "nzShowWeekNumber", "nzShowTime"], outputs: ["nzOnPanelChange", "nzOnCalendarChange", "nzOnOk", "nzOnOpenChange"], exportAs: ["nzDatePicker"] }, { kind: "directive", type: i4.NzRangePickerComponent, selector: "nz-range-picker", exportAs: ["nzRangePicker"] }, { kind: "ngmodule", type: DatePickerModule }, { kind: "component", type: i12.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: AutoCompleteModule }, { kind: "component", type: i18.AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "minQueryLength", "delay", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "addOnTab", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "addOnBlur", "separator", "appendTo", "motionOptions"], outputs: ["completeMethod", "onSelect", "onUnselect", "onAdd", "onFocus", "onBlur", "onDropdownClick", "onClear", "onInputKeydown", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: CustomKeyboardDirective, selector: "[vlCustomKeyboard]", inputs: ["vlCustomKeyboard"] }, { kind: "pipe", type: i5.CurrencyPipe, name: "currency" }] });
3793
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.2", type: FormComponent, isStandalone: true, selector: "vl-form", inputs: { errorMessages: "errorMessages", creditCardError: "creditCardError", form: "form", selectedLanguageInfo: "selectedLanguageInfo" }, outputs: { formButtonClick: "formButtonClick", checkBoxChangeEvent: "checkBoxChangeEvent" }, usesOnChanges: true, ngImport: i0, template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\r\n - change: autocomplete fields take the toolkit string keyboard unless option.keyboardType\r\n names another one ('native' keeps the system keyboard)\r\n - change: date and time fields use the shared picker markup (picker-field, DateTimePickerService)\r\n instead of PrimeNG's own icon with empty handlers -->\r\n\r\n<!-- Shuaib 2026-09-24\r\n - change: the form field switch uses renderFieldType so legacy checkbox fields render as boolean. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - change: the credit card type is spelled creditCard. The rename of 2026-09-15 reached\r\n model.component.html and the whole Designer but not this file, so one data object rendered\r\n in a modal and stayed empty here\r\n - new: a @contract comment on the type switch and on the credit card case, so the rule sits\r\n next to the code that implements it -->\r\n<div class=\"form-renderer\"\r\n [ngClass]=\"getStyleWithoutClasses(form?.layoutSchema?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(form?.layoutSchema?.style)?.style\">\r\n <form *ngIf=\"form?.layoutSchema?.formSections?.length > 0\">\r\n <div\r\n *ngFor=\"let formSection of form?.layoutSchema?.formSections; let i = index\"\r\n class=\"section-card\">\r\n <div *ngIf=\"formSection?.label?.caption\" class=\"section-header\">\r\n <div class=\"section-title\"\r\n [ngClass]=\"getStyleWithoutClasses(formSection?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formSection?.label?.style)?.style\">\r\n {{ formSection.label.caption }}\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"formSection?.formFields?.length > 0\" class=\"section-body\"\r\n [ngClass]=\"getStyleWithoutClasses(formSection?.style)?.classes\"\r\n [ngStyle]=\"getSectionBodyStyle(formSection)\">\r\n <div *ngFor=\"let formField of formSection.formFields\" class=\"field-row\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.style)?.classes\"\r\n [ngStyle]=\"getFieldWrapperStyle(formField)\">\r\n <!-- @contract decision: this switch decides whether a field appears at all\r\n reality: renders string, number, image, date, time, dropdown, boolean,\r\n autocomplete, dateRange and the credit card type. Everything else\r\n falls into the default branch and the field renders empty - no error,\r\n nothing in the console either. Existing checkbox fields are normalized\r\n to boolean before this switch; new fields should use boolean.\r\n seen: measured 2026-09-19 with viewdefinition-contract/derive-capabilities.sh\r\n against this file; date, time, dropdown, boolean, autocomplete and\r\n dateRange arrived on 2026-09-18 -->\r\n <div [ngSwitch]=\"renderFieldType(\r\n formField?.scope?.reference &&\r\n form?.propertySchema?.[getScope(formField.scope?.reference)]\r\n ? form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.type\r\n : 'default'\r\n )\">\r\n <!-- image control -->\r\n <div *ngSwitchCase=\"'image'\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <img [src]=\"formField?.option?.src\" [alt]=\"'img'\" />\r\n </div>\r\n\r\n <!-- credit card control -->\r\n <!-- @contract decision: the credit card type is spelled creditCard\r\n reality: it was carditCard here until 2026-09-19. The rename of 2026-09-15\r\n reached model.component.html and the whole Designer, but not this\r\n file and not utility.service.ts, so the same data object rendered in\r\n one container and stayed empty in the other. All four places agree now.\r\n failure: a data object still declaring carditCard renders empty everywhere -\r\n that spelling is read nowhere any more.\r\n instead: declare the type as creditCard. -->\r\n <div *ngSwitchCase=\"'creditCard'\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <div id=\"card-element\"></div>\r\n <div id=\"card-errors\"\r\n [class]=\"creditCardError?.textContent !== '' ? 'text-red-400 m-0' : ''\"></div>\r\n </div>\r\n\r\n <!-- string control -->\r\n <span *ngSwitchCase=\"'string'\" class=\"form-group\">\r\n <span *ngIf=\"formField?.option?.protected\">\r\n <h5 style=\"display: flex; justify-content: space-between\">\r\n <span\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </span>\r\n\r\n <span>\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.value\r\n }}\r\n </span>\r\n </h5>\r\n </span>\r\n\r\n <span *ngIf=\"!formField?.option?.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n formField?.label?.value\r\n ? formField.label.value\r\n : formField?.label?.caption\r\n ? formField.label.caption\r\n :\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n\r\n <div [ngStyle]=\"\r\n formField?.option?.password\r\n ? {\r\n position: 'relative',\r\n display: 'flex',\r\n alignItems: 'center'\r\n }\r\n : {}\r\n \">\r\n <input [type]=\"\r\n formField?.option?.password\r\n ? !showPassword\r\n ? 'password'\r\n : 'text'\r\n : 'text'\r\n \" #inputField=\"ngModel\" [required]=\"formField?.required\"\r\n [attr.inputmode]=\"\r\n formField?.option?.email\r\n ? 'email'\r\n : formField?.option?.password\r\n ? 'numeric'\r\n : 'text'\r\n \" [pattern]=\"formField?.email ? customEmailPattern : ''\"\r\n [email]=\"formField?.email\"\r\n [minlength]=\"formField?.minlength\"\r\n [maxlength]=\"formField?.maxlength\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\" [placeholder]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.placeholder\r\n \" />\r\n\r\n <i *ngIf=\"formField?.option?.password\" class=\"fa\"\r\n [ngClass]=\"showPassword ? 'fa-eye' : 'fa-eye-slash'\"\r\n (click)=\"togglePasswordVisibility()\"\r\n style=\"position: absolute; right: 10px; cursor: pointer\"></i>\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"inputField?.touched && inputField?.invalid\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.validation?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.validation?.style)?.style\">\r\n <p [class]=\"\r\n formField?.layout === 'horizontal'\r\n ? 'text-red-400 m-0 horizontal-error'\r\n : 'text-red-400 m-0'\r\n \" *ngIf=\"inputField?.errors\">\r\n {{ setErroMessage(formField, errorMessages, inputField) }}\r\n </p>\r\n </div>\r\n </span>\r\n </span>\r\n\r\n <!-- number control -->\r\n <div *ngSwitchCase=\"'number'\" class=\"form-group\">\r\n <span *ngIf=\"formField?.option?.protected\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n <h5>\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.value\r\n | currency\r\n : formField?.option?.currency\r\n : true\r\n : formField?.option?.decimalPoint\r\n }}\r\n </h5>\r\n </span>\r\n\r\n <span *ngIf=\"!formField?.option?.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n formField?.label?.value\r\n ? formField.label.value\r\n :\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n\r\n <input type=\"number\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\" [placeholder]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.placeholder\r\n \" />\r\n </div>\r\n </span>\r\n </div>\r\n\r\n <!-- checkbox control -->\r\n <div *ngSwitchCase=\"'boolean'\" class=\"form-group\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <span *ngIf=\"formField?.option?.postfixCaption\">\r\n <label class=\"form-check-label\">\r\n <input type=\"checkbox\" class=\"form-check-input mr-2\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\"\r\n (ngModelChange)=\"setformData()\" />\r\n\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n </span>\r\n\r\n <div *ngIf=\"!formField?.option?.postfixCaption\"\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label class=\"form-check-label\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n\r\n <input type=\"checkbox\" class=\"form-check-input\" [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\"\r\n (ngModelChange)=\"setformData()\" />\r\n </label>\r\n </div>\r\n </div>\r\n\r\n <!-- DateRange control -->\r\n <div *ngSwitchCase=\"'dateRange'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{\r\n formField.label?.value\r\n ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <nz-range-picker #dateRangeField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [(ngModel)]=\"form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"></nz-range-picker>\r\n </div>\r\n <!-- Error Message -->\r\n <div *ngIf=\"dateRangeField?.touched && dateRangeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['required']\">\r\n {{formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{getLabel(formField)}}</label>\r\n\r\n <span>\r\n <div class=\"picker-field\">\r\n <p-datepicker #datePicker #dateField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n [autoZIndex]=\"true\" [baseZIndex]=\"10000\"\r\n (ngModelChange)=\"dateChangeEvent(formField, $event)\"\r\n (onInput)=\"onDateTyping($event)\" [dateFormat]=\"\r\n form.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ]?.format || 'yy-mm-dd'\r\n \" [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [class]=\"formField?.scope?.style?.classes?.[0] || ''\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\" appendTo=\"body\"\r\n [panelStyleClass]=\"picker.datePanelClass\" [motionOptions]=\"picker.dockedMotion\"\r\n [vlCustomKeyboard]=\"formField?.option?.keyboardType ?? 'date'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, datePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(datePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\" aria-label=\"Open calendar\"\r\n (pointerdown)=\"picker.openFromIcon($event, datePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-calendar\"></i>\r\n </button>\r\n </div>\r\n <!-- Error Message -->\r\n <div *ngIf=\"dateField?.touched && dateField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages,\r\n \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Time control -->\r\n <div *ngSwitchCase=\"'time'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{getLabel(formField)}}</label>\r\n <span>\r\n <div class=\"picker-field\">\r\n <p-datepicker #timePicker #timeField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\" [timeOnly]=\"true\"\r\n hourFormat=\"24\" [disabled]=\"formField?.option?.disabled\" [autoZIndex]=\"true\" [baseZIndex]=\"10000\"\r\n (onInput)=\"onTimeTyping($event)\"\r\n (ngModelChange)=\"timeChangeEvent(formField, $event)\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"formField?.scope?.style?.classes?.[0] || ''\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\" appendTo=\"body\"\r\n [panelStyleClass]=\"picker.timePanelClass\"\r\n [vlCustomKeyboard]=\"formField?.option?.keyboardType ?? 'time'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, timePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(timePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\" aria-label=\"Open time picker\"\r\n (pointerdown)=\"picker.openFromIcon($event, timePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-clock\"></i>\r\n </button>\r\n </div>\r\n\r\n <!-- Error Message -->\r\n <div *ngIf=\"timeField?.touched && timeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages,\r\n \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Autocomplete control -->\r\n <div *ngSwitchCase=\"'autocomplete'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-label>\r\n\r\n <span>\r\n <p-autocomplete #autocompleteField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [class]=\"formField?.scope?.style?.classes ? formField?.scope?.style?.classes[0] : ''\"\r\n [suggestions]=\"matchData\" [dropdown]=\"true\"\r\n [optionLabel]=\"formField.option.autocomplete.response.label\"\r\n [placeholder]=\"form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.placeholder\"\r\n (completeMethod)=\"autocompChangeEvent($event, formField)\"\r\n (ngModelChange)=\"autocompFocusOutEvent($event, formField)\"\r\n [vlCustomKeyboard]=\"formField?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"autocompleteField?.touched && autocompleteField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages,\r\n \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- dropdown control -->\r\n <div *ngSwitchCase=\"'dropdown'\" class=\"form-group\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <span *ngIf=\"formField?.option?.postfixCaption\">\r\n <select id=\"dynamic-dropdown\" #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.data\r\n \" [value]=\"item[formField?.option?.optionValue]\">\r\n {{ item[formField?.option?.optionLabel] }}\r\n </option>\r\n </select>\r\n\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n </span>\r\n\r\n <div *ngIf=\"!formField?.option?.postfixCaption\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n\r\n <nz-select nzShowSearch nzAllowClear nzPlaceHolder=\"Select\"\r\n #dropdownField=\"ngModel\"\r\n [required]=\"formField?.required\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [name]=\"formField.scope?.reference\" [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <nz-option *ngFor=\"\r\n let item of form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.data\r\n \" [nzLabel]=\"item[formField?.option?.optionLabel]\"\r\n [nzValue]=\"item[formField?.option?.optionValue]\"></nz-option>\r\n </nz-select>\r\n </div>\r\n\r\n <div *ngIf=\"dropdownField?.touched && dropdownField?.invalid\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.validation?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.validation?.style)?.style\">\r\n <p [class]=\"\r\n formField?.layout === 'horizontal'\r\n ? 'text-red-400 m-0 horizontal-error'\r\n : 'text-red-400 m-0'\r\n \" *ngIf=\"dropdownField?.errors\">\r\n {{ setErroMessage(formField, errorMessages, dropdownField)\r\n }}\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- button/default control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button type=\"submit\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.style)?.style\"\r\n (click)=\"invokeFormBtnClick(formField?.option, form.propertySchema)\">\r\n {{ formField?.label?.caption }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </form>\r\n</div>\r\n", styles: [":host{display:block;width:100%;height:100%;min-height:0}.form-renderer{width:100%;height:100%;min-height:0;overflow-y:auto;overflow-x:hidden;box-sizing:border-box;padding:12px}.section-card{border:1px solid #e5e7eb;border-radius:var(--radius-sm, 8px);padding:12px;margin-bottom:12px;background:#fff;box-sizing:border-box}.section-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;font-weight:600}.section-title{font-size:14px;color:#0f172a}.section-body{position:relative;width:100%;min-height:78px;box-sizing:border-box}.field-row{position:relative;min-width:0;width:100%;box-sizing:border-box}.field-row input,.field-row select,.field-row nz-select,.field-row textarea,.field-row button{max-width:100%;box-sizing:border-box}@media(max-width:768px){.section-body{grid-template-columns:repeat(1,minmax(0,1fr))!important}.field-row{grid-column:span 1!important}}@media(min-width:769px)and (max-width:1024px){.section-body{grid-template-columns:repeat(6,minmax(0,1fr))!important}.field-row{grid-column:span 6!important}}:host-context(.mobile-frame) .section-body,:host-context(.device-mobile) .section-body{grid-template-columns:repeat(1,minmax(0,1fr))!important}:host-context(.mobile-frame) .field-row,:host-context(.device-mobile) .field-row{grid-column:span 1!important}:host-context(.tablet-frame) .section-body,:host-context(.device-tablet) .section-body{grid-template-columns:repeat(6,minmax(0,1fr))!important}:host-context(.tablet-frame) .field-row,:host-context(.device-tablet) .field-row{grid-column:span 6!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i5.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i5.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.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: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i2.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i2.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i2.EmailValidator, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: ["email"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: NzSelectModule }, { kind: "component", type: i3.NzOptionComponent, selector: "nz-option", inputs: ["nzTitle", "nzLabel", "nzValue", "nzKey", "nzDisabled", "nzHide", "nzCustomContent"], exportAs: ["nzOption"] }, { kind: "component", type: i3.NzSelectComponent, selector: "nz-select", inputs: ["nzId", "nzSize", "nzStatus", "nzVariant", "nzOptionHeightPx", "nzOptionOverflowSize", "nzDropdownClassName", "nzDropdownMatchSelectWidth", "nzDropdownStyle", "nzNotFoundContent", "nzPlaceHolder", "nzPlacement", "nzMaxTagCount", "nzDropdownRender", "nzCustomTemplate", "nzPrefix", "nzSuffixIcon", "nzClearIcon", "nzRemoveIcon", "nzMenuItemSelectedIcon", "nzTokenSeparators", "nzMaxTagPlaceholder", "nzMaxMultipleCount", "nzMode", "nzFilterOption", "compareWith", "nzAllowClear", "nzShowSearch", "nzLoading", "nzAutoFocus", "nzAutoClearSearchValue", "nzServerSearch", "nzDisabled", "nzOpen", "nzSelectOnTab", "nzBackdrop", "nzOptions", "nzShowArrow"], outputs: ["nzOnSearch", "nzScrollToBottom", "nzOpenChange", "nzBlur", "nzFocus", "nzOnClear"], exportAs: ["nzSelect"] }, { kind: "component", type: String$1, selector: "vl-string", inputs: ["formField", "modalItemInfo"] }, { kind: "component", type: Label, selector: "vl-label", inputs: ["formField", "modalItemInfo"] }, { kind: "ngmodule", type: NzDatePickerModule }, { kind: "component", type: i4.NzDatePickerComponent, selector: "nz-date-picker,nz-week-picker,nz-month-picker,nz-quarter-picker,nz-year-picker,nz-range-picker", inputs: ["nzInline", "nzAllowClear", "nzAutoFocus", "nzDisabled", "nzInputReadOnly", "nzOpen", "nzDisabledDate", "nzLocale", "nzPlaceHolder", "nzPopupStyle", "nzDropdownClassName", "nzSize", "nzStatus", "nzFormat", "nzVariant", "nzDateRender", "nzDisabledTime", "nzRenderExtraFooter", "nzShowToday", "nzMode", "nzShowNow", "nzRanges", "nzDefaultPickerValue", "nzSeparator", "nzSuffixIcon", "nzBackdrop", "nzId", "nzPlacement", "nzShowWeekNumber", "nzShowTime"], outputs: ["nzOnPanelChange", "nzOnCalendarChange", "nzOnOk", "nzOnOpenChange"], exportAs: ["nzDatePicker"] }, { kind: "directive", type: i4.NzRangePickerComponent, selector: "nz-range-picker", exportAs: ["nzRangePicker"] }, { kind: "ngmodule", type: DatePickerModule }, { kind: "component", type: i12.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: AutoCompleteModule }, { kind: "component", type: i18.AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "minQueryLength", "delay", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "addOnTab", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "addOnBlur", "separator", "appendTo", "motionOptions"], outputs: ["completeMethod", "onSelect", "onUnselect", "onAdd", "onFocus", "onBlur", "onDropdownClick", "onClear", "onInputKeydown", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: CustomKeyboardDirective, selector: "[vlCustomKeyboard]", inputs: ["vlCustomKeyboard"] }, { kind: "pipe", type: i5.CurrencyPipe, name: "currency" }] });
3237
3794
  }
3238
3795
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: FormComponent, decorators: [{
3239
3796
  type: Component,
@@ -3243,7 +3800,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
3243
3800
  AutoCompleteModule,
3244
3801
  InputTextModule,
3245
3802
  CustomKeyboardDirective,
3246
- ], selector: 'vl-form', template: "<!-- Shuaib 2026-09-24\r\n - change: the form field switch uses renderFieldType so legacy checkbox fields render as boolean. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - change: the credit card type is spelled creditCard. The rename of 2026-09-15 reached\r\n model.component.html and the whole Designer but not this file, so one data object rendered\r\n in a modal and stayed empty here\r\n - new: a @contract comment on the type switch and on the credit card case, so the rule sits\r\n next to the code that implements it -->\r\n<div class=\"form-renderer\"\r\n [ngClass]=\"getStyleWithoutClasses(form?.layoutSchema?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(form?.layoutSchema?.style)?.style\">\r\n <form *ngIf=\"form?.layoutSchema?.formSections?.length > 0\">\r\n <div\r\n *ngFor=\"let formSection of form?.layoutSchema?.formSections; let i = index\"\r\n class=\"section-card\">\r\n <div *ngIf=\"formSection?.label?.caption\" class=\"section-header\">\r\n <div class=\"section-title\"\r\n [ngClass]=\"getStyleWithoutClasses(formSection?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formSection?.label?.style)?.style\">\r\n {{ formSection.label.caption }}\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"formSection?.formFields?.length > 0\" class=\"section-body\"\r\n [ngClass]=\"getStyleWithoutClasses(formSection?.style)?.classes\"\r\n [ngStyle]=\"getSectionBodyStyle(formSection)\">\r\n <div *ngFor=\"let formField of formSection.formFields\" class=\"field-row\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.style)?.classes\"\r\n [ngStyle]=\"getFieldWrapperStyle(formField)\">\r\n <!-- @contract decision: this switch decides whether a field appears at all\r\n reality: renders string, number, image, date, time, dropdown, boolean,\r\n autocomplete, dateRange and the credit card type. Everything else\r\n falls into the default branch and the field renders empty - no error,\r\n nothing in the console either. Existing checkbox fields are normalized\r\n to boolean before this switch; new fields should use boolean.\r\n seen: measured 2026-09-19 with viewdefinition-contract/derive-capabilities.sh\r\n against this file; date, time, dropdown, boolean, autocomplete and\r\n dateRange arrived on 2026-09-18 -->\r\n <div [ngSwitch]=\"renderFieldType(\r\n formField?.scope?.reference &&\r\n form?.propertySchema?.[getScope(formField.scope?.reference)]\r\n ? form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.type\r\n : 'default'\r\n )\">\r\n <!-- image control -->\r\n <div *ngSwitchCase=\"'image'\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <img [src]=\"formField?.option?.src\" [alt]=\"'img'\" />\r\n </div>\r\n\r\n <!-- credit card control -->\r\n <!-- @contract decision: the credit card type is spelled creditCard\r\n reality: it was carditCard here until 2026-09-19. The rename of 2026-09-15\r\n reached model.component.html and the whole Designer, but not this\r\n file and not utility.service.ts, so the same data object rendered in\r\n one container and stayed empty in the other. All four places agree now.\r\n failure: a data object still declaring carditCard renders empty everywhere -\r\n that spelling is read nowhere any more.\r\n instead: declare the type as creditCard. -->\r\n <div *ngSwitchCase=\"'creditCard'\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <div id=\"card-element\"></div>\r\n <div id=\"card-errors\"\r\n [class]=\"creditCardError?.textContent !== '' ? 'text-red-400 m-0' : ''\"></div>\r\n </div>\r\n\r\n <!-- string control -->\r\n <span *ngSwitchCase=\"'string'\" class=\"form-group\">\r\n <span *ngIf=\"formField?.option?.protected\">\r\n <h5 style=\"display: flex; justify-content: space-between\">\r\n <span\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </span>\r\n\r\n <span>\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.value\r\n }}\r\n </span>\r\n </h5>\r\n </span>\r\n\r\n <span *ngIf=\"!formField?.option?.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n formField?.label?.value\r\n ? formField.label.value\r\n : formField?.label?.caption\r\n ? formField.label.caption\r\n :\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n\r\n <div [ngStyle]=\"\r\n formField?.option?.password\r\n ? {\r\n position: 'relative',\r\n display: 'flex',\r\n alignItems: 'center'\r\n }\r\n : {}\r\n \">\r\n <input [type]=\"\r\n formField?.option?.password\r\n ? !showPassword\r\n ? 'password'\r\n : 'text'\r\n : 'text'\r\n \" #inputField=\"ngModel\" [required]=\"formField?.required\"\r\n [attr.inputmode]=\"\r\n formField?.option?.email\r\n ? 'email'\r\n : formField?.option?.password\r\n ? 'numeric'\r\n : 'text'\r\n \" [pattern]=\"formField?.email ? customEmailPattern : ''\"\r\n [email]=\"formField?.email\"\r\n [minlength]=\"formField?.minlength\"\r\n [maxlength]=\"formField?.maxlength\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\" [placeholder]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.placeholder\r\n \" />\r\n\r\n <i *ngIf=\"formField?.option?.password\" class=\"fa\"\r\n [ngClass]=\"showPassword ? 'fa-eye' : 'fa-eye-slash'\"\r\n (click)=\"togglePasswordVisibility()\"\r\n style=\"position: absolute; right: 10px; cursor: pointer\"></i>\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"inputField?.touched && inputField?.invalid\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.validation?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.validation?.style)?.style\">\r\n <p [class]=\"\r\n formField?.layout === 'horizontal'\r\n ? 'text-red-400 m-0 horizontal-error'\r\n : 'text-red-400 m-0'\r\n \" *ngIf=\"inputField?.errors\">\r\n {{ setErroMessage(formField, errorMessages, inputField) }}\r\n </p>\r\n </div>\r\n </span>\r\n </span>\r\n\r\n <!-- number control -->\r\n <div *ngSwitchCase=\"'number'\" class=\"form-group\">\r\n <span *ngIf=\"formField?.option?.protected\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n <h5>\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.value\r\n | currency\r\n : formField?.option?.currency\r\n : true\r\n : formField?.option?.decimalPoint\r\n }}\r\n </h5>\r\n </span>\r\n\r\n <span *ngIf=\"!formField?.option?.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n formField?.label?.value\r\n ? formField.label.value\r\n :\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n\r\n <input type=\"number\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\" [placeholder]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.placeholder\r\n \" />\r\n </div>\r\n </span>\r\n </div>\r\n\r\n <!-- checkbox control -->\r\n <div *ngSwitchCase=\"'boolean'\" class=\"form-group\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <span *ngIf=\"formField?.option?.postfixCaption\">\r\n <label class=\"form-check-label\">\r\n <input type=\"checkbox\" class=\"form-check-input mr-2\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\"\r\n (ngModelChange)=\"setformData()\" />\r\n\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n </span>\r\n\r\n <div *ngIf=\"!formField?.option?.postfixCaption\"\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label class=\"form-check-label\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n\r\n <input type=\"checkbox\" class=\"form-check-input\" [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\"\r\n (ngModelChange)=\"setformData()\" />\r\n </label>\r\n </div>\r\n </div>\r\n\r\n <!-- DateRange control -->\r\n <div *ngSwitchCase=\"'dateRange'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{\r\n formField.label?.value\r\n ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <nz-range-picker #dateRangeField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [(ngModel)]=\"form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"></nz-range-picker>\r\n </div>\r\n <!-- Error Message -->\r\n <div *ngIf=\"dateRangeField?.touched && dateRangeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['required']\">\r\n {{formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{getLabel(formField)}}</label>\r\n\r\n <span>\r\n <p-datepicker #datePicker #dateField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [disabled]=\"formField?.option?.disabled\" [showIcon]=\"true\"\r\n [showOnFocus]=\"false\" appendTo=\"body\"\r\n [autoZIndex]=\"true\" [baseZIndex]=\"10000\"\r\n panelStyleClass=\"model-datepicker-panel\"\r\n (pointerdown)=\"onPickerContainerPointerDown($event, datePicker)\"\r\n (onFocus)=\"onPickerInputFocus($event, datePicker)\"\r\n (onShow)=\"onPickerShown()\"\r\n (onClose)=\"onPickerClose()\"\r\n (ngModelChange)=\"dateChangeEvent(formField, $event)\"\r\n (onInput)=\"onDateTyping($event)\" [dateFormat]=\"\r\n form.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ]?.format || 'yy-mm-dd'\r\n \" [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [class]=\"formField?.scope?.style?.classes?.[0] || ''\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\">\r\n <ng-template #inputicon>\r\n <i class=\"pi pi-calendar\" aria-label=\"Open calendar\"\r\n (pointerdown)=\"openPickerFromIcon($event, datePicker)\"\r\n (click)=\"blockPickerIconClick($event)\">\r\n </i>\r\n </ng-template>\r\n </p-datepicker>\r\n <!-- Error Message -->\r\n <div *ngIf=\"dateField?.touched && dateField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages,\r\n \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Time control -->\r\n <div *ngSwitchCase=\"'time'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{getLabel(formField)}}</label>\r\n <span>\r\n <p-datepicker #timePicker #timeField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\" [timeOnly]=\"true\"\r\n hourFormat=\"24\" [disabled]=\"formField?.option?.disabled\"\r\n [showIcon]=\"true\" [showOnFocus]=\"false\"\r\n appendTo=\"body\" [autoZIndex]=\"true\" [baseZIndex]=\"10000\"\r\n panelStyleClass=\"model-datepicker-panel\"\r\n (pointerdown)=\"onPickerContainerPointerDown($event, timePicker)\"\r\n (onFocus)=\"onPickerInputFocus($event, timePicker)\"\r\n (onShow)=\"onTimePanelShow(formField)\"\r\n (onClose)=\"onPickerClose()\"\r\n (onInput)=\"onTimeTyping($event)\"\r\n (ngModelChange)=\"timeChangeEvent(formField, $event)\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"formField?.scope?.style?.classes?.[0] || ''\">\r\n\r\n <ng-template #inputicon>\r\n <i class=\"pi pi-clock\" aria-label=\"Open time picker\"\r\n (pointerdown)=\"openPickerFromIcon($event, timePicker)\"\r\n (click)=\"blockPickerIconClick($event)\">\r\n </i>\r\n </ng-template>\r\n </p-datepicker>\r\n\r\n <!-- Error Message -->\r\n <div *ngIf=\"timeField?.touched && timeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages,\r\n \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Autocomplete control -->\r\n <div *ngSwitchCase=\"'autocomplete'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-label>\r\n\r\n <span>\r\n <p-autocomplete #autocompleteField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [class]=\"formField?.scope?.style?.classes ? formField?.scope?.style?.classes[0] : ''\"\r\n [suggestions]=\"matchData\" [dropdown]=\"true\"\r\n [optionLabel]=\"formField.option.autocomplete.response.label\"\r\n [placeholder]=\"form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.placeholder\"\r\n (completeMethod)=\"autocompChangeEvent($event, formField)\"\r\n (ngModelChange)=\"autocompFocusOutEvent($event, formField)\"\r\n [vlCustomKeyboard]=\"formField?.option?.keyboardType\">\r\n </p-autocomplete>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"autocompleteField?.touched && autocompleteField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages,\r\n \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- dropdown control -->\r\n <div *ngSwitchCase=\"'dropdown'\" class=\"form-group\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <span *ngIf=\"formField?.option?.postfixCaption\">\r\n <select id=\"dynamic-dropdown\" #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.data\r\n \" [value]=\"item[formField?.option?.optionValue]\">\r\n {{ item[formField?.option?.optionLabel] }}\r\n </option>\r\n </select>\r\n\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n </span>\r\n\r\n <div *ngIf=\"!formField?.option?.postfixCaption\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n\r\n <nz-select nzShowSearch nzAllowClear nzPlaceHolder=\"Select\"\r\n #dropdownField=\"ngModel\"\r\n [required]=\"formField?.required\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [name]=\"formField.scope?.reference\" [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <nz-option *ngFor=\"\r\n let item of form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.data\r\n \" [nzLabel]=\"item[formField?.option?.optionLabel]\"\r\n [nzValue]=\"item[formField?.option?.optionValue]\"></nz-option>\r\n </nz-select>\r\n </div>\r\n\r\n <div *ngIf=\"dropdownField?.touched && dropdownField?.invalid\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.validation?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.validation?.style)?.style\">\r\n <p [class]=\"\r\n formField?.layout === 'horizontal'\r\n ? 'text-red-400 m-0 horizontal-error'\r\n : 'text-red-400 m-0'\r\n \" *ngIf=\"dropdownField?.errors\">\r\n {{ setErroMessage(formField, errorMessages, dropdownField)\r\n }}\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- button/default control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button type=\"submit\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.style)?.style\"\r\n (click)=\"invokeFormBtnClick(formField?.option, form.propertySchema)\">\r\n {{ formField?.label?.caption }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </form>\r\n</div>\r\n", styles: [":host{display:block;width:100%;height:100%;min-height:0}.form-renderer{width:100%;height:100%;min-height:0;overflow-y:auto;overflow-x:hidden;box-sizing:border-box;padding:12px}.section-card{border:1px solid #e5e7eb;border-radius:var(--radius-sm, 8px);padding:12px;margin-bottom:12px;background:#fff;box-sizing:border-box}.section-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;font-weight:600}.section-title{font-size:14px;color:#0f172a}.section-body{position:relative;width:100%;min-height:78px;box-sizing:border-box}.field-row{position:relative;min-width:0;width:100%;box-sizing:border-box}.field-row input,.field-row select,.field-row nz-select,.field-row textarea,.field-row button{max-width:100%;box-sizing:border-box}@media(max-width:768px){.section-body{grid-template-columns:repeat(1,minmax(0,1fr))!important}.field-row{grid-column:span 1!important}}@media(min-width:769px)and (max-width:1024px){.section-body{grid-template-columns:repeat(6,minmax(0,1fr))!important}.field-row{grid-column:span 6!important}}:host-context(.mobile-frame) .section-body,:host-context(.device-mobile) .section-body{grid-template-columns:repeat(1,minmax(0,1fr))!important}:host-context(.mobile-frame) .field-row,:host-context(.device-mobile) .field-row{grid-column:span 1!important}:host-context(.tablet-frame) .section-body,:host-context(.device-tablet) .section-body{grid-template-columns:repeat(6,minmax(0,1fr))!important}:host-context(.tablet-frame) .field-row,:host-context(.device-tablet) .field-row{grid-column:span 6!important}\n"] }]
3803
+ ], selector: 'vl-form', template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\r\n - change: autocomplete fields take the toolkit string keyboard unless option.keyboardType\r\n names another one ('native' keeps the system keyboard)\r\n - change: date and time fields use the shared picker markup (picker-field, DateTimePickerService)\r\n instead of PrimeNG's own icon with empty handlers -->\r\n\r\n<!-- Shuaib 2026-09-24\r\n - change: the form field switch uses renderFieldType so legacy checkbox fields render as boolean. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - change: the credit card type is spelled creditCard. The rename of 2026-09-15 reached\r\n model.component.html and the whole Designer but not this file, so one data object rendered\r\n in a modal and stayed empty here\r\n - new: a @contract comment on the type switch and on the credit card case, so the rule sits\r\n next to the code that implements it -->\r\n<div class=\"form-renderer\"\r\n [ngClass]=\"getStyleWithoutClasses(form?.layoutSchema?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(form?.layoutSchema?.style)?.style\">\r\n <form *ngIf=\"form?.layoutSchema?.formSections?.length > 0\">\r\n <div\r\n *ngFor=\"let formSection of form?.layoutSchema?.formSections; let i = index\"\r\n class=\"section-card\">\r\n <div *ngIf=\"formSection?.label?.caption\" class=\"section-header\">\r\n <div class=\"section-title\"\r\n [ngClass]=\"getStyleWithoutClasses(formSection?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formSection?.label?.style)?.style\">\r\n {{ formSection.label.caption }}\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"formSection?.formFields?.length > 0\" class=\"section-body\"\r\n [ngClass]=\"getStyleWithoutClasses(formSection?.style)?.classes\"\r\n [ngStyle]=\"getSectionBodyStyle(formSection)\">\r\n <div *ngFor=\"let formField of formSection.formFields\" class=\"field-row\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.style)?.classes\"\r\n [ngStyle]=\"getFieldWrapperStyle(formField)\">\r\n <!-- @contract decision: this switch decides whether a field appears at all\r\n reality: renders string, number, image, date, time, dropdown, boolean,\r\n autocomplete, dateRange and the credit card type. Everything else\r\n falls into the default branch and the field renders empty - no error,\r\n nothing in the console either. Existing checkbox fields are normalized\r\n to boolean before this switch; new fields should use boolean.\r\n seen: measured 2026-09-19 with viewdefinition-contract/derive-capabilities.sh\r\n against this file; date, time, dropdown, boolean, autocomplete and\r\n dateRange arrived on 2026-09-18 -->\r\n <div [ngSwitch]=\"renderFieldType(\r\n formField?.scope?.reference &&\r\n form?.propertySchema?.[getScope(formField.scope?.reference)]\r\n ? form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.type\r\n : 'default'\r\n )\">\r\n <!-- image control -->\r\n <div *ngSwitchCase=\"'image'\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <img [src]=\"formField?.option?.src\" [alt]=\"'img'\" />\r\n </div>\r\n\r\n <!-- credit card control -->\r\n <!-- @contract decision: the credit card type is spelled creditCard\r\n reality: it was carditCard here until 2026-09-19. The rename of 2026-09-15\r\n reached model.component.html and the whole Designer, but not this\r\n file and not utility.service.ts, so the same data object rendered in\r\n one container and stayed empty in the other. All four places agree now.\r\n failure: a data object still declaring carditCard renders empty everywhere -\r\n that spelling is read nowhere any more.\r\n instead: declare the type as creditCard. -->\r\n <div *ngSwitchCase=\"'creditCard'\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <div id=\"card-element\"></div>\r\n <div id=\"card-errors\"\r\n [class]=\"creditCardError?.textContent !== '' ? 'text-red-400 m-0' : ''\"></div>\r\n </div>\r\n\r\n <!-- string control -->\r\n <span *ngSwitchCase=\"'string'\" class=\"form-group\">\r\n <span *ngIf=\"formField?.option?.protected\">\r\n <h5 style=\"display: flex; justify-content: space-between\">\r\n <span\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </span>\r\n\r\n <span>\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.value\r\n }}\r\n </span>\r\n </h5>\r\n </span>\r\n\r\n <span *ngIf=\"!formField?.option?.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n formField?.label?.value\r\n ? formField.label.value\r\n : formField?.label?.caption\r\n ? formField.label.caption\r\n :\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n\r\n <div [ngStyle]=\"\r\n formField?.option?.password\r\n ? {\r\n position: 'relative',\r\n display: 'flex',\r\n alignItems: 'center'\r\n }\r\n : {}\r\n \">\r\n <input [type]=\"\r\n formField?.option?.password\r\n ? !showPassword\r\n ? 'password'\r\n : 'text'\r\n : 'text'\r\n \" #inputField=\"ngModel\" [required]=\"formField?.required\"\r\n [attr.inputmode]=\"\r\n formField?.option?.email\r\n ? 'email'\r\n : formField?.option?.password\r\n ? 'numeric'\r\n : 'text'\r\n \" [pattern]=\"formField?.email ? customEmailPattern : ''\"\r\n [email]=\"formField?.email\"\r\n [minlength]=\"formField?.minlength\"\r\n [maxlength]=\"formField?.maxlength\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\" [placeholder]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.placeholder\r\n \" />\r\n\r\n <i *ngIf=\"formField?.option?.password\" class=\"fa\"\r\n [ngClass]=\"showPassword ? 'fa-eye' : 'fa-eye-slash'\"\r\n (click)=\"togglePasswordVisibility()\"\r\n style=\"position: absolute; right: 10px; cursor: pointer\"></i>\r\n </div>\r\n </div>\r\n\r\n <div *ngIf=\"inputField?.touched && inputField?.invalid\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.validation?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.validation?.style)?.style\">\r\n <p [class]=\"\r\n formField?.layout === 'horizontal'\r\n ? 'text-red-400 m-0 horizontal-error'\r\n : 'text-red-400 m-0'\r\n \" *ngIf=\"inputField?.errors\">\r\n {{ setErroMessage(formField, errorMessages, inputField) }}\r\n </p>\r\n </div>\r\n </span>\r\n </span>\r\n\r\n <!-- number control -->\r\n <div *ngSwitchCase=\"'number'\" class=\"form-group\">\r\n <span *ngIf=\"formField?.option?.protected\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n <h5>\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.value\r\n | currency\r\n : formField?.option?.currency\r\n : true\r\n : formField?.option?.decimalPoint\r\n }}\r\n </h5>\r\n </span>\r\n\r\n <span *ngIf=\"!formField?.option?.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n formField?.label?.value\r\n ? formField.label.value\r\n :\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n\r\n <input type=\"number\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\" [placeholder]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.placeholder\r\n \" />\r\n </div>\r\n </span>\r\n </div>\r\n\r\n <!-- checkbox control -->\r\n <div *ngSwitchCase=\"'boolean'\" class=\"form-group\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <span *ngIf=\"formField?.option?.postfixCaption\">\r\n <label class=\"form-check-label\">\r\n <input type=\"checkbox\" class=\"form-check-input mr-2\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\"\r\n (ngModelChange)=\"setformData()\" />\r\n\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n </span>\r\n\r\n <div *ngIf=\"!formField?.option?.postfixCaption\"\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label class=\"form-check-label\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n\r\n <input type=\"checkbox\" class=\"form-check-input\" [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\"\r\n (ngModelChange)=\"setformData()\" />\r\n </label>\r\n </div>\r\n </div>\r\n\r\n <!-- DateRange control -->\r\n <div *ngSwitchCase=\"'dateRange'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{\r\n formField.label?.value\r\n ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <nz-range-picker #dateRangeField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [(ngModel)]=\"form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"></nz-range-picker>\r\n </div>\r\n <!-- Error Message -->\r\n <div *ngIf=\"dateRangeField?.touched && dateRangeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['required']\">\r\n {{formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{getLabel(formField)}}</label>\r\n\r\n <span>\r\n <div class=\"picker-field\">\r\n <p-datepicker #datePicker #dateField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n [autoZIndex]=\"true\" [baseZIndex]=\"10000\"\r\n (ngModelChange)=\"dateChangeEvent(formField, $event)\"\r\n (onInput)=\"onDateTyping($event)\" [dateFormat]=\"\r\n form.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ]?.format || 'yy-mm-dd'\r\n \" [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \" [class]=\"formField?.scope?.style?.classes?.[0] || ''\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\" appendTo=\"body\"\r\n [panelStyleClass]=\"picker.datePanelClass\" [motionOptions]=\"picker.dockedMotion\"\r\n [vlCustomKeyboard]=\"formField?.option?.keyboardType ?? 'date'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, datePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(datePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\" aria-label=\"Open calendar\"\r\n (pointerdown)=\"picker.openFromIcon($event, datePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-calendar\"></i>\r\n </button>\r\n </div>\r\n <!-- Error Message -->\r\n <div *ngIf=\"dateField?.touched && dateField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages,\r\n \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Time control -->\r\n <div *ngSwitchCase=\"'time'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{getLabel(formField)}}</label>\r\n <span>\r\n <div class=\"picker-field\">\r\n <p-datepicker #timePicker #timeField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\" [timeOnly]=\"true\"\r\n hourFormat=\"24\" [disabled]=\"formField?.option?.disabled\" [autoZIndex]=\"true\" [baseZIndex]=\"10000\"\r\n (onInput)=\"onTimeTyping($event)\"\r\n (ngModelChange)=\"timeChangeEvent(formField, $event)\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"formField?.scope?.style?.classes?.[0] || ''\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\" appendTo=\"body\"\r\n [panelStyleClass]=\"picker.timePanelClass\"\r\n [vlCustomKeyboard]=\"formField?.option?.keyboardType ?? 'time'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, timePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(timePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\" aria-label=\"Open time picker\"\r\n (pointerdown)=\"picker.openFromIcon($event, timePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-clock\"></i>\r\n </button>\r\n </div>\r\n\r\n <!-- Error Message -->\r\n <div *ngIf=\"timeField?.touched && timeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages,\r\n \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Autocomplete control -->\r\n <div *ngSwitchCase=\"'autocomplete'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\" [modalItemInfo]=\"form\">\r\n </vl-label>\r\n\r\n <span>\r\n <p-autocomplete #autocompleteField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [class]=\"formField?.scope?.style?.classes ? formField?.scope?.style?.classes[0] : ''\"\r\n [suggestions]=\"matchData\" [dropdown]=\"true\"\r\n [optionLabel]=\"formField.option.autocomplete.response.label\"\r\n [placeholder]=\"form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.placeholder\"\r\n (completeMethod)=\"autocompChangeEvent($event, formField)\"\r\n (ngModelChange)=\"autocompFocusOutEvent($event, formField)\"\r\n [vlCustomKeyboard]=\"formField?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"autocompleteField?.touched && autocompleteField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages,\r\n \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n form.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- dropdown control -->\r\n <div *ngSwitchCase=\"'dropdown'\" class=\"form-group\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\">\r\n <span *ngIf=\"formField?.option?.postfixCaption\">\r\n <select id=\"dynamic-dropdown\" #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.data\r\n \" [value]=\"item[formField?.option?.optionValue]\">\r\n {{ item[formField?.option?.optionLabel] }}\r\n </option>\r\n </select>\r\n\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n </span>\r\n\r\n <div *ngIf=\"!formField?.option?.postfixCaption\">\r\n <div [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [ngClass]=\"getStyleWithoutClasses(formField?.label?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.label?.style)?.style\">\r\n {{\r\n form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption\r\n }}\r\n </label>\r\n\r\n <nz-select nzShowSearch nzAllowClear nzPlaceHolder=\"Select\"\r\n #dropdownField=\"ngModel\"\r\n [required]=\"formField?.required\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.scope?.style)?.style\"\r\n [name]=\"formField.scope?.reference\" [(ngModel)]=\"\r\n form.propertySchema[getScope(formField.scope?.reference)][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <nz-option *ngFor=\"\r\n let item of form.propertySchema[getScope(formField.scope?.reference)]?.[\r\n formField.scope?.reference.split('/').pop()\r\n ]?.data\r\n \" [nzLabel]=\"item[formField?.option?.optionLabel]\"\r\n [nzValue]=\"item[formField?.option?.optionValue]\"></nz-option>\r\n </nz-select>\r\n </div>\r\n\r\n <div *ngIf=\"dropdownField?.touched && dropdownField?.invalid\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.validation?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.validation?.style)?.style\">\r\n <p [class]=\"\r\n formField?.layout === 'horizontal'\r\n ? 'text-red-400 m-0 horizontal-error'\r\n : 'text-red-400 m-0'\r\n \" *ngIf=\"dropdownField?.errors\">\r\n {{ setErroMessage(formField, errorMessages, dropdownField)\r\n }}\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- button/default control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button type=\"submit\"\r\n [ngClass]=\"getStyleWithoutClasses(formField?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField?.style)?.style\"\r\n (click)=\"invokeFormBtnClick(formField?.option, form.propertySchema)\">\r\n {{ formField?.label?.caption }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </form>\r\n</div>\r\n", styles: [":host{display:block;width:100%;height:100%;min-height:0}.form-renderer{width:100%;height:100%;min-height:0;overflow-y:auto;overflow-x:hidden;box-sizing:border-box;padding:12px}.section-card{border:1px solid #e5e7eb;border-radius:var(--radius-sm, 8px);padding:12px;margin-bottom:12px;background:#fff;box-sizing:border-box}.section-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;font-weight:600}.section-title{font-size:14px;color:#0f172a}.section-body{position:relative;width:100%;min-height:78px;box-sizing:border-box}.field-row{position:relative;min-width:0;width:100%;box-sizing:border-box}.field-row input,.field-row select,.field-row nz-select,.field-row textarea,.field-row button{max-width:100%;box-sizing:border-box}@media(max-width:768px){.section-body{grid-template-columns:repeat(1,minmax(0,1fr))!important}.field-row{grid-column:span 1!important}}@media(min-width:769px)and (max-width:1024px){.section-body{grid-template-columns:repeat(6,minmax(0,1fr))!important}.field-row{grid-column:span 6!important}}:host-context(.mobile-frame) .section-body,:host-context(.device-mobile) .section-body{grid-template-columns:repeat(1,minmax(0,1fr))!important}:host-context(.mobile-frame) .field-row,:host-context(.device-mobile) .field-row{grid-column:span 1!important}:host-context(.tablet-frame) .section-body,:host-context(.device-tablet) .section-body{grid-template-columns:repeat(6,minmax(0,1fr))!important}:host-context(.tablet-frame) .field-row,:host-context(.device-tablet) .field-row{grid-column:span 6!important}\n"] }]
3247
3804
  }], propDecorators: { errorMessages: [{
3248
3805
  type: Input
3249
3806
  }], creditCardError: [{
@@ -3258,6 +3815,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
3258
3815
  type: Output
3259
3816
  }] } });
3260
3817
 
3818
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07
3819
+ // - change: date and time cells use DateTimePickerService like the dialog
3261
3820
  // Shuaib 2026-09-24
3262
3821
  // - new: isLegacyCheckboxFieldType recognises existing checkbox fields so the grid can render them as boolean.
3263
3822
  class GridComponent {
@@ -3265,6 +3824,12 @@ class GridComponent {
3265
3824
  utilityService;
3266
3825
  notificationService;
3267
3826
  oktaAuthService;
3827
+ /** Date and time picker behaviour shared by all containers. */
3828
+ picker = inject(DateTimePickerService);
3829
+ host = inject((ElementRef));
3830
+ ngOnDestroy() {
3831
+ this.picker.closeIfWithin(this.host.nativeElement);
3832
+ }
3268
3833
  /** New data objects use boolean; older projects may still use checkbox. */
3269
3834
  isLegacyCheckboxFieldType(type) {
3270
3835
  return type === 'checkbox';
@@ -3678,7 +4243,7 @@ class GridComponent {
3678
4243
  }
3679
4244
  }
3680
4245
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: GridComponent, deps: [{ token: RequestService }, { token: UtilityService }, { token: NotificationService }, { token: i4$1.OktaAuthStateService }], target: i0.ɵɵFactoryTarget.Component });
3681
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.2", type: GridComponent, isStandalone: true, selector: "vl-grid", inputs: { errorMessages: "errorMessages", environment: "environment", selectedLanguageInfo: "selectedLanguageInfo", tableitemInfo: "tableitemInfo", form: "form", isTableDataLoad: "isTableDataLoad", currenTableOptions: "currenTableOptions", projectSeleted: "projectSeleted", pageSize: "pageSize", currentPage: "currentPage", pages: "pages", userInfo: "userInfo" }, outputs: { onPageChange: "onPageChange", onPageSizeChange: "onPageSizeChange", onGridScroll: "onGridScroll", showGridLink: "showGridLink", gridButtonClick: "gridButtonClick", gridIncreamentClick: "gridIncreamentClick", gridDecreamentClick: "gridDecreamentClick", gridImgClick: "gridImgClick" }, usesOnChanges: true, ngImport: i0, template: "<!-- Shuaib 2026-09-24\r\n - change: grid cells render legacy checkbox fields through the boolean control. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - new: @contract comments on the sorting, on the column filter and on the footer. The filter\r\n reads option.filter and never option.columnFilter, which is the mistake a layout author\r\n makes without being told -->\r\n<div *ngIf=\"!tableitemInfo.data?.length\" class=\"alert alert-warning col-md-12\"\r\n role=\"alert\">\r\n {{ tableitemInfo.layoutSchema?.noRecordMessage}}\r\n</div>\r\n<div *ngIf=\"tableitemInfo.data?.length\" class=\"reservation-container pt-5 px-3\">\r\n <nz-table #basicTable [nzData]=\"gridDisplayData\"\r\n [nzNoResult]=\"'No matching records found'\" [nzBordered]=\"true\"\r\n [nzFrontPagination]=\"currenTableOptions?.pagination?.tabs\"\r\n [style]=\"getStyleWithoutClasses(currenTableOptions?.style).style\"\r\n (scroll)=\"onScrollEvent($event)\"\r\n [nzLoading]=\"isTableDataLoad\">\r\n <thead>\r\n <!-- @contract decision: option.sortable is read here\r\n reality: nzSortFn is wired to getColumnSortFn since 2026-09-16; before that\r\n the key was decoration. The column also needs a scope.reference -\r\n without one getColumnSortFn has nothing to sort by.\r\n seen: grid.component.ts:338 -->\r\n <!-- Column headings and sorting -->\r\n <tr>\r\n <th *ngFor=\"let column of tableitemInfo.layoutSchema.elements\"\r\n [nzSortFn]=\"\r\n column?.option?.sortable\r\n ? getColumnSortFn(column)\r\n : false\r\n \">\r\n\r\n {{ column?.label?.caption }}\r\n </th>\r\n </tr>\r\n\r\n <!-- @contract decision: the column filter reads option.filter, NOT option.columnFilter\r\n reality: option.filter.filtering switches it on, option.filter.defaultOperator\r\n starts the comparison (the renderer falls back to contains), and\r\n option.filter.placeholder labels the input.\r\n failure: option.columnFilter is read nowhere, here or in the runtime app. A\r\n column carrying it looks filterable in the file and is not on screen.\r\n seen: grid.component.ts:450-475; 5 uses of columnFilter across the reference\r\n projects, all of them dead -->\r\n <!-- Column filters -->\r\n <tr *ngIf=\"hasAnyColumnFilter()\" class=\"grid-filter-row\">\r\n\r\n <th *ngFor=\"let column of tableitemInfo.layoutSchema.elements\"\r\n class=\"grid-filter-cell\">\r\n\r\n <ng-container *ngIf=\"\r\n column?.option?.filter?.filtering === true &&\r\n column?.scope?.reference\r\n \">\r\n\r\n <div class=\"grid-filter-input-wrapper\">\r\n <input type=\"text\" class=\"grid-filter-input\"\r\n [placeholder]=\"getColumnFilterPlaceholder(column)\"\r\n [ngModel]=\"getColumnFilterState(column).value\"\r\n (ngModelChange)=\"\r\n setColumnFilterValue(column, $event)\r\n \" />\r\n\r\n <!-- Operator selection button -->\r\n <button type=\"button\"\r\n class=\"grid-filter-operator-button\" nz-dropdown\r\n nzTrigger=\"click\"\r\n [nzDropdownMenu]=\"operatorMenu\"\r\n [class.grid-filter-operator-button--active]=\"\r\n hasActiveColumnFilter(column)\r\n \" [title]=\"\r\n 'Operator: ' +\r\n getFilterOperatorLabel(\r\n getColumnFilterState(column).operator\r\n )\r\n \">\r\n\r\n <span nz-icon nzType=\"filter\"></span>\r\n </button>\r\n\r\n <!-- Clear current column filter -->\r\n <button *ngIf=\"hasActiveColumnFilter(column)\"\r\n type=\"button\" class=\"grid-filter-clear-button\"\r\n aria-label=\"Clear column filter\"\r\n title=\"Clear filter\"\r\n (click)=\"clearColumnFilter(column)\">\r\n\r\n <span nz-icon nzType=\"close\"></span>\r\n </button>\r\n </div>\r\n\r\n <!-- Operator dropdown -->\r\n <nz-dropdown-menu #operatorMenu=\"nzDropdownMenu\">\r\n <ul nz-menu class=\"grid-filter-operator-menu\">\r\n <li *ngFor=\"let operator of filterOperators\"\r\n nz-menu-item [nzSelected]=\"\r\n getColumnFilterState(column).operator ===\r\n operator.value\r\n \" (click)=\"\r\n setColumnFilterOperator(\r\n column,\r\n operator.value\r\n )\r\n \">\r\n\r\n <span *ngIf=\"\r\n getColumnFilterState(column).operator ===\r\n operator.value\r\n \" nz-icon nzType=\"check\">\r\n </span>\r\n\r\n {{ operator.label }}\r\n </li>\r\n </ul>\r\n </nz-dropdown-menu>\r\n </ng-container>\r\n </th>\r\n </tr>\r\n </thead>\r\n <!-- @contract decision: the column chain below, and what it does not carry\r\n reality: string, number, image, quantity, date, time, dropdown, boolean,\r\n autocomplete and dateRange each have their own condition. There is no\r\n branch for the credit card type.\r\n failure: a column whose type has no branch stays empty, with no error.\r\n instead: format the value in the endpoint and send a string.\r\n\r\n @contract decision: columns without scope.reference are valid\r\n reality: with option.actionColumn they render save/delete icons, otherwise a\r\n button. That is why the conditions below all guard on the reference. -->\r\n <tbody>\r\n <tr *ngFor=\"let data of basicTable.data; let i = index\">\r\n <td *ngFor=\"let col of tableitemInfo.layoutSchema.elements\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\">\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'string'\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\">\r\n <span *ngIf=\"col.option.protected\">\r\n <span *ngIf=\"col.option?.link\"\r\n (click)=\"showLinkDetails(col.option, data)\">\r\n {{data[col.scope?.reference.split('/').pop()]}}\r\n </span>\r\n <span *ngIf=\"!col.option?.link\">\r\n <ng-container\r\n *ngIf=\"col?.option?.dateFormat; else string\">\r\n {{\r\n data[col.scope?.reference.split('/').pop()]\r\n | date:'yyyy-MM-dd' }}\r\n </ng-container>\r\n <ng-template #string>\r\n {{\r\n data[col.scope?.reference.split('/').pop()]\r\n }}\r\n </ng-template>\r\n </span>\r\n </span>\r\n <span *ngIf=\"!col.option.protected\">\r\n <input\r\n [(ngModel)]=\"data[col.scope?.reference.split('/').pop()]\"\r\n class=\"p-inputtext p-component\" />\r\n </span>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'number'\">\r\n <span *ngIf=\"col.option.protected\">\r\n <span\r\n *ngIf=\"col.option?.innerAction?.action == 'multiply'\">\r\n {{\r\n (data[col.option?.innerAction?.from] *\r\n data[col.option?.innerAction?.to])\r\n | number:'1.2-2'\r\n }}\r\n </span>\r\n <span *ngIf=\"!col.option?.innerAction\">\r\n {{data[col.scope?.reference.split('/').pop()] |\r\n currency:\r\n tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.currency\r\n :true }}\r\n </span>\r\n </span>\r\n <span *ngIf=\"!col.option.protected\">\r\n <input\r\n [(ngModel)]=\"data[col.scope?.reference.split('/').pop()]\"\r\n type=\"number\"\r\n class=\"p-inputtext p-component\" />\r\n </span>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'image'\">\r\n <img *ngIf=\"data[col.scope?.reference.split('/').pop()]\"\r\n [src]=\"data[col.scope?.reference.split('/').pop()]\"\r\n alt=\"Image\"\r\n (click)=\"invokeTableImageClick(col, data)\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\" />\r\n </ng-container>\r\n\r\n <!-- @contract compatibility: existing checkbox fields render as boolean. -->\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && (tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'boolean' || isLegacyCheckboxFieldType(tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type))\">\r\n <input type=\"checkbox\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n class=\"form-check-input mr-2\"\r\n [(ngModel)]=\"data[col.scope?.reference.split('/').pop()]\"\r\n [name]=\"col.scope?.reference\" />\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'dateRange'\">\r\n <nz-range-picker #dateRangeField=\"ngModel\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\"\r\n [style]=\"getStyleWithoutClasses(col?.style).style\"\r\n [(ngModel)]=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)][col.scope?.reference.split('/').pop()].value\"></nz-range-picker>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'date'\">\r\n <p-datepicker #datePicker #dateField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\"\r\n [disabled]=\"col?.option?.disabled\" [showIcon]=\"true\"\r\n [showOnFocus]=\"false\" appendTo=\"body\"\r\n [autoZIndex]=\"true\" [baseZIndex]=\"10000\"\r\n panelStyleClass=\"model-datepicker-panel\"\r\n [dateFormat]=\"\r\n tableitemInfo.propertySchema[\r\n getScope(col.scope?.reference)\r\n ][\r\n col.scope?.reference.split('/').pop()\r\n ]?.format || 'yy-mm-dd'\r\n \" [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(col.scope?.reference)\r\n ][\r\n col.scope?.reference.split('/').pop()\r\n ].value\r\n \" [class]=\"col?.scope?.style?.classes?.[0] || ''\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\">\r\n <ng-template #inputicon>\r\n <i class=\"pi pi-calendar\"\r\n aria-label=\"Open calendar\">\r\n </i>\r\n </ng-template>\r\n </p-datepicker>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'time'\">\r\n <p-datepicker #timePicker #timeField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\" [timeOnly]=\"true\"\r\n hourFormat=\"24\" [disabled]=\"col?.option?.disabled\"\r\n [showIcon]=\"true\" [showOnFocus]=\"false\"\r\n appendTo=\"body\" [autoZIndex]=\"true\"\r\n [baseZIndex]=\"10000\"\r\n panelStyleClass=\"model-datepicker-panel\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(col.scope?.reference)\r\n ][\r\n col.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"col?.scope?.style?.classes?.[0] || ''\">\r\n\r\n <ng-template #inputicon>\r\n <i class=\"pi pi-clock\"\r\n aria-label=\"Open time picker\">\r\n </i>\r\n </ng-template>\r\n </p-datepicker>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'autocomplete'\">\r\n <p-autocomplete #autocompleteField=\"ngModel\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"form.propertySchema[getScope(col.scope?.reference)][col.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n [class]=\"col?.scope?.style?.classes ? col?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"col.option.autocomplete.response.label\"\r\n [placeholder]=\"form.propertySchema[getScope(col.scope?.reference)][col.scope?.reference.split('/').pop()]?.placeholder\"\r\n [vlCustomKeyboard]=\"col?.option?.keyboardType\">\r\n </p-autocomplete>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'dropdown'\">\r\n <select id=\"dynamic-dropdown\" #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(col?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style)?.style\"\r\n [name]=\"col.scope?.reference\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(col.scope?.reference)][\r\n col.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of form.propertySchema[getScope(col.scope?.reference)]?.[\r\n col.scope?.reference.split('/').pop()\r\n ]?.data\r\n \" [value]=\"item[col?.option?.optionValue]\">\r\n {{ item[col?.option?.optionLabel] }}\r\n </option>\r\n </select>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"!col?.scope?.reference\">\r\n <span *ngIf=\"col.option?.actionColumn\">\r\n <span nz-icon\r\n *ngIf=\"setActionColumn(col.option?.save, data)\"\r\n [nzType]=\"col.option?.save?.icon\"\r\n [nzTheme]=\"'twotone'\"\r\n [style]=\"col.option?.save?.style\"\r\n [nzTwotoneColor]=\"'#52c41a'\"\r\n [class]=\"col.option?.save?.style?.classes[0]\"\r\n (click)=\"upsertAction(col.option?.save, data)\">\r\n </span>\r\n <span nz-icon\r\n *ngIf=\"setActionColumn(col.option?.delete, data)\"\r\n [nzType]=\"col.option?.delete?.icon\"\r\n [nzTheme]=\"'twotone'\"\r\n [style]=\"col.option?.delete?.style\"\r\n [nzTwotoneColor]=\"'#FF0000'\"\r\n [class]=\"col.option?.delete?.style?.classes[0]\"\r\n (click)=\"upsertAction(col.option?.delete, data)\"></span>\r\n\r\n </span>\r\n <span *ngIf=\"!col.option?.actionColumn\">\r\n <button type=\"submit\"\r\n [style]=\"getStyleWithoutClasses(col?.label?.style).style\"\r\n (click)=\"invokeGridBtnClick(col.option, data)\"\r\n [class]=\"col?.label?.style?.classes[0]\">\r\n {{ col.label?.caption }}\r\n </button>\r\n </span>\r\n </ng-container>\r\n\r\n <!-- For quantity column, display increment/decrement buttons -->\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'quantity'\">\r\n <div style=\"display: flex; align-items: center;\">\r\n <button pButton icon=\"pi pi-minus\"\r\n (click)=\"decrementQuantity(i,col.scope?.reference.split('/').pop(), col.option)\"\r\n [disabled]=\"data.quantity <= 0\"\r\n style=\"margin-right: 8px; height: 10px; width: 10px;\"></button>\r\n <span>{{ data[col.scope?.reference.split('/').pop()]\r\n }}</span>\r\n <button pButton icon=\"pi pi-plus\"\r\n (click)=\"incrementQuantity(i, col.scope?.reference.split('/').pop(), col.option)\"\r\n style=\"margin-left: 8px; height: 10px; width: 10px;\"></button>\r\n </div>\r\n </ng-container>\r\n </td>\r\n </tr>\r\n </tbody>\r\n </nz-table>\r\n</div>\r\n\r\n<!-- table footer -->\r\n<div *ngIf=\"tableitemInfo?.footer?.formFields && tableitemInfo.data?.length\"\r\n class=\"table-footer flex flex-column align-items-end gap-2 pt-2 mx-4 border-top-1 border-cyan-500\">\r\n <!-- @contract decision: the footer renders string and number only\r\n reality: the switch below carries those two cases and a default. It did NOT change\r\n when the columns gained their types on 2026-09-18.\r\n failure: any other type in a footer field renders empty.\r\n instead: keep aggregates numeric, or pre-format them as text in the endpoint. -->\r\n <div *ngFor=\"let tableField of tableitemInfo?.footer?.formFields\">\r\n <div\r\n [ngSwitch]=\"tableField?.scope?.reference && tableitemInfo.propertySchema[getScope(tableField.scope?.reference)] && tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()] ?\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.type : 'default'\">\r\n\r\n <!-- string control -->\r\n <div *ngSwitchCase=\"'string'\" class=\"form-group\">\r\n <span *ngIf=\"tableField.option.protected\">\r\n <h5 style=\"display: flex; justify-content: space-between;\">\r\n <span\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">\r\n {{\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }}\r\n </span>\r\n <span>\r\n {{\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.value\r\n }}\r\n </span>\r\n </h5>\r\n </span>\r\n <div *ngIf=\"!tableField.option.protected\">\r\n <div\r\n [class]=\"tableField?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">{{\r\n tableField.label?.value ?\r\n tableField.label?.value :\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <input type=\"text\" #inputField=\"ngModel\"\r\n [required]=\"tableField.option.required\"\r\n [email]=\"tableField.option.email\"\r\n [class]=\"tableField.scope?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(tableField?.scope?.style).style\"\r\n [(ngModel)]=\"tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"tableField.scope?.reference\"\r\n [placeholder]=\"tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.placeholder\" />\r\n </div>\r\n <!-- Error Message -->\r\n <div *ngIf=\"inputField?.touched && inputField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(tableField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"inputField?.errors?.['required']\">{{tableField.label?.value\r\n ?\r\n tableField.label?.value :\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"inputField?.errors?.['email']\">{{(tableField.label?.value\r\n ?\r\n tableField.label?.value :\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- number control -->\r\n <div *ngSwitchCase=\"'number'\" class=\"form-group\">\r\n <span *ngIf=\"tableField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">\r\n <h5>{{\r\n form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }} {{\r\n form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.value\r\n |\r\n currency:tableField.option.currency :true:\r\n tableField.option?.decimalPoint}}\r\n </h5>\r\n </span>\r\n <span *ngIf=\"!tableField.option.protected\">\r\n <div\r\n [class]=\"tableField?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">{{\r\n tableField.label?.value ?\r\n tableField.label?.value :\r\n form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <input type=\"number\"\r\n [class]=\"tableField.scope?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(tableField?.scope?.style).style\"\r\n [(ngModel)]=\"form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"tableField.scope?.reference\"\r\n [placeholder]=\"form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.placeholder\" />\r\n </div>\r\n </span>\r\n </div>\r\n\r\n <!-- button control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button type=\"submit\"\r\n [style]=\"getStyleWithoutClasses(tableField.label.style).style\"\r\n (click)=\"invokeGridBtnClick(tableField.option, tableitemInfo.propertySchema)\">\r\n {{ tableField.label?.caption }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<!-- bottom pagination on grid -->\r\n<div\r\n *ngIf=\"tableitemInfo.data?.length && !tableitemInfo?.layoutSchema?.pagination?.top && tableitemInfo?.layoutSchema?.pagination?.bottom\">\r\n <vl-pagination [itemInfo]=\"tableitemInfo\" [pageSize]=\"pageSize\"\r\n [currentPage]=\"currentPage\" [pages]=\"pages\"\r\n (pageSizeChange)=\"invokeOnPageSizeChange($event)\"\r\n (onGoToPage)=\"invokeGoToPage($event)\">\r\n </vl-pagination>\r\n</div>\r\n", styles: [".grid-filter-row th{padding:8px;vertical-align:middle;background:#fafafa}.grid-filter-cell{min-width:150px}.grid-filter-input-wrapper{display:flex;align-items:stretch;width:100%}.grid-filter-input{width:100%;min-width:0;min-height:34px;padding:5px 8px;border:1px solid #d9d9d9;border-right:0;border-radius:4px 0 0 4px;outline:none}.grid-filter-input:focus{border-color:#1677ff}.grid-filter-operator-button,.grid-filter-clear-button{display:inline-flex;align-items:center;justify-content:center;min-width:36px;min-height:34px;padding:0;cursor:pointer;background:#fff;border:1px solid #d9d9d9}.grid-filter-operator-button{color:#595959;border-radius:0 4px 4px 0}.grid-filter-operator-button:hover,.grid-filter-operator-button--active{color:#1677ff;border-color:#1677ff}.grid-filter-clear-button{margin-left:5px;color:#ff4d4f;border-radius:4px}.grid-filter-clear-button:hover{color:#fff;background:#ff4d4f;border-color:#ff4d4f}.grid-filter-operator-menu{min-width:190px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i5.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i5.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.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: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.EmailValidator, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: ["email"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: NzTableModule }, { kind: "component", type: i7$1.NzTableComponent, selector: "nz-table", inputs: ["nzTableLayout", "nzShowTotal", "nzItemRender", "nzTitle", "nzFooter", "nzNoResult", "nzPageSizeOptions", "nzVirtualItemSize", "nzVirtualMaxBufferPx", "nzVirtualMinBufferPx", "nzVirtualForTrackBy", "nzLoadingDelay", "nzPageIndex", "nzPageSize", "nzTotal", "nzWidthConfig", "nzData", "nzCustomColumn", "nzPaginationPosition", "nzScroll", "noDataVirtualHeight", "nzPaginationType", "nzFrontPagination", "nzTemplateMode", "nzShowPagination", "nzLoading", "nzOuterBordered", "nzLoadingIndicator", "nzBordered", "nzSize", "nzShowSizeChanger", "nzHideOnSinglePage", "nzShowQuickJumper", "nzSimple"], outputs: ["nzPageSizeChange", "nzPageIndexChange", "nzQueryParams", "nzCurrentPageDataChange", "nzCustomColumnChange"], exportAs: ["nzTable"] }, { kind: "component", type: i7$1.NzThAddOnComponent, selector: "th[nzColumnKey], th[nzSortFn], th[nzSortOrder], th[nzFilters], th[nzShowSort], th[nzShowFilter], th[nzCustomFilter]", inputs: ["nzColumnKey", "nzFilterMultiple", "nzSortOrder", "nzSortPriority", "nzSortDirections", "nzFilters", "nzSortFn", "nzFilterFn", "nzShowSort", "nzShowFilter", "nzCustomFilter"], outputs: ["nzCheckedChange", "nzSortOrderChange", "nzFilterChange"] }, { kind: "directive", type: i7$1.NzTableCellDirective, selector: "th:not(.nz-disable-th), td:not(.nz-disable-td)" }, { kind: "directive", type: i7$1.NzThMeasureDirective, selector: "th", inputs: ["nzWidth", "colspan", "colSpan", "rowspan", "rowSpan"] }, { kind: "component", type: i7$1.NzTheadComponent, selector: "thead:not(.ant-table-thead)", outputs: ["nzSortOrderChange"] }, { kind: "component", type: i7$1.NzTbodyComponent, selector: "tbody" }, { kind: "directive", type: i7$1.NzTrDirective, selector: "tr:not([nz-table-measure-row]):not([nzExpand]):not([nz-table-fixed-row])" }, { kind: "ngmodule", type: NzIconModule }, { kind: "directive", type: i11.NzIconDirective, selector: "nz-icon,[nz-icon]", inputs: ["nzSpin", "nzRotate", "nzType", "nzTheme", "nzTwotoneColor", "nzIconfont"], exportAs: ["nzIcon"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i9.ButtonDirective, selector: "[pButton]", inputs: ["ptButtonDirective", "pButtonPT", "pButtonUnstyled", "hostName", "text", "plain", "raised", "size", "outlined", "rounded", "iconPos", "loadingIcon", "fluid", "label", "icon", "loading", "buttonProps", "severity"] }, { kind: "ngmodule", type: NzDropDownModule }, { kind: "directive", type: i11$1.NzMenuDirective, selector: "[nz-menu]", inputs: ["nzInlineIndent", "nzTheme", "nzMode", "nzInlineCollapsed", "nzSelectable"], outputs: ["nzClick"], exportAs: ["nzMenu"] }, { kind: "component", type: i11$1.NzMenuItemComponent, selector: "[nz-menu-item]", inputs: ["nzPaddingLeft", "nzDisabled", "nzSelected", "nzDanger", "nzMatchRouterExact", "nzMatchRouter"], exportAs: ["nzMenuItem"] }, { kind: "directive", type: i11$2.NzDropdownDirective, selector: "[nz-dropdown]", inputs: ["nzDropdownMenu", "nzTrigger", "nzMatchWidthElement", "nzBackdrop", "nzClickHide", "nzDisabled", "nzVisible", "nzArrow", "nzOverlayClassName", "nzOverlayStyle", "nzPlacement"], outputs: ["nzVisibleChange"], exportAs: ["nzDropdown"] }, { kind: "component", type: i11$2.NzDropdownMenuComponent, selector: "nz-dropdown-menu", exportAs: ["nzDropdownMenu"] }, { kind: "ngmodule", type: NzMenuModule }, { kind: "component", type: PaginationComponent, selector: "vl-pagination", inputs: ["itemInfo", "pageSize", "currentPage", "selectedLanguageInfo", "pages"], outputs: ["pageSizeChange", "onGoToPage"] }, { kind: "ngmodule", type: NzDatePickerModule }, { kind: "component", type: i4.NzDatePickerComponent, selector: "nz-date-picker,nz-week-picker,nz-month-picker,nz-quarter-picker,nz-year-picker,nz-range-picker", inputs: ["nzInline", "nzAllowClear", "nzAutoFocus", "nzDisabled", "nzInputReadOnly", "nzOpen", "nzDisabledDate", "nzLocale", "nzPlaceHolder", "nzPopupStyle", "nzDropdownClassName", "nzSize", "nzStatus", "nzFormat", "nzVariant", "nzDateRender", "nzDisabledTime", "nzRenderExtraFooter", "nzShowToday", "nzMode", "nzShowNow", "nzRanges", "nzDefaultPickerValue", "nzSeparator", "nzSuffixIcon", "nzBackdrop", "nzId", "nzPlacement", "nzShowWeekNumber", "nzShowTime"], outputs: ["nzOnPanelChange", "nzOnCalendarChange", "nzOnOk", "nzOnOpenChange"], exportAs: ["nzDatePicker"] }, { kind: "directive", type: i4.NzRangePickerComponent, selector: "nz-range-picker", exportAs: ["nzRangePicker"] }, { kind: "ngmodule", type: DatePickerModule }, { kind: "component", type: i12.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: AutoCompleteModule }, { kind: "component", type: i18.AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "minQueryLength", "delay", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "addOnTab", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "addOnBlur", "separator", "appendTo", "motionOptions"], outputs: ["completeMethod", "onSelect", "onUnselect", "onAdd", "onFocus", "onBlur", "onDropdownClick", "onClear", "onInputKeydown", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: CustomKeyboardDirective, selector: "[vlCustomKeyboard]", inputs: ["vlCustomKeyboard"] }, { kind: "pipe", type: i5.DecimalPipe, name: "number" }, { kind: "pipe", type: i5.CurrencyPipe, name: "currency" }, { kind: "pipe", type: i5.DatePipe, name: "date" }] });
4246
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.2", type: GridComponent, isStandalone: true, selector: "vl-grid", inputs: { errorMessages: "errorMessages", environment: "environment", selectedLanguageInfo: "selectedLanguageInfo", tableitemInfo: "tableitemInfo", form: "form", isTableDataLoad: "isTableDataLoad", currenTableOptions: "currenTableOptions", projectSeleted: "projectSeleted", pageSize: "pageSize", currentPage: "currentPage", pages: "pages", userInfo: "userInfo" }, outputs: { onPageChange: "onPageChange", onPageSizeChange: "onPageSizeChange", onGridScroll: "onGridScroll", showGridLink: "showGridLink", gridButtonClick: "gridButtonClick", gridIncreamentClick: "gridIncreamentClick", gridDecreamentClick: "gridDecreamentClick", gridImgClick: "gridImgClick" }, usesOnChanges: true, ngImport: i0, template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\r\n - change: autocomplete fields take the toolkit string keyboard unless option.keyboardType\r\n names another one ('native' keeps the system keyboard)\r\n - change: date and time cells use the shared picker markup (picker-field, DateTimePickerService);\r\n their PrimeNG icon opened the calendar together with the system keyboard -->\r\n\r\n<!-- Shuaib 2026-09-24\r\n - change: grid cells render legacy checkbox fields through the boolean control. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - new: @contract comments on the sorting, on the column filter and on the footer. The filter\r\n reads option.filter and never option.columnFilter, which is the mistake a layout author\r\n makes without being told -->\r\n<div *ngIf=\"!tableitemInfo.data?.length\" class=\"alert alert-warning col-md-12\"\r\n role=\"alert\">\r\n {{ tableitemInfo.layoutSchema?.noRecordMessage}}\r\n</div>\r\n<div *ngIf=\"tableitemInfo.data?.length\" class=\"reservation-container pt-5 px-3\">\r\n <nz-table #basicTable [nzData]=\"gridDisplayData\"\r\n [nzNoResult]=\"'No matching records found'\" [nzBordered]=\"true\"\r\n [nzFrontPagination]=\"currenTableOptions?.pagination?.tabs\"\r\n [style]=\"getStyleWithoutClasses(currenTableOptions?.style).style\"\r\n (scroll)=\"onScrollEvent($event)\"\r\n [nzLoading]=\"isTableDataLoad\">\r\n <thead>\r\n <!-- @contract decision: option.sortable is read here\r\n reality: nzSortFn is wired to getColumnSortFn since 2026-09-16; before that\r\n the key was decoration. The column also needs a scope.reference -\r\n without one getColumnSortFn has nothing to sort by.\r\n seen: grid.component.ts:338 -->\r\n <!-- Column headings and sorting -->\r\n <tr>\r\n <th *ngFor=\"let column of tableitemInfo.layoutSchema.elements\"\r\n [nzSortFn]=\"\r\n column?.option?.sortable\r\n ? getColumnSortFn(column)\r\n : false\r\n \">\r\n\r\n {{ column?.label?.caption }}\r\n </th>\r\n </tr>\r\n\r\n <!-- @contract decision: the column filter reads option.filter, NOT option.columnFilter\r\n reality: option.filter.filtering switches it on, option.filter.defaultOperator\r\n starts the comparison (the renderer falls back to contains), and\r\n option.filter.placeholder labels the input.\r\n failure: option.columnFilter is read nowhere, here or in the runtime app. A\r\n column carrying it looks filterable in the file and is not on screen.\r\n seen: grid.component.ts:450-475; 5 uses of columnFilter across the reference\r\n projects, all of them dead -->\r\n <!-- Column filters -->\r\n <tr *ngIf=\"hasAnyColumnFilter()\" class=\"grid-filter-row\">\r\n\r\n <th *ngFor=\"let column of tableitemInfo.layoutSchema.elements\"\r\n class=\"grid-filter-cell\">\r\n\r\n <ng-container *ngIf=\"\r\n column?.option?.filter?.filtering === true &&\r\n column?.scope?.reference\r\n \">\r\n\r\n <div class=\"grid-filter-input-wrapper\">\r\n <input type=\"text\" class=\"grid-filter-input\"\r\n [placeholder]=\"getColumnFilterPlaceholder(column)\"\r\n [ngModel]=\"getColumnFilterState(column).value\"\r\n (ngModelChange)=\"\r\n setColumnFilterValue(column, $event)\r\n \" />\r\n\r\n <!-- Operator selection button -->\r\n <button type=\"button\"\r\n class=\"grid-filter-operator-button\" nz-dropdown\r\n nzTrigger=\"click\"\r\n [nzDropdownMenu]=\"operatorMenu\"\r\n [class.grid-filter-operator-button--active]=\"\r\n hasActiveColumnFilter(column)\r\n \" [title]=\"\r\n 'Operator: ' +\r\n getFilterOperatorLabel(\r\n getColumnFilterState(column).operator\r\n )\r\n \">\r\n\r\n <span nz-icon nzType=\"filter\"></span>\r\n </button>\r\n\r\n <!-- Clear current column filter -->\r\n <button *ngIf=\"hasActiveColumnFilter(column)\"\r\n type=\"button\" class=\"grid-filter-clear-button\"\r\n aria-label=\"Clear column filter\"\r\n title=\"Clear filter\"\r\n (click)=\"clearColumnFilter(column)\">\r\n\r\n <span nz-icon nzType=\"close\"></span>\r\n </button>\r\n </div>\r\n\r\n <!-- Operator dropdown -->\r\n <nz-dropdown-menu #operatorMenu=\"nzDropdownMenu\">\r\n <ul nz-menu class=\"grid-filter-operator-menu\">\r\n <li *ngFor=\"let operator of filterOperators\"\r\n nz-menu-item [nzSelected]=\"\r\n getColumnFilterState(column).operator ===\r\n operator.value\r\n \" (click)=\"\r\n setColumnFilterOperator(\r\n column,\r\n operator.value\r\n )\r\n \">\r\n\r\n <span *ngIf=\"\r\n getColumnFilterState(column).operator ===\r\n operator.value\r\n \" nz-icon nzType=\"check\">\r\n </span>\r\n\r\n {{ operator.label }}\r\n </li>\r\n </ul>\r\n </nz-dropdown-menu>\r\n </ng-container>\r\n </th>\r\n </tr>\r\n </thead>\r\n <!-- @contract decision: the column chain below, and what it does not carry\r\n reality: string, number, image, quantity, date, time, dropdown, boolean,\r\n autocomplete and dateRange each have their own condition. There is no\r\n branch for the credit card type.\r\n failure: a column whose type has no branch stays empty, with no error.\r\n instead: format the value in the endpoint and send a string.\r\n\r\n @contract decision: columns without scope.reference are valid\r\n reality: with option.actionColumn they render save/delete icons, otherwise a\r\n button. That is why the conditions below all guard on the reference. -->\r\n <tbody>\r\n <tr *ngFor=\"let data of basicTable.data; let i = index\">\r\n <td *ngFor=\"let col of tableitemInfo.layoutSchema.elements\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\">\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'string'\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\">\r\n <span *ngIf=\"col.option.protected\">\r\n <span *ngIf=\"col.option?.link\"\r\n (click)=\"showLinkDetails(col.option, data)\">\r\n {{data[col.scope?.reference.split('/').pop()]}}\r\n </span>\r\n <span *ngIf=\"!col.option?.link\">\r\n <ng-container\r\n *ngIf=\"col?.option?.dateFormat; else string\">\r\n {{\r\n data[col.scope?.reference.split('/').pop()]\r\n | date:'yyyy-MM-dd' }}\r\n </ng-container>\r\n <ng-template #string>\r\n {{\r\n data[col.scope?.reference.split('/').pop()]\r\n }}\r\n </ng-template>\r\n </span>\r\n </span>\r\n <span *ngIf=\"!col.option.protected\">\r\n <input\r\n [(ngModel)]=\"data[col.scope?.reference.split('/').pop()]\"\r\n class=\"p-inputtext p-component\" />\r\n </span>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'number'\">\r\n <span *ngIf=\"col.option.protected\">\r\n <span\r\n *ngIf=\"col.option?.innerAction?.action == 'multiply'\">\r\n {{\r\n (data[col.option?.innerAction?.from] *\r\n data[col.option?.innerAction?.to])\r\n | number:'1.2-2'\r\n }}\r\n </span>\r\n <span *ngIf=\"!col.option?.innerAction\">\r\n {{data[col.scope?.reference.split('/').pop()] |\r\n currency:\r\n tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.currency\r\n :true }}\r\n </span>\r\n </span>\r\n <span *ngIf=\"!col.option.protected\">\r\n <input\r\n [(ngModel)]=\"data[col.scope?.reference.split('/').pop()]\"\r\n type=\"number\"\r\n class=\"p-inputtext p-component\" />\r\n </span>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'image'\">\r\n <img *ngIf=\"data[col.scope?.reference.split('/').pop()]\"\r\n [src]=\"data[col.scope?.reference.split('/').pop()]\"\r\n alt=\"Image\"\r\n (click)=\"invokeTableImageClick(col, data)\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\" />\r\n </ng-container>\r\n\r\n <!-- @contract compatibility: existing checkbox fields render as boolean. -->\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && (tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'boolean' || isLegacyCheckboxFieldType(tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type))\">\r\n <input type=\"checkbox\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n class=\"form-check-input mr-2\"\r\n [(ngModel)]=\"data[col.scope?.reference.split('/').pop()]\"\r\n [name]=\"col.scope?.reference\" />\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'dateRange'\">\r\n <nz-range-picker #dateRangeField=\"ngModel\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\"\r\n [style]=\"getStyleWithoutClasses(col?.style).style\"\r\n [(ngModel)]=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)][col.scope?.reference.split('/').pop()].value\"></nz-range-picker>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'date'\">\r\n <div class=\"picker-field\">\r\n <p-datepicker #datePicker #dateField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\"\r\n [disabled]=\"col?.option?.disabled\"\r\n [autoZIndex]=\"true\" [baseZIndex]=\"10000\"\r\n [dateFormat]=\"\r\n tableitemInfo.propertySchema[\r\n getScope(col.scope?.reference)\r\n ][\r\n col.scope?.reference.split('/').pop()\r\n ]?.format || 'yy-mm-dd'\r\n \" [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(col.scope?.reference)\r\n ][\r\n col.scope?.reference.split('/').pop()\r\n ].value\r\n \" [class]=\"col?.scope?.style?.classes?.[0] || ''\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\" appendTo=\"body\"\r\n [panelStyleClass]=\"picker.datePanelClass\" [motionOptions]=\"picker.dockedMotion\"\r\n [vlCustomKeyboard]=\"col?.option?.keyboardType ?? 'date'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, datePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(datePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\" aria-label=\"Open calendar\"\r\n (pointerdown)=\"picker.openFromIcon($event, datePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-calendar\"></i>\r\n </button>\r\n </div>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'time'\">\r\n <div class=\"picker-field\">\r\n <p-datepicker #timePicker #timeField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\" [timeOnly]=\"true\"\r\n hourFormat=\"24\" [disabled]=\"col?.option?.disabled\" [autoZIndex]=\"true\"\r\n [baseZIndex]=\"10000\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(col.scope?.reference)\r\n ][\r\n col.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"col?.scope?.style?.classes?.[0] || ''\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\" appendTo=\"body\"\r\n [panelStyleClass]=\"picker.timePanelClass\"\r\n [vlCustomKeyboard]=\"col?.option?.keyboardType ?? 'time'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, timePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(timePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\" aria-label=\"Open time picker\"\r\n (pointerdown)=\"picker.openFromIcon($event, timePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-clock\"></i>\r\n </button>\r\n </div>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'autocomplete'\">\r\n <p-autocomplete #autocompleteField=\"ngModel\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"form.propertySchema[getScope(col.scope?.reference)][col.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n [class]=\"col?.scope?.style?.classes ? col?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"col.option.autocomplete.response.label\"\r\n [placeholder]=\"form.propertySchema[getScope(col.scope?.reference)][col.scope?.reference.split('/').pop()]?.placeholder\"\r\n [vlCustomKeyboard]=\"col?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'dropdown'\">\r\n <select id=\"dynamic-dropdown\" #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(col?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style)?.style\"\r\n [name]=\"col.scope?.reference\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(col.scope?.reference)][\r\n col.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of form.propertySchema[getScope(col.scope?.reference)]?.[\r\n col.scope?.reference.split('/').pop()\r\n ]?.data\r\n \" [value]=\"item[col?.option?.optionValue]\">\r\n {{ item[col?.option?.optionLabel] }}\r\n </option>\r\n </select>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"!col?.scope?.reference\">\r\n <span *ngIf=\"col.option?.actionColumn\">\r\n <span nz-icon\r\n *ngIf=\"setActionColumn(col.option?.save, data)\"\r\n [nzType]=\"col.option?.save?.icon\"\r\n [nzTheme]=\"'twotone'\"\r\n [style]=\"col.option?.save?.style\"\r\n [nzTwotoneColor]=\"'#52c41a'\"\r\n [class]=\"col.option?.save?.style?.classes[0]\"\r\n (click)=\"upsertAction(col.option?.save, data)\">\r\n </span>\r\n <span nz-icon\r\n *ngIf=\"setActionColumn(col.option?.delete, data)\"\r\n [nzType]=\"col.option?.delete?.icon\"\r\n [nzTheme]=\"'twotone'\"\r\n [style]=\"col.option?.delete?.style\"\r\n [nzTwotoneColor]=\"'#FF0000'\"\r\n [class]=\"col.option?.delete?.style?.classes[0]\"\r\n (click)=\"upsertAction(col.option?.delete, data)\"></span>\r\n\r\n </span>\r\n <span *ngIf=\"!col.option?.actionColumn\">\r\n <button type=\"submit\"\r\n [style]=\"getStyleWithoutClasses(col?.label?.style).style\"\r\n (click)=\"invokeGridBtnClick(col.option, data)\"\r\n [class]=\"col?.label?.style?.classes[0]\">\r\n {{ col.label?.caption }}\r\n </button>\r\n </span>\r\n </ng-container>\r\n\r\n <!-- For quantity column, display increment/decrement buttons -->\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'quantity'\">\r\n <div style=\"display: flex; align-items: center;\">\r\n <button pButton icon=\"pi pi-minus\"\r\n (click)=\"decrementQuantity(i,col.scope?.reference.split('/').pop(), col.option)\"\r\n [disabled]=\"data.quantity <= 0\"\r\n style=\"margin-right: 8px; height: 10px; width: 10px;\"></button>\r\n <span>{{ data[col.scope?.reference.split('/').pop()]\r\n }}</span>\r\n <button pButton icon=\"pi pi-plus\"\r\n (click)=\"incrementQuantity(i, col.scope?.reference.split('/').pop(), col.option)\"\r\n style=\"margin-left: 8px; height: 10px; width: 10px;\"></button>\r\n </div>\r\n </ng-container>\r\n </td>\r\n </tr>\r\n </tbody>\r\n </nz-table>\r\n</div>\r\n\r\n<!-- table footer -->\r\n<div *ngIf=\"tableitemInfo?.footer?.formFields && tableitemInfo.data?.length\"\r\n class=\"table-footer flex flex-column align-items-end gap-2 pt-2 mx-4 border-top-1 border-cyan-500\">\r\n <!-- @contract decision: the footer renders string and number only\r\n reality: the switch below carries those two cases and a default. It did NOT change\r\n when the columns gained their types on 2026-09-18.\r\n failure: any other type in a footer field renders empty.\r\n instead: keep aggregates numeric, or pre-format them as text in the endpoint. -->\r\n <div *ngFor=\"let tableField of tableitemInfo?.footer?.formFields\">\r\n <div\r\n [ngSwitch]=\"tableField?.scope?.reference && tableitemInfo.propertySchema[getScope(tableField.scope?.reference)] && tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()] ?\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.type : 'default'\">\r\n\r\n <!-- string control -->\r\n <div *ngSwitchCase=\"'string'\" class=\"form-group\">\r\n <span *ngIf=\"tableField.option.protected\">\r\n <h5 style=\"display: flex; justify-content: space-between;\">\r\n <span\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">\r\n {{\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }}\r\n </span>\r\n <span>\r\n {{\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.value\r\n }}\r\n </span>\r\n </h5>\r\n </span>\r\n <div *ngIf=\"!tableField.option.protected\">\r\n <div\r\n [class]=\"tableField?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">{{\r\n tableField.label?.value ?\r\n tableField.label?.value :\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <input type=\"text\" #inputField=\"ngModel\"\r\n [required]=\"tableField.option.required\"\r\n [email]=\"tableField.option.email\"\r\n [class]=\"tableField.scope?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(tableField?.scope?.style).style\"\r\n [(ngModel)]=\"tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"tableField.scope?.reference\"\r\n [placeholder]=\"tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.placeholder\" />\r\n </div>\r\n <!-- Error Message -->\r\n <div *ngIf=\"inputField?.touched && inputField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(tableField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"inputField?.errors?.['required']\">{{tableField.label?.value\r\n ?\r\n tableField.label?.value :\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"inputField?.errors?.['email']\">{{(tableField.label?.value\r\n ?\r\n tableField.label?.value :\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- number control -->\r\n <div *ngSwitchCase=\"'number'\" class=\"form-group\">\r\n <span *ngIf=\"tableField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">\r\n <h5>{{\r\n form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }} {{\r\n form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.value\r\n |\r\n currency:tableField.option.currency :true:\r\n tableField.option?.decimalPoint}}\r\n </h5>\r\n </span>\r\n <span *ngIf=\"!tableField.option.protected\">\r\n <div\r\n [class]=\"tableField?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">{{\r\n tableField.label?.value ?\r\n tableField.label?.value :\r\n form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <input type=\"number\"\r\n [class]=\"tableField.scope?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(tableField?.scope?.style).style\"\r\n [(ngModel)]=\"form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"tableField.scope?.reference\"\r\n [placeholder]=\"form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.placeholder\" />\r\n </div>\r\n </span>\r\n </div>\r\n\r\n <!-- button control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button type=\"submit\"\r\n [style]=\"getStyleWithoutClasses(tableField.label.style).style\"\r\n (click)=\"invokeGridBtnClick(tableField.option, tableitemInfo.propertySchema)\">\r\n {{ tableField.label?.caption }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<!-- bottom pagination on grid -->\r\n<div\r\n *ngIf=\"tableitemInfo.data?.length && !tableitemInfo?.layoutSchema?.pagination?.top && tableitemInfo?.layoutSchema?.pagination?.bottom\">\r\n <vl-pagination [itemInfo]=\"tableitemInfo\" [pageSize]=\"pageSize\"\r\n [currentPage]=\"currentPage\" [pages]=\"pages\"\r\n (pageSizeChange)=\"invokeOnPageSizeChange($event)\"\r\n (onGoToPage)=\"invokeGoToPage($event)\">\r\n </vl-pagination>\r\n</div>\r\n", styles: [".grid-filter-row th{padding:8px;vertical-align:middle;background:#fafafa}.grid-filter-cell{min-width:150px}.grid-filter-input-wrapper{display:flex;align-items:stretch;width:100%}.grid-filter-input{width:100%;min-width:0;min-height:34px;padding:5px 8px;border:1px solid #d9d9d9;border-right:0;border-radius:4px 0 0 4px;outline:none}.grid-filter-input:focus{border-color:#1677ff}.grid-filter-operator-button,.grid-filter-clear-button{display:inline-flex;align-items:center;justify-content:center;min-width:36px;min-height:34px;padding:0;cursor:pointer;background:#fff;border:1px solid #d9d9d9}.grid-filter-operator-button{color:#595959;border-radius:0 4px 4px 0}.grid-filter-operator-button:hover,.grid-filter-operator-button--active{color:#1677ff;border-color:#1677ff}.grid-filter-clear-button{margin-left:5px;color:#ff4d4f;border-radius:4px}.grid-filter-clear-button:hover{color:#fff;background:#ff4d4f;border-color:#ff4d4f}.grid-filter-operator-menu{min-width:190px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i5.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i5.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.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: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.EmailValidator, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: ["email"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: NzTableModule }, { kind: "component", type: i7$1.NzTableComponent, selector: "nz-table", inputs: ["nzTableLayout", "nzShowTotal", "nzItemRender", "nzTitle", "nzFooter", "nzNoResult", "nzPageSizeOptions", "nzVirtualItemSize", "nzVirtualMaxBufferPx", "nzVirtualMinBufferPx", "nzVirtualForTrackBy", "nzLoadingDelay", "nzPageIndex", "nzPageSize", "nzTotal", "nzWidthConfig", "nzData", "nzCustomColumn", "nzPaginationPosition", "nzScroll", "noDataVirtualHeight", "nzPaginationType", "nzFrontPagination", "nzTemplateMode", "nzShowPagination", "nzLoading", "nzOuterBordered", "nzLoadingIndicator", "nzBordered", "nzSize", "nzShowSizeChanger", "nzHideOnSinglePage", "nzShowQuickJumper", "nzSimple"], outputs: ["nzPageSizeChange", "nzPageIndexChange", "nzQueryParams", "nzCurrentPageDataChange", "nzCustomColumnChange"], exportAs: ["nzTable"] }, { kind: "component", type: i7$1.NzThAddOnComponent, selector: "th[nzColumnKey], th[nzSortFn], th[nzSortOrder], th[nzFilters], th[nzShowSort], th[nzShowFilter], th[nzCustomFilter]", inputs: ["nzColumnKey", "nzFilterMultiple", "nzSortOrder", "nzSortPriority", "nzSortDirections", "nzFilters", "nzSortFn", "nzFilterFn", "nzShowSort", "nzShowFilter", "nzCustomFilter"], outputs: ["nzCheckedChange", "nzSortOrderChange", "nzFilterChange"] }, { kind: "directive", type: i7$1.NzTableCellDirective, selector: "th:not(.nz-disable-th), td:not(.nz-disable-td)" }, { kind: "directive", type: i7$1.NzThMeasureDirective, selector: "th", inputs: ["nzWidth", "colspan", "colSpan", "rowspan", "rowSpan"] }, { kind: "component", type: i7$1.NzTheadComponent, selector: "thead:not(.ant-table-thead)", outputs: ["nzSortOrderChange"] }, { kind: "component", type: i7$1.NzTbodyComponent, selector: "tbody" }, { kind: "directive", type: i7$1.NzTrDirective, selector: "tr:not([nz-table-measure-row]):not([nzExpand]):not([nz-table-fixed-row])" }, { kind: "ngmodule", type: NzIconModule }, { kind: "directive", type: i11.NzIconDirective, selector: "nz-icon,[nz-icon]", inputs: ["nzSpin", "nzRotate", "nzType", "nzTheme", "nzTwotoneColor", "nzIconfont"], exportAs: ["nzIcon"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i9.ButtonDirective, selector: "[pButton]", inputs: ["ptButtonDirective", "pButtonPT", "pButtonUnstyled", "hostName", "text", "plain", "raised", "size", "outlined", "rounded", "iconPos", "loadingIcon", "fluid", "label", "icon", "loading", "buttonProps", "severity"] }, { kind: "ngmodule", type: NzDropDownModule }, { kind: "directive", type: i11$1.NzMenuDirective, selector: "[nz-menu]", inputs: ["nzInlineIndent", "nzTheme", "nzMode", "nzInlineCollapsed", "nzSelectable"], outputs: ["nzClick"], exportAs: ["nzMenu"] }, { kind: "component", type: i11$1.NzMenuItemComponent, selector: "[nz-menu-item]", inputs: ["nzPaddingLeft", "nzDisabled", "nzSelected", "nzDanger", "nzMatchRouterExact", "nzMatchRouter"], exportAs: ["nzMenuItem"] }, { kind: "directive", type: i11$2.NzDropdownDirective, selector: "[nz-dropdown]", inputs: ["nzDropdownMenu", "nzTrigger", "nzMatchWidthElement", "nzBackdrop", "nzClickHide", "nzDisabled", "nzVisible", "nzArrow", "nzOverlayClassName", "nzOverlayStyle", "nzPlacement"], outputs: ["nzVisibleChange"], exportAs: ["nzDropdown"] }, { kind: "component", type: i11$2.NzDropdownMenuComponent, selector: "nz-dropdown-menu", exportAs: ["nzDropdownMenu"] }, { kind: "ngmodule", type: NzMenuModule }, { kind: "component", type: PaginationComponent, selector: "vl-pagination", inputs: ["itemInfo", "pageSize", "currentPage", "selectedLanguageInfo", "pages"], outputs: ["pageSizeChange", "onGoToPage"] }, { kind: "ngmodule", type: NzDatePickerModule }, { kind: "component", type: i4.NzDatePickerComponent, selector: "nz-date-picker,nz-week-picker,nz-month-picker,nz-quarter-picker,nz-year-picker,nz-range-picker", inputs: ["nzInline", "nzAllowClear", "nzAutoFocus", "nzDisabled", "nzInputReadOnly", "nzOpen", "nzDisabledDate", "nzLocale", "nzPlaceHolder", "nzPopupStyle", "nzDropdownClassName", "nzSize", "nzStatus", "nzFormat", "nzVariant", "nzDateRender", "nzDisabledTime", "nzRenderExtraFooter", "nzShowToday", "nzMode", "nzShowNow", "nzRanges", "nzDefaultPickerValue", "nzSeparator", "nzSuffixIcon", "nzBackdrop", "nzId", "nzPlacement", "nzShowWeekNumber", "nzShowTime"], outputs: ["nzOnPanelChange", "nzOnCalendarChange", "nzOnOk", "nzOnOpenChange"], exportAs: ["nzDatePicker"] }, { kind: "directive", type: i4.NzRangePickerComponent, selector: "nz-range-picker", exportAs: ["nzRangePicker"] }, { kind: "ngmodule", type: DatePickerModule }, { kind: "component", type: i12.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: AutoCompleteModule }, { kind: "component", type: i18.AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "minQueryLength", "delay", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "addOnTab", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "addOnBlur", "separator", "appendTo", "motionOptions"], outputs: ["completeMethod", "onSelect", "onUnselect", "onAdd", "onFocus", "onBlur", "onDropdownClick", "onClear", "onInputKeydown", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: CustomKeyboardDirective, selector: "[vlCustomKeyboard]", inputs: ["vlCustomKeyboard"] }, { kind: "pipe", type: i5.DecimalPipe, name: "number" }, { kind: "pipe", type: i5.CurrencyPipe, name: "currency" }, { kind: "pipe", type: i5.DatePipe, name: "date" }] });
3682
4247
  }
3683
4248
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: GridComponent, decorators: [{
3684
4249
  type: Component,
@@ -3696,7 +4261,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
3696
4261
  AutoCompleteModule,
3697
4262
  InputTextModule,
3698
4263
  CustomKeyboardDirective,
3699
- ], selector: 'vl-grid', template: "<!-- Shuaib 2026-09-24\r\n - change: grid cells render legacy checkbox fields through the boolean control. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - new: @contract comments on the sorting, on the column filter and on the footer. The filter\r\n reads option.filter and never option.columnFilter, which is the mistake a layout author\r\n makes without being told -->\r\n<div *ngIf=\"!tableitemInfo.data?.length\" class=\"alert alert-warning col-md-12\"\r\n role=\"alert\">\r\n {{ tableitemInfo.layoutSchema?.noRecordMessage}}\r\n</div>\r\n<div *ngIf=\"tableitemInfo.data?.length\" class=\"reservation-container pt-5 px-3\">\r\n <nz-table #basicTable [nzData]=\"gridDisplayData\"\r\n [nzNoResult]=\"'No matching records found'\" [nzBordered]=\"true\"\r\n [nzFrontPagination]=\"currenTableOptions?.pagination?.tabs\"\r\n [style]=\"getStyleWithoutClasses(currenTableOptions?.style).style\"\r\n (scroll)=\"onScrollEvent($event)\"\r\n [nzLoading]=\"isTableDataLoad\">\r\n <thead>\r\n <!-- @contract decision: option.sortable is read here\r\n reality: nzSortFn is wired to getColumnSortFn since 2026-09-16; before that\r\n the key was decoration. The column also needs a scope.reference -\r\n without one getColumnSortFn has nothing to sort by.\r\n seen: grid.component.ts:338 -->\r\n <!-- Column headings and sorting -->\r\n <tr>\r\n <th *ngFor=\"let column of tableitemInfo.layoutSchema.elements\"\r\n [nzSortFn]=\"\r\n column?.option?.sortable\r\n ? getColumnSortFn(column)\r\n : false\r\n \">\r\n\r\n {{ column?.label?.caption }}\r\n </th>\r\n </tr>\r\n\r\n <!-- @contract decision: the column filter reads option.filter, NOT option.columnFilter\r\n reality: option.filter.filtering switches it on, option.filter.defaultOperator\r\n starts the comparison (the renderer falls back to contains), and\r\n option.filter.placeholder labels the input.\r\n failure: option.columnFilter is read nowhere, here or in the runtime app. A\r\n column carrying it looks filterable in the file and is not on screen.\r\n seen: grid.component.ts:450-475; 5 uses of columnFilter across the reference\r\n projects, all of them dead -->\r\n <!-- Column filters -->\r\n <tr *ngIf=\"hasAnyColumnFilter()\" class=\"grid-filter-row\">\r\n\r\n <th *ngFor=\"let column of tableitemInfo.layoutSchema.elements\"\r\n class=\"grid-filter-cell\">\r\n\r\n <ng-container *ngIf=\"\r\n column?.option?.filter?.filtering === true &&\r\n column?.scope?.reference\r\n \">\r\n\r\n <div class=\"grid-filter-input-wrapper\">\r\n <input type=\"text\" class=\"grid-filter-input\"\r\n [placeholder]=\"getColumnFilterPlaceholder(column)\"\r\n [ngModel]=\"getColumnFilterState(column).value\"\r\n (ngModelChange)=\"\r\n setColumnFilterValue(column, $event)\r\n \" />\r\n\r\n <!-- Operator selection button -->\r\n <button type=\"button\"\r\n class=\"grid-filter-operator-button\" nz-dropdown\r\n nzTrigger=\"click\"\r\n [nzDropdownMenu]=\"operatorMenu\"\r\n [class.grid-filter-operator-button--active]=\"\r\n hasActiveColumnFilter(column)\r\n \" [title]=\"\r\n 'Operator: ' +\r\n getFilterOperatorLabel(\r\n getColumnFilterState(column).operator\r\n )\r\n \">\r\n\r\n <span nz-icon nzType=\"filter\"></span>\r\n </button>\r\n\r\n <!-- Clear current column filter -->\r\n <button *ngIf=\"hasActiveColumnFilter(column)\"\r\n type=\"button\" class=\"grid-filter-clear-button\"\r\n aria-label=\"Clear column filter\"\r\n title=\"Clear filter\"\r\n (click)=\"clearColumnFilter(column)\">\r\n\r\n <span nz-icon nzType=\"close\"></span>\r\n </button>\r\n </div>\r\n\r\n <!-- Operator dropdown -->\r\n <nz-dropdown-menu #operatorMenu=\"nzDropdownMenu\">\r\n <ul nz-menu class=\"grid-filter-operator-menu\">\r\n <li *ngFor=\"let operator of filterOperators\"\r\n nz-menu-item [nzSelected]=\"\r\n getColumnFilterState(column).operator ===\r\n operator.value\r\n \" (click)=\"\r\n setColumnFilterOperator(\r\n column,\r\n operator.value\r\n )\r\n \">\r\n\r\n <span *ngIf=\"\r\n getColumnFilterState(column).operator ===\r\n operator.value\r\n \" nz-icon nzType=\"check\">\r\n </span>\r\n\r\n {{ operator.label }}\r\n </li>\r\n </ul>\r\n </nz-dropdown-menu>\r\n </ng-container>\r\n </th>\r\n </tr>\r\n </thead>\r\n <!-- @contract decision: the column chain below, and what it does not carry\r\n reality: string, number, image, quantity, date, time, dropdown, boolean,\r\n autocomplete and dateRange each have their own condition. There is no\r\n branch for the credit card type.\r\n failure: a column whose type has no branch stays empty, with no error.\r\n instead: format the value in the endpoint and send a string.\r\n\r\n @contract decision: columns without scope.reference are valid\r\n reality: with option.actionColumn they render save/delete icons, otherwise a\r\n button. That is why the conditions below all guard on the reference. -->\r\n <tbody>\r\n <tr *ngFor=\"let data of basicTable.data; let i = index\">\r\n <td *ngFor=\"let col of tableitemInfo.layoutSchema.elements\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\">\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'string'\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\">\r\n <span *ngIf=\"col.option.protected\">\r\n <span *ngIf=\"col.option?.link\"\r\n (click)=\"showLinkDetails(col.option, data)\">\r\n {{data[col.scope?.reference.split('/').pop()]}}\r\n </span>\r\n <span *ngIf=\"!col.option?.link\">\r\n <ng-container\r\n *ngIf=\"col?.option?.dateFormat; else string\">\r\n {{\r\n data[col.scope?.reference.split('/').pop()]\r\n | date:'yyyy-MM-dd' }}\r\n </ng-container>\r\n <ng-template #string>\r\n {{\r\n data[col.scope?.reference.split('/').pop()]\r\n }}\r\n </ng-template>\r\n </span>\r\n </span>\r\n <span *ngIf=\"!col.option.protected\">\r\n <input\r\n [(ngModel)]=\"data[col.scope?.reference.split('/').pop()]\"\r\n class=\"p-inputtext p-component\" />\r\n </span>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'number'\">\r\n <span *ngIf=\"col.option.protected\">\r\n <span\r\n *ngIf=\"col.option?.innerAction?.action == 'multiply'\">\r\n {{\r\n (data[col.option?.innerAction?.from] *\r\n data[col.option?.innerAction?.to])\r\n | number:'1.2-2'\r\n }}\r\n </span>\r\n <span *ngIf=\"!col.option?.innerAction\">\r\n {{data[col.scope?.reference.split('/').pop()] |\r\n currency:\r\n tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.currency\r\n :true }}\r\n </span>\r\n </span>\r\n <span *ngIf=\"!col.option.protected\">\r\n <input\r\n [(ngModel)]=\"data[col.scope?.reference.split('/').pop()]\"\r\n type=\"number\"\r\n class=\"p-inputtext p-component\" />\r\n </span>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'image'\">\r\n <img *ngIf=\"data[col.scope?.reference.split('/').pop()]\"\r\n [src]=\"data[col.scope?.reference.split('/').pop()]\"\r\n alt=\"Image\"\r\n (click)=\"invokeTableImageClick(col, data)\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\" />\r\n </ng-container>\r\n\r\n <!-- @contract compatibility: existing checkbox fields render as boolean. -->\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && (tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'boolean' || isLegacyCheckboxFieldType(tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type))\">\r\n <input type=\"checkbox\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n class=\"form-check-input mr-2\"\r\n [(ngModel)]=\"data[col.scope?.reference.split('/').pop()]\"\r\n [name]=\"col.scope?.reference\" />\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'dateRange'\">\r\n <nz-range-picker #dateRangeField=\"ngModel\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\"\r\n [style]=\"getStyleWithoutClasses(col?.style).style\"\r\n [(ngModel)]=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)][col.scope?.reference.split('/').pop()].value\"></nz-range-picker>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'date'\">\r\n <p-datepicker #datePicker #dateField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\"\r\n [disabled]=\"col?.option?.disabled\" [showIcon]=\"true\"\r\n [showOnFocus]=\"false\" appendTo=\"body\"\r\n [autoZIndex]=\"true\" [baseZIndex]=\"10000\"\r\n panelStyleClass=\"model-datepicker-panel\"\r\n [dateFormat]=\"\r\n tableitemInfo.propertySchema[\r\n getScope(col.scope?.reference)\r\n ][\r\n col.scope?.reference.split('/').pop()\r\n ]?.format || 'yy-mm-dd'\r\n \" [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(col.scope?.reference)\r\n ][\r\n col.scope?.reference.split('/').pop()\r\n ].value\r\n \" [class]=\"col?.scope?.style?.classes?.[0] || ''\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\">\r\n <ng-template #inputicon>\r\n <i class=\"pi pi-calendar\"\r\n aria-label=\"Open calendar\">\r\n </i>\r\n </ng-template>\r\n </p-datepicker>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'time'\">\r\n <p-datepicker #timePicker #timeField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\" [timeOnly]=\"true\"\r\n hourFormat=\"24\" [disabled]=\"col?.option?.disabled\"\r\n [showIcon]=\"true\" [showOnFocus]=\"false\"\r\n appendTo=\"body\" [autoZIndex]=\"true\"\r\n [baseZIndex]=\"10000\"\r\n panelStyleClass=\"model-datepicker-panel\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(col.scope?.reference)\r\n ][\r\n col.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"col?.scope?.style?.classes?.[0] || ''\">\r\n\r\n <ng-template #inputicon>\r\n <i class=\"pi pi-clock\"\r\n aria-label=\"Open time picker\">\r\n </i>\r\n </ng-template>\r\n </p-datepicker>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'autocomplete'\">\r\n <p-autocomplete #autocompleteField=\"ngModel\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"form.propertySchema[getScope(col.scope?.reference)][col.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n [class]=\"col?.scope?.style?.classes ? col?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"col.option.autocomplete.response.label\"\r\n [placeholder]=\"form.propertySchema[getScope(col.scope?.reference)][col.scope?.reference.split('/').pop()]?.placeholder\"\r\n [vlCustomKeyboard]=\"col?.option?.keyboardType\">\r\n </p-autocomplete>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'dropdown'\">\r\n <select id=\"dynamic-dropdown\" #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(col?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style)?.style\"\r\n [name]=\"col.scope?.reference\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(col.scope?.reference)][\r\n col.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of form.propertySchema[getScope(col.scope?.reference)]?.[\r\n col.scope?.reference.split('/').pop()\r\n ]?.data\r\n \" [value]=\"item[col?.option?.optionValue]\">\r\n {{ item[col?.option?.optionLabel] }}\r\n </option>\r\n </select>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"!col?.scope?.reference\">\r\n <span *ngIf=\"col.option?.actionColumn\">\r\n <span nz-icon\r\n *ngIf=\"setActionColumn(col.option?.save, data)\"\r\n [nzType]=\"col.option?.save?.icon\"\r\n [nzTheme]=\"'twotone'\"\r\n [style]=\"col.option?.save?.style\"\r\n [nzTwotoneColor]=\"'#52c41a'\"\r\n [class]=\"col.option?.save?.style?.classes[0]\"\r\n (click)=\"upsertAction(col.option?.save, data)\">\r\n </span>\r\n <span nz-icon\r\n *ngIf=\"setActionColumn(col.option?.delete, data)\"\r\n [nzType]=\"col.option?.delete?.icon\"\r\n [nzTheme]=\"'twotone'\"\r\n [style]=\"col.option?.delete?.style\"\r\n [nzTwotoneColor]=\"'#FF0000'\"\r\n [class]=\"col.option?.delete?.style?.classes[0]\"\r\n (click)=\"upsertAction(col.option?.delete, data)\"></span>\r\n\r\n </span>\r\n <span *ngIf=\"!col.option?.actionColumn\">\r\n <button type=\"submit\"\r\n [style]=\"getStyleWithoutClasses(col?.label?.style).style\"\r\n (click)=\"invokeGridBtnClick(col.option, data)\"\r\n [class]=\"col?.label?.style?.classes[0]\">\r\n {{ col.label?.caption }}\r\n </button>\r\n </span>\r\n </ng-container>\r\n\r\n <!-- For quantity column, display increment/decrement buttons -->\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'quantity'\">\r\n <div style=\"display: flex; align-items: center;\">\r\n <button pButton icon=\"pi pi-minus\"\r\n (click)=\"decrementQuantity(i,col.scope?.reference.split('/').pop(), col.option)\"\r\n [disabled]=\"data.quantity <= 0\"\r\n style=\"margin-right: 8px; height: 10px; width: 10px;\"></button>\r\n <span>{{ data[col.scope?.reference.split('/').pop()]\r\n }}</span>\r\n <button pButton icon=\"pi pi-plus\"\r\n (click)=\"incrementQuantity(i, col.scope?.reference.split('/').pop(), col.option)\"\r\n style=\"margin-left: 8px; height: 10px; width: 10px;\"></button>\r\n </div>\r\n </ng-container>\r\n </td>\r\n </tr>\r\n </tbody>\r\n </nz-table>\r\n</div>\r\n\r\n<!-- table footer -->\r\n<div *ngIf=\"tableitemInfo?.footer?.formFields && tableitemInfo.data?.length\"\r\n class=\"table-footer flex flex-column align-items-end gap-2 pt-2 mx-4 border-top-1 border-cyan-500\">\r\n <!-- @contract decision: the footer renders string and number only\r\n reality: the switch below carries those two cases and a default. It did NOT change\r\n when the columns gained their types on 2026-09-18.\r\n failure: any other type in a footer field renders empty.\r\n instead: keep aggregates numeric, or pre-format them as text in the endpoint. -->\r\n <div *ngFor=\"let tableField of tableitemInfo?.footer?.formFields\">\r\n <div\r\n [ngSwitch]=\"tableField?.scope?.reference && tableitemInfo.propertySchema[getScope(tableField.scope?.reference)] && tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()] ?\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.type : 'default'\">\r\n\r\n <!-- string control -->\r\n <div *ngSwitchCase=\"'string'\" class=\"form-group\">\r\n <span *ngIf=\"tableField.option.protected\">\r\n <h5 style=\"display: flex; justify-content: space-between;\">\r\n <span\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">\r\n {{\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }}\r\n </span>\r\n <span>\r\n {{\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.value\r\n }}\r\n </span>\r\n </h5>\r\n </span>\r\n <div *ngIf=\"!tableField.option.protected\">\r\n <div\r\n [class]=\"tableField?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">{{\r\n tableField.label?.value ?\r\n tableField.label?.value :\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <input type=\"text\" #inputField=\"ngModel\"\r\n [required]=\"tableField.option.required\"\r\n [email]=\"tableField.option.email\"\r\n [class]=\"tableField.scope?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(tableField?.scope?.style).style\"\r\n [(ngModel)]=\"tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"tableField.scope?.reference\"\r\n [placeholder]=\"tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.placeholder\" />\r\n </div>\r\n <!-- Error Message -->\r\n <div *ngIf=\"inputField?.touched && inputField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(tableField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"inputField?.errors?.['required']\">{{tableField.label?.value\r\n ?\r\n tableField.label?.value :\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"inputField?.errors?.['email']\">{{(tableField.label?.value\r\n ?\r\n tableField.label?.value :\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- number control -->\r\n <div *ngSwitchCase=\"'number'\" class=\"form-group\">\r\n <span *ngIf=\"tableField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">\r\n <h5>{{\r\n form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }} {{\r\n form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.value\r\n |\r\n currency:tableField.option.currency :true:\r\n tableField.option?.decimalPoint}}\r\n </h5>\r\n </span>\r\n <span *ngIf=\"!tableField.option.protected\">\r\n <div\r\n [class]=\"tableField?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">{{\r\n tableField.label?.value ?\r\n tableField.label?.value :\r\n form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <input type=\"number\"\r\n [class]=\"tableField.scope?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(tableField?.scope?.style).style\"\r\n [(ngModel)]=\"form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"tableField.scope?.reference\"\r\n [placeholder]=\"form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.placeholder\" />\r\n </div>\r\n </span>\r\n </div>\r\n\r\n <!-- button control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button type=\"submit\"\r\n [style]=\"getStyleWithoutClasses(tableField.label.style).style\"\r\n (click)=\"invokeGridBtnClick(tableField.option, tableitemInfo.propertySchema)\">\r\n {{ tableField.label?.caption }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<!-- bottom pagination on grid -->\r\n<div\r\n *ngIf=\"tableitemInfo.data?.length && !tableitemInfo?.layoutSchema?.pagination?.top && tableitemInfo?.layoutSchema?.pagination?.bottom\">\r\n <vl-pagination [itemInfo]=\"tableitemInfo\" [pageSize]=\"pageSize\"\r\n [currentPage]=\"currentPage\" [pages]=\"pages\"\r\n (pageSizeChange)=\"invokeOnPageSizeChange($event)\"\r\n (onGoToPage)=\"invokeGoToPage($event)\">\r\n </vl-pagination>\r\n</div>\r\n", styles: [".grid-filter-row th{padding:8px;vertical-align:middle;background:#fafafa}.grid-filter-cell{min-width:150px}.grid-filter-input-wrapper{display:flex;align-items:stretch;width:100%}.grid-filter-input{width:100%;min-width:0;min-height:34px;padding:5px 8px;border:1px solid #d9d9d9;border-right:0;border-radius:4px 0 0 4px;outline:none}.grid-filter-input:focus{border-color:#1677ff}.grid-filter-operator-button,.grid-filter-clear-button{display:inline-flex;align-items:center;justify-content:center;min-width:36px;min-height:34px;padding:0;cursor:pointer;background:#fff;border:1px solid #d9d9d9}.grid-filter-operator-button{color:#595959;border-radius:0 4px 4px 0}.grid-filter-operator-button:hover,.grid-filter-operator-button--active{color:#1677ff;border-color:#1677ff}.grid-filter-clear-button{margin-left:5px;color:#ff4d4f;border-radius:4px}.grid-filter-clear-button:hover{color:#fff;background:#ff4d4f;border-color:#ff4d4f}.grid-filter-operator-menu{min-width:190px}\n"] }]
4264
+ ], selector: 'vl-grid', template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\r\n - change: autocomplete fields take the toolkit string keyboard unless option.keyboardType\r\n names another one ('native' keeps the system keyboard)\r\n - change: date and time cells use the shared picker markup (picker-field, DateTimePickerService);\r\n their PrimeNG icon opened the calendar together with the system keyboard -->\r\n\r\n<!-- Shuaib 2026-09-24\r\n - change: grid cells render legacy checkbox fields through the boolean control. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - new: @contract comments on the sorting, on the column filter and on the footer. The filter\r\n reads option.filter and never option.columnFilter, which is the mistake a layout author\r\n makes without being told -->\r\n<div *ngIf=\"!tableitemInfo.data?.length\" class=\"alert alert-warning col-md-12\"\r\n role=\"alert\">\r\n {{ tableitemInfo.layoutSchema?.noRecordMessage}}\r\n</div>\r\n<div *ngIf=\"tableitemInfo.data?.length\" class=\"reservation-container pt-5 px-3\">\r\n <nz-table #basicTable [nzData]=\"gridDisplayData\"\r\n [nzNoResult]=\"'No matching records found'\" [nzBordered]=\"true\"\r\n [nzFrontPagination]=\"currenTableOptions?.pagination?.tabs\"\r\n [style]=\"getStyleWithoutClasses(currenTableOptions?.style).style\"\r\n (scroll)=\"onScrollEvent($event)\"\r\n [nzLoading]=\"isTableDataLoad\">\r\n <thead>\r\n <!-- @contract decision: option.sortable is read here\r\n reality: nzSortFn is wired to getColumnSortFn since 2026-09-16; before that\r\n the key was decoration. The column also needs a scope.reference -\r\n without one getColumnSortFn has nothing to sort by.\r\n seen: grid.component.ts:338 -->\r\n <!-- Column headings and sorting -->\r\n <tr>\r\n <th *ngFor=\"let column of tableitemInfo.layoutSchema.elements\"\r\n [nzSortFn]=\"\r\n column?.option?.sortable\r\n ? getColumnSortFn(column)\r\n : false\r\n \">\r\n\r\n {{ column?.label?.caption }}\r\n </th>\r\n </tr>\r\n\r\n <!-- @contract decision: the column filter reads option.filter, NOT option.columnFilter\r\n reality: option.filter.filtering switches it on, option.filter.defaultOperator\r\n starts the comparison (the renderer falls back to contains), and\r\n option.filter.placeholder labels the input.\r\n failure: option.columnFilter is read nowhere, here or in the runtime app. A\r\n column carrying it looks filterable in the file and is not on screen.\r\n seen: grid.component.ts:450-475; 5 uses of columnFilter across the reference\r\n projects, all of them dead -->\r\n <!-- Column filters -->\r\n <tr *ngIf=\"hasAnyColumnFilter()\" class=\"grid-filter-row\">\r\n\r\n <th *ngFor=\"let column of tableitemInfo.layoutSchema.elements\"\r\n class=\"grid-filter-cell\">\r\n\r\n <ng-container *ngIf=\"\r\n column?.option?.filter?.filtering === true &&\r\n column?.scope?.reference\r\n \">\r\n\r\n <div class=\"grid-filter-input-wrapper\">\r\n <input type=\"text\" class=\"grid-filter-input\"\r\n [placeholder]=\"getColumnFilterPlaceholder(column)\"\r\n [ngModel]=\"getColumnFilterState(column).value\"\r\n (ngModelChange)=\"\r\n setColumnFilterValue(column, $event)\r\n \" />\r\n\r\n <!-- Operator selection button -->\r\n <button type=\"button\"\r\n class=\"grid-filter-operator-button\" nz-dropdown\r\n nzTrigger=\"click\"\r\n [nzDropdownMenu]=\"operatorMenu\"\r\n [class.grid-filter-operator-button--active]=\"\r\n hasActiveColumnFilter(column)\r\n \" [title]=\"\r\n 'Operator: ' +\r\n getFilterOperatorLabel(\r\n getColumnFilterState(column).operator\r\n )\r\n \">\r\n\r\n <span nz-icon nzType=\"filter\"></span>\r\n </button>\r\n\r\n <!-- Clear current column filter -->\r\n <button *ngIf=\"hasActiveColumnFilter(column)\"\r\n type=\"button\" class=\"grid-filter-clear-button\"\r\n aria-label=\"Clear column filter\"\r\n title=\"Clear filter\"\r\n (click)=\"clearColumnFilter(column)\">\r\n\r\n <span nz-icon nzType=\"close\"></span>\r\n </button>\r\n </div>\r\n\r\n <!-- Operator dropdown -->\r\n <nz-dropdown-menu #operatorMenu=\"nzDropdownMenu\">\r\n <ul nz-menu class=\"grid-filter-operator-menu\">\r\n <li *ngFor=\"let operator of filterOperators\"\r\n nz-menu-item [nzSelected]=\"\r\n getColumnFilterState(column).operator ===\r\n operator.value\r\n \" (click)=\"\r\n setColumnFilterOperator(\r\n column,\r\n operator.value\r\n )\r\n \">\r\n\r\n <span *ngIf=\"\r\n getColumnFilterState(column).operator ===\r\n operator.value\r\n \" nz-icon nzType=\"check\">\r\n </span>\r\n\r\n {{ operator.label }}\r\n </li>\r\n </ul>\r\n </nz-dropdown-menu>\r\n </ng-container>\r\n </th>\r\n </tr>\r\n </thead>\r\n <!-- @contract decision: the column chain below, and what it does not carry\r\n reality: string, number, image, quantity, date, time, dropdown, boolean,\r\n autocomplete and dateRange each have their own condition. There is no\r\n branch for the credit card type.\r\n failure: a column whose type has no branch stays empty, with no error.\r\n instead: format the value in the endpoint and send a string.\r\n\r\n @contract decision: columns without scope.reference are valid\r\n reality: with option.actionColumn they render save/delete icons, otherwise a\r\n button. That is why the conditions below all guard on the reference. -->\r\n <tbody>\r\n <tr *ngFor=\"let data of basicTable.data; let i = index\">\r\n <td *ngFor=\"let col of tableitemInfo.layoutSchema.elements\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\">\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'string'\"\r\n [class]=\"getStyleWithoutClasses(col?.scope?.style).classes\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\">\r\n <span *ngIf=\"col.option.protected\">\r\n <span *ngIf=\"col.option?.link\"\r\n (click)=\"showLinkDetails(col.option, data)\">\r\n {{data[col.scope?.reference.split('/').pop()]}}\r\n </span>\r\n <span *ngIf=\"!col.option?.link\">\r\n <ng-container\r\n *ngIf=\"col?.option?.dateFormat; else string\">\r\n {{\r\n data[col.scope?.reference.split('/').pop()]\r\n | date:'yyyy-MM-dd' }}\r\n </ng-container>\r\n <ng-template #string>\r\n {{\r\n data[col.scope?.reference.split('/').pop()]\r\n }}\r\n </ng-template>\r\n </span>\r\n </span>\r\n <span *ngIf=\"!col.option.protected\">\r\n <input\r\n [(ngModel)]=\"data[col.scope?.reference.split('/').pop()]\"\r\n class=\"p-inputtext p-component\" />\r\n </span>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'number'\">\r\n <span *ngIf=\"col.option.protected\">\r\n <span\r\n *ngIf=\"col.option?.innerAction?.action == 'multiply'\">\r\n {{\r\n (data[col.option?.innerAction?.from] *\r\n data[col.option?.innerAction?.to])\r\n | number:'1.2-2'\r\n }}\r\n </span>\r\n <span *ngIf=\"!col.option?.innerAction\">\r\n {{data[col.scope?.reference.split('/').pop()] |\r\n currency:\r\n tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.currency\r\n :true }}\r\n </span>\r\n </span>\r\n <span *ngIf=\"!col.option.protected\">\r\n <input\r\n [(ngModel)]=\"data[col.scope?.reference.split('/').pop()]\"\r\n type=\"number\"\r\n class=\"p-inputtext p-component\" />\r\n </span>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'image'\">\r\n <img *ngIf=\"data[col.scope?.reference.split('/').pop()]\"\r\n [src]=\"data[col.scope?.reference.split('/').pop()]\"\r\n alt=\"Image\"\r\n (click)=\"invokeTableImageClick(col, data)\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\" />\r\n </ng-container>\r\n\r\n <!-- @contract compatibility: existing checkbox fields render as boolean. -->\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && (tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'boolean' || isLegacyCheckboxFieldType(tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type))\">\r\n <input type=\"checkbox\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n class=\"form-check-input mr-2\"\r\n [(ngModel)]=\"data[col.scope?.reference.split('/').pop()]\"\r\n [name]=\"col.scope?.reference\" />\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'dateRange'\">\r\n <nz-range-picker #dateRangeField=\"ngModel\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\"\r\n [style]=\"getStyleWithoutClasses(col?.style).style\"\r\n [(ngModel)]=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)][col.scope?.reference.split('/').pop()].value\"></nz-range-picker>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'date'\">\r\n <div class=\"picker-field\">\r\n <p-datepicker #datePicker #dateField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\"\r\n [disabled]=\"col?.option?.disabled\"\r\n [autoZIndex]=\"true\" [baseZIndex]=\"10000\"\r\n [dateFormat]=\"\r\n tableitemInfo.propertySchema[\r\n getScope(col.scope?.reference)\r\n ][\r\n col.scope?.reference.split('/').pop()\r\n ]?.format || 'yy-mm-dd'\r\n \" [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(col.scope?.reference)\r\n ][\r\n col.scope?.reference.split('/').pop()\r\n ].value\r\n \" [class]=\"col?.scope?.style?.classes?.[0] || ''\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\" appendTo=\"body\"\r\n [panelStyleClass]=\"picker.datePanelClass\" [motionOptions]=\"picker.dockedMotion\"\r\n [vlCustomKeyboard]=\"col?.option?.keyboardType ?? 'date'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, datePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(datePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\" aria-label=\"Open calendar\"\r\n (pointerdown)=\"picker.openFromIcon($event, datePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-calendar\"></i>\r\n </button>\r\n </div>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'time'\">\r\n <div class=\"picker-field\">\r\n <p-datepicker #timePicker #timeField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\" [timeOnly]=\"true\"\r\n hourFormat=\"24\" [disabled]=\"col?.option?.disabled\" [autoZIndex]=\"true\"\r\n [baseZIndex]=\"10000\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n [(ngModel)]=\"\r\n form.propertySchema[\r\n getScope(col.scope?.reference)\r\n ][\r\n col.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"col?.scope?.style?.classes?.[0] || ''\"\r\n [showIcon]=\"false\" [showOnFocus]=\"false\" appendTo=\"body\"\r\n [panelStyleClass]=\"picker.timePanelClass\"\r\n [vlCustomKeyboard]=\"col?.option?.keyboardType ?? 'time'\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, timePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(timePicker)\">\r\n </p-datepicker>\r\n <button type=\"button\" class=\"picker-field__icon\" aria-label=\"Open time picker\"\r\n (pointerdown)=\"picker.openFromIcon($event, timePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-clock\"></i>\r\n </button>\r\n </div>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'autocomplete'\">\r\n <p-autocomplete #autocompleteField=\"ngModel\"\r\n [required]=\"col.option.required\"\r\n [name]=\"col.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"form.propertySchema[getScope(col.scope?.reference)][col.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(col?.scope?.style).style\"\r\n [class]=\"col?.scope?.style?.classes ? col?.scope?.style?.classes[0] : ''\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"col.option.autocomplete.response.label\"\r\n [placeholder]=\"form.propertySchema[getScope(col.scope?.reference)][col.scope?.reference.split('/').pop()]?.placeholder\"\r\n [vlCustomKeyboard]=\"col?.option?.keyboardType ?? 'string'\">\r\n </p-autocomplete>\r\n </ng-container>\r\n\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'dropdown'\">\r\n <select id=\"dynamic-dropdown\" #dropdownField=\"ngModel\"\r\n [ngClass]=\"getStyleWithoutClasses(col?.scope?.style)?.classes\"\r\n [ngStyle]=\"getStyleWithoutClasses(col?.scope?.style)?.style\"\r\n [name]=\"col.scope?.reference\"\r\n [(ngModel)]=\"\r\n form.propertySchema[getScope(col.scope?.reference)][\r\n col.scope?.reference.split('/').pop()\r\n ].value\r\n \">\r\n <option *ngFor=\"\r\n let item of form.propertySchema[getScope(col.scope?.reference)]?.[\r\n col.scope?.reference.split('/').pop()\r\n ]?.data\r\n \" [value]=\"item[col?.option?.optionValue]\">\r\n {{ item[col?.option?.optionLabel] }}\r\n </option>\r\n </select>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"!col?.scope?.reference\">\r\n <span *ngIf=\"col.option?.actionColumn\">\r\n <span nz-icon\r\n *ngIf=\"setActionColumn(col.option?.save, data)\"\r\n [nzType]=\"col.option?.save?.icon\"\r\n [nzTheme]=\"'twotone'\"\r\n [style]=\"col.option?.save?.style\"\r\n [nzTwotoneColor]=\"'#52c41a'\"\r\n [class]=\"col.option?.save?.style?.classes[0]\"\r\n (click)=\"upsertAction(col.option?.save, data)\">\r\n </span>\r\n <span nz-icon\r\n *ngIf=\"setActionColumn(col.option?.delete, data)\"\r\n [nzType]=\"col.option?.delete?.icon\"\r\n [nzTheme]=\"'twotone'\"\r\n [style]=\"col.option?.delete?.style\"\r\n [nzTwotoneColor]=\"'#FF0000'\"\r\n [class]=\"col.option?.delete?.style?.classes[0]\"\r\n (click)=\"upsertAction(col.option?.delete, data)\"></span>\r\n\r\n </span>\r\n <span *ngIf=\"!col.option?.actionColumn\">\r\n <button type=\"submit\"\r\n [style]=\"getStyleWithoutClasses(col?.label?.style).style\"\r\n (click)=\"invokeGridBtnClick(col.option, data)\"\r\n [class]=\"col?.label?.style?.classes[0]\">\r\n {{ col.label?.caption }}\r\n </button>\r\n </span>\r\n </ng-container>\r\n\r\n <!-- For quantity column, display increment/decrement buttons -->\r\n <ng-container\r\n *ngIf=\"tableitemInfo.propertySchema[getScope(col.scope?.reference)] && tableitemInfo.propertySchema[getScope(col.scope?.reference)][col?.scope?.reference?.split('/').pop()]?.type === 'quantity'\">\r\n <div style=\"display: flex; align-items: center;\">\r\n <button pButton icon=\"pi pi-minus\"\r\n (click)=\"decrementQuantity(i,col.scope?.reference.split('/').pop(), col.option)\"\r\n [disabled]=\"data.quantity <= 0\"\r\n style=\"margin-right: 8px; height: 10px; width: 10px;\"></button>\r\n <span>{{ data[col.scope?.reference.split('/').pop()]\r\n }}</span>\r\n <button pButton icon=\"pi pi-plus\"\r\n (click)=\"incrementQuantity(i, col.scope?.reference.split('/').pop(), col.option)\"\r\n style=\"margin-left: 8px; height: 10px; width: 10px;\"></button>\r\n </div>\r\n </ng-container>\r\n </td>\r\n </tr>\r\n </tbody>\r\n </nz-table>\r\n</div>\r\n\r\n<!-- table footer -->\r\n<div *ngIf=\"tableitemInfo?.footer?.formFields && tableitemInfo.data?.length\"\r\n class=\"table-footer flex flex-column align-items-end gap-2 pt-2 mx-4 border-top-1 border-cyan-500\">\r\n <!-- @contract decision: the footer renders string and number only\r\n reality: the switch below carries those two cases and a default. It did NOT change\r\n when the columns gained their types on 2026-09-18.\r\n failure: any other type in a footer field renders empty.\r\n instead: keep aggregates numeric, or pre-format them as text in the endpoint. -->\r\n <div *ngFor=\"let tableField of tableitemInfo?.footer?.formFields\">\r\n <div\r\n [ngSwitch]=\"tableField?.scope?.reference && tableitemInfo.propertySchema[getScope(tableField.scope?.reference)] && tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()] ?\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.type : 'default'\">\r\n\r\n <!-- string control -->\r\n <div *ngSwitchCase=\"'string'\" class=\"form-group\">\r\n <span *ngIf=\"tableField.option.protected\">\r\n <h5 style=\"display: flex; justify-content: space-between;\">\r\n <span\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">\r\n {{\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }}\r\n </span>\r\n <span>\r\n {{\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.value\r\n }}\r\n </span>\r\n </h5>\r\n </span>\r\n <div *ngIf=\"!tableField.option.protected\">\r\n <div\r\n [class]=\"tableField?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">{{\r\n tableField.label?.value ?\r\n tableField.label?.value :\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <input type=\"text\" #inputField=\"ngModel\"\r\n [required]=\"tableField.option.required\"\r\n [email]=\"tableField.option.email\"\r\n [class]=\"tableField.scope?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(tableField?.scope?.style).style\"\r\n [(ngModel)]=\"tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"tableField.scope?.reference\"\r\n [placeholder]=\"tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.placeholder\" />\r\n </div>\r\n <!-- Error Message -->\r\n <div *ngIf=\"inputField?.touched && inputField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(tableField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"inputField?.errors?.['required']\">{{tableField.label?.value\r\n ?\r\n tableField.label?.value :\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"inputField?.errors?.['email']\">{{(tableField.label?.value\r\n ?\r\n tableField.label?.value :\r\n tableitemInfo.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- number control -->\r\n <div *ngSwitchCase=\"'number'\" class=\"form-group\">\r\n <span *ngIf=\"tableField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">\r\n <h5>{{\r\n form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }} {{\r\n form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.value\r\n |\r\n currency:tableField.option.currency :true:\r\n tableField.option?.decimalPoint}}\r\n </h5>\r\n </span>\r\n <span *ngIf=\"!tableField.option.protected\">\r\n <div\r\n [class]=\"tableField?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(tableField?.label?.style).style\"\r\n [class]=\"tableField?.label?.style?.classes[0]\">{{\r\n tableField.label?.value ?\r\n tableField.label?.value :\r\n form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <input type=\"number\"\r\n [class]=\"tableField.scope?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(tableField?.scope?.style).style\"\r\n [(ngModel)]=\"form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"tableField.scope?.reference\"\r\n [placeholder]=\"form.propertySchema[getScope(tableField.scope?.reference)][tableField.scope?.reference.split('/').pop()]?.placeholder\" />\r\n </div>\r\n </span>\r\n </div>\r\n\r\n <!-- button control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button type=\"submit\"\r\n [style]=\"getStyleWithoutClasses(tableField.label.style).style\"\r\n (click)=\"invokeGridBtnClick(tableField.option, tableitemInfo.propertySchema)\">\r\n {{ tableField.label?.caption }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<!-- bottom pagination on grid -->\r\n<div\r\n *ngIf=\"tableitemInfo.data?.length && !tableitemInfo?.layoutSchema?.pagination?.top && tableitemInfo?.layoutSchema?.pagination?.bottom\">\r\n <vl-pagination [itemInfo]=\"tableitemInfo\" [pageSize]=\"pageSize\"\r\n [currentPage]=\"currentPage\" [pages]=\"pages\"\r\n (pageSizeChange)=\"invokeOnPageSizeChange($event)\"\r\n (onGoToPage)=\"invokeGoToPage($event)\">\r\n </vl-pagination>\r\n</div>\r\n", styles: [".grid-filter-row th{padding:8px;vertical-align:middle;background:#fafafa}.grid-filter-cell{min-width:150px}.grid-filter-input-wrapper{display:flex;align-items:stretch;width:100%}.grid-filter-input{width:100%;min-width:0;min-height:34px;padding:5px 8px;border:1px solid #d9d9d9;border-right:0;border-radius:4px 0 0 4px;outline:none}.grid-filter-input:focus{border-color:#1677ff}.grid-filter-operator-button,.grid-filter-clear-button{display:inline-flex;align-items:center;justify-content:center;min-width:36px;min-height:34px;padding:0;cursor:pointer;background:#fff;border:1px solid #d9d9d9}.grid-filter-operator-button{color:#595959;border-radius:0 4px 4px 0}.grid-filter-operator-button:hover,.grid-filter-operator-button--active{color:#1677ff;border-color:#1677ff}.grid-filter-clear-button{margin-left:5px;color:#ff4d4f;border-radius:4px}.grid-filter-clear-button:hover{color:#fff;background:#ff4d4f;border-color:#ff4d4f}.grid-filter-operator-menu{min-width:190px}\n"] }]
3700
4265
  }], ctorParameters: () => [{ type: RequestService }, { type: UtilityService }, { type: NotificationService }, { type: i4$1.OktaAuthStateService }], propDecorators: { errorMessages: [{
3701
4266
  type: Input
3702
4267
  }], environment: [{
@@ -3945,6 +4510,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
3945
4510
  ], selector: 'vl-logs', template: "<div class=\"network-log-wrapper\">\r\n\r\n <div class=\"network-log-table\">\r\n <table>\r\n <thead>\r\n <tr>\r\n <th>#</th>\r\n <th>Time</th>\r\n <th>Method</th>\r\n <th>URL</th>\r\n <th>Status</th>\r\n <th>Duration (ms)</th>\r\n <th>Size (KB)</th>\r\n </tr>\r\n </thead>\r\n\r\n <tbody>\r\n <ng-container *ngFor=\"let log of logs; let i = index\">\r\n\r\n <!-- MAIN ROW -->\r\n <tr class=\"log-row\" (click)=\"toggleRow(i)\">\r\n <td>{{ i + 1 }}</td>\r\n <td>{{ log.time }}</td>\r\n\r\n <td>\r\n <span class=\"method\" [class.get]=\"log.method === 'GET'\" [class.post]=\"log.method === 'POST'\"\r\n [class.put]=\"log.method === 'PUT'\" [class.delete]=\"log.method === 'DELETE'\">\r\n {{ log.method }}\r\n </span>\r\n </td>\r\n\r\n <td class=\"ellipsis\" [title]=\"log.url\">\r\n {{ log.url }}\r\n </td>\r\n\r\n <td [class.error]=\"log.status >= 400\">\r\n {{ log.status }}\r\n </td>\r\n\r\n <td>{{ log.duration }}</td>\r\n\r\n <td>{{ log.size | number:'1.0-2' }}</td>\r\n </tr>\r\n\r\n <!-- DETAILS ROW -->\r\n <tr *ngIf=\"expandedRow === i\" class=\"details-row\">\r\n <td colspan=\"7\">\r\n <div class=\"details-container\">\r\n\r\n <!-- META -->\r\n <div class=\"detail-block\">\r\n <h4>Meta</h4>\r\n <p><strong>Date:</strong> {{ log.date }}</p>\r\n <p><strong>Type:</strong> {{ log.type }}</p>\r\n </div>\r\n\r\n <!-- REQUEST -->\r\n <div class=\"detail-block\">\r\n <h4>Request</h4>\r\n <p><strong>Headers</strong></p>\r\n <pre>{{ log.requestHeaders | json }}</pre>\r\n\r\n <p><strong>Body</strong></p>\r\n <pre>{{ log.requestBody | json }}</pre>\r\n </div>\r\n\r\n <!-- RESPONSE -->\r\n <div class=\"detail-block\">\r\n <h4>Response</h4>\r\n <pre>{{ log.responseBody | json }}</pre>\r\n </div>\r\n\r\n <!-- PERFORMANCE -->\r\n <div class=\"detail-block\">\r\n <h4>Performance</h4>\r\n <p><strong>Duration:</strong> {{ log.duration }} ms</p>\r\n <p><strong>Size:</strong> {{ log.size | number:'1.0-2' }} KB</p>\r\n </div>\r\n\r\n </div>\r\n </td>\r\n </tr>\r\n\r\n </ng-container>\r\n </tbody>\r\n </table>\r\n </div>\r\n\r\n</div>", styles: [".network-log-wrapper{padding:12px}.network-log-table{width:100%;overflow-x:auto}table{width:100%;border-collapse:collapse;font-size:13px;min-width:900px}thead th{background:#06b6d4;color:#fff;padding:8px;text-align:left;position:sticky;top:0;z-index:2}tbody td{padding:8px;border-bottom:1px solid #e5e7eb}.log-row{cursor:pointer}.log-row:hover{background:#f1f5f9}.details-row{background:#f8fafc}.method{padding:2px 8px;border-radius:4px;font-weight:600;font-size:11px;color:#fff}.method.get{background:#16a34a}.method.post{background:#2563eb}.method.put{background:#d97706}.method.delete{background:#dc2626}td.error{color:#dc2626;font-weight:700}.ellipsis{max-width:320px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.details-container{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px}.detail-block{background:#fff;border:1px solid #e5e7eb;border-radius:6px;padding:10px}.detail-block h4{margin:0 0 6px;font-size:14px;color:#0f172a}pre{background:#020617;color:#e5e7eb;padding:8px;border-radius:6px;max-height:260px;overflow:auto;font-size:12px;white-space:pre-wrap;word-break:break-word}@media(max-width:768px){table{min-width:100%;font-size:12px}.ellipsis{max-width:140px}.details-container{grid-template-columns:1fr}pre{max-height:200px}}\n"] }]
3946
4511
  }], ctorParameters: () => [{ type: NetworkLogService }] });
3947
4512
 
4513
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07
4514
+ // - new: keyboardTypeFor. A text field took the toolkit keyboard only with option.keyboardType,
4515
+ // which no layout sets; the attribute type now decides
3948
4516
  class Textbox {
3949
4517
  cdr;
3950
4518
  utilityService;
@@ -3981,23 +4549,55 @@ class Textbox {
3981
4549
  const { classes, ...styleWithoutClasses } = styleObject;
3982
4550
  return { classes: classes && classes.length > 0 ? classes[0] : '', style: styleWithoutClasses };
3983
4551
  }
4552
+ /** The toolkit keyboard for this field, from option.keyboardType or the attribute type. */
4553
+ keyboardTypeFor(formField) {
4554
+ const reference = formField?.scope?.reference;
4555
+ const property = reference
4556
+ ? this.modalItemInfo?.propertySchema?.[this.getScope(reference)]?.[reference.split('/').pop()]
4557
+ : null;
4558
+ return resolveCustomKeyboardType(formField?.option, property);
4559
+ }
3984
4560
  getPlaceholder(formField) {
3985
4561
  let placeholder = this.modalItemInfo.propertySchema[this.getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.placeholder;
3986
4562
  const translation = this.selectedLanguageInfo?.selectedLanguage;
3987
4563
  return translation && translation[placeholder] ? translation[placeholder] : placeholder;
3988
4564
  }
3989
4565
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: Textbox, deps: [{ token: i0.ChangeDetectorRef }, { token: UtilityService }], target: i0.ɵɵFactoryTarget.Component });
3990
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.2", type: Textbox, isStandalone: true, selector: "vl-textbox", inputs: { formField: "formField", modalItemInfo: "modalItemInfo" }, ngImport: i0, template: "<input pInputText type=\"text\" #inputField=\"ngModel\" [vlCustomKeyboard]=\"formField?.option?.keyboardType\"\r\n [required]=\"formField?.option?.required\" [email]=\"formField?.option?.email\" [disabled]=\"formField?.option?.disabled\"\r\n [readonly]=\"\r\n modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope.reference.split('/').pop()\r\n ]?.readonly\r\n \" [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\" [class]=\"\r\n formField?.scope?.style?.classes\r\n ? formField.scope.style.classes[0]\r\n : ''\r\n \" [(ngModel)]=\"\r\n modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\" [placeholder]=\"getPlaceholder(formField)\" />", styles: [""], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.EmailValidator, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: ["email"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i3$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "directive", type: CustomKeyboardDirective, selector: "[vlCustomKeyboard]", inputs: ["vlCustomKeyboard"] }] });
4566
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.2", type: Textbox, isStandalone: true, selector: "vl-textbox", inputs: { formField: "formField", modalItemInfo: "modalItemInfo" }, ngImport: i0, template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\r\n - change: the toolkit keyboard follows the attribute type (keyboardTypeFor) -->\r\n<input pInputText type=\"text\" #inputField=\"ngModel\" [vlCustomKeyboard]=\"keyboardTypeFor(formField)\"\r\n [required]=\"formField?.option?.required\" [email]=\"formField?.option?.email\" [disabled]=\"formField?.option?.disabled\"\r\n [readonly]=\"\r\n modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope.reference.split('/').pop()\r\n ]?.readonly\r\n \" [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\" [class]=\"\r\n formField?.scope?.style?.classes\r\n ? formField.scope.style.classes[0]\r\n : ''\r\n \" [(ngModel)]=\"\r\n modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\" [placeholder]=\"getPlaceholder(formField)\" />", styles: [""], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.EmailValidator, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: ["email"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i3$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "directive", type: CustomKeyboardDirective, selector: "[vlCustomKeyboard]", inputs: ["vlCustomKeyboard"] }] });
3991
4567
  }
3992
4568
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: Textbox, decorators: [{
3993
4569
  type: Component,
3994
- args: [{ selector: 'vl-textbox', standalone: true, imports: [FormsModule, InputTextModule, CustomKeyboardDirective], template: "<input pInputText type=\"text\" #inputField=\"ngModel\" [vlCustomKeyboard]=\"formField?.option?.keyboardType\"\r\n [required]=\"formField?.option?.required\" [email]=\"formField?.option?.email\" [disabled]=\"formField?.option?.disabled\"\r\n [readonly]=\"\r\n modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope.reference.split('/').pop()\r\n ]?.readonly\r\n \" [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\" [class]=\"\r\n formField?.scope?.style?.classes\r\n ? formField.scope.style.classes[0]\r\n : ''\r\n \" [(ngModel)]=\"\r\n modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\" [placeholder]=\"getPlaceholder(formField)\" />" }]
4570
+ args: [{ selector: 'vl-textbox', standalone: true, imports: [FormsModule, InputTextModule, CustomKeyboardDirective], template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\r\n - change: the toolkit keyboard follows the attribute type (keyboardTypeFor) -->\r\n<input pInputText type=\"text\" #inputField=\"ngModel\" [vlCustomKeyboard]=\"keyboardTypeFor(formField)\"\r\n [required]=\"formField?.option?.required\" [email]=\"formField?.option?.email\" [disabled]=\"formField?.option?.disabled\"\r\n [readonly]=\"\r\n modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope.reference.split('/').pop()\r\n ]?.readonly\r\n \" [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\" [class]=\"\r\n formField?.scope?.style?.classes\r\n ? formField.scope.style.classes[0]\r\n : ''\r\n \" [(ngModel)]=\"\r\n modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope.reference.split('/').pop()\r\n ].value\r\n \" [name]=\"formField.scope?.reference\" [placeholder]=\"getPlaceholder(formField)\" />" }]
3995
4571
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: UtilityService }], propDecorators: { formField: [{
3996
4572
  type: Input
3997
4573
  }], modalItemInfo: [{
3998
4574
  type: Input
3999
4575
  }] } });
4000
4576
 
4577
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07
4578
+ // - change: the date and time icons open the picker with showOverlay() and markForCheck(). PrimeNG 21
4579
+ // DatePicker has no show(); the call threw, the catch swallowed it, and the overlay set in finally
4580
+ // stayed invisible (the picker is OnPush) until the user tapped the field - which then showed the
4581
+ // calendar together with a keyboard: the "two keyboards" seen on iOS
4582
+ // - change: a second picker icon is no longer ignored while another picker is open. The open picker
4583
+ // is closed without focusing its field, and its late onClose no longer cancels the new one
4584
+ // - change: after an icon tap a click that no longer hits the icon is dropped. Closing the keyboard
4585
+ // resizes the dialog under the finger, and the click opened the autocomplete moved there
4586
+ // - change: an icon tap releases the field that has the focus. Its preventDefault kept an
4587
+ // autocomplete focused, so its list and the system keyboard stayed open beside the calendar
4588
+ // - new: keyboardTypeFor, so every field gets the toolkit keyboard of its attribute type
4589
+ // - new: on the devices the date picker docks at the bottom, where the keyboard would be, and the
4590
+ // dialog ends above it; the small time picker stays at its field. Tapping into a field with an
4591
+ // open picker opens the keyboard at once
4592
+ // - change: after a selection in a picker the field gives up the focus PrimeNG returns to it; it
4593
+ // showed a focus frame without any keyboard
4594
+ // - change: the docked date picker has no open/close animation; it made the panel jump up to its
4595
+ // field while closing
4596
+ // - new: the field of an open picker keeps a focus frame (picker-field--active), since the field
4597
+ // itself gives the focus away
4598
+ // - change: the picker logic moved to DateTimePickerService, shared with form, card and grid
4599
+ // - new: ngOnDestroy closes a picker that is still open, so closing the dialog cannot leave the
4600
+ // keyboard service blocked and the next dialog cut short
4001
4601
  // Shuaib 2026-09-24
4002
4602
  // - new: renderFieldType maps legacy checkbox fields to boolean.
4003
4603
  class ModelComponent {
@@ -4005,16 +4605,23 @@ class ModelComponent {
4005
4605
  dialogRef;
4006
4606
  dialogConfig;
4007
4607
  utilityService;
4008
- customKeyboardService;
4009
4608
  /** Keep existing checkbox data objects usable; new objects use boolean. */
4010
4609
  renderFieldType(type) {
4011
4610
  return type === 'checkbox' ? 'boolean' : type;
4012
4611
  }
4013
- activePickerRequest = null;
4612
+ /** Date and time picker behaviour shared by all containers. */
4613
+ picker = inject(DateTimePickerService);
4614
+ host = inject((ElementRef));
4615
+ /** The toolkit keyboard for a field, from option.keyboardType or the attribute type. */
4616
+ keyboardTypeFor(formField) {
4617
+ const reference = formField?.scope?.reference;
4618
+ const property = reference
4619
+ ? this.modalItemInfo?.propertySchema?.[this.getScope(reference)]?.[reference.split('/').pop()]
4620
+ : null;
4621
+ return resolveCustomKeyboardType(formField?.option, property);
4622
+ }
4014
4623
  matchData = [];
4015
4624
  date2;
4016
- pickerOpening = false;
4017
- lockedPickerInput = null;
4018
4625
  errorMessages = {};
4019
4626
  modalItemInfo = {};
4020
4627
  selectedLanguageInfo = {};
@@ -4023,12 +4630,11 @@ class ModelComponent {
4023
4630
  copyLayoutSchema = {};
4024
4631
  isLoading = false;
4025
4632
  projectLanguage = {};
4026
- constructor(cdr, dialogRef, dialogConfig, utilityService, customKeyboardService) {
4633
+ constructor(cdr, dialogRef, dialogConfig, utilityService) {
4027
4634
  this.cdr = cdr;
4028
4635
  this.dialogRef = dialogRef;
4029
4636
  this.dialogConfig = dialogConfig;
4030
4637
  this.utilityService = utilityService;
4031
- this.customKeyboardService = customKeyboardService;
4032
4638
  }
4033
4639
  ngAfterViewInit() {
4034
4640
  this.projectLanguage = this.utilityService.currentProjectlanguage$.subscribe((uiLang) => {
@@ -4042,6 +4648,14 @@ class ModelComponent {
4042
4648
  });
4043
4649
  });
4044
4650
  }
4651
+ ngOnDestroy() {
4652
+ /*
4653
+ * The dialog can be closed with a picker still open. Without this the keyboard service keeps
4654
+ * "picker open" and the reserved bottom area: no toolkit keyboard opens any more and the next
4655
+ * dialog is cut short above an empty strip.
4656
+ */
4657
+ this.picker.closeIfWithin(this.host.nativeElement);
4658
+ }
4045
4659
  ngOnChanges(changes) {
4046
4660
  if (!changes['modalItemInfo'] &&
4047
4661
  !changes['selectedLanguageInfo'] &&
@@ -4337,7 +4951,7 @@ class ModelComponent {
4337
4951
  };
4338
4952
  }
4339
4953
  onTimePanelShow(formField) {
4340
- this.onPickerShown();
4954
+ this.picker.onShown();
4341
4955
  const reference = formField?.scope?.reference;
4342
4956
  const scope = this.getScope(reference);
4343
4957
  const fieldName = reference?.split('/')?.pop();
@@ -4561,203 +5175,8 @@ class ModelComponent {
4561
5175
  action: 'cancel'
4562
5176
  });
4563
5177
  }
4564
- onPickerShown() {
4565
- this.pickerOpening = true;
4566
- }
4567
- onPickerClose() {
4568
- console.log('onPickerClose: ');
4569
- this.activePickerRequest = null;
4570
- this.pickerOpening = false;
4571
- this.unlockPickerInput();
4572
- this.customKeyboardService
4573
- .endPickerOverlay();
4574
- }
4575
- async openPickerFromIcon(event, picker) {
4576
- event.preventDefault();
4577
- event.stopPropagation();
4578
- event.stopImmediatePropagation();
4579
- console.log("openPickerFromIcon");
4580
- if (this.pickerOpening) {
4581
- return;
4582
- }
4583
- const button = event.currentTarget;
4584
- console.log('button: ', button);
4585
- const pickerInput = button
4586
- .closest('.picker-field')
4587
- ?.querySelector('input');
4588
- console.log('pickerInput: ', pickerInput);
4589
- if (!pickerInput) {
4590
- return;
4591
- }
4592
- const request = Symbol('picker-open');
4593
- this.activePickerRequest = request;
4594
- this.pickerOpening = true;
4595
- this.lockPickerInput(pickerInput);
4596
- pickerInput.blur();
4597
- this.customKeyboardService.beginPickerOverlay();
4598
- let keyboardDidHideListener;
4599
- let resolveKeyboardHidden;
4600
- const keyboardHidden = new Promise(resolve => {
4601
- resolveKeyboardHidden = resolve;
4602
- });
4603
- try {
4604
- if (Capacitor.isNativePlatform()) {
4605
- keyboardDidHideListener = await Keyboard.addListener('keyboardDidHide', () => resolveKeyboardHidden?.());
4606
- await Keyboard.hide().catch(() => {
4607
- // The native keyboard may already be hidden.
4608
- });
4609
- await Promise.race([
4610
- keyboardHidden,
4611
- new Promise(resolve => {
4612
- window.setTimeout(resolve, 500);
4613
- })
4614
- ]);
4615
- await new Promise(resolve => {
4616
- window.requestAnimationFrame(() => {
4617
- window.requestAnimationFrame(() => resolve());
4618
- });
4619
- });
4620
- }
4621
- // The user may have tapped the input while Keyboard.hide() was pending.
4622
- if (this.activePickerRequest !== request) {
4623
- return;
4624
- }
4625
- if (Capacitor.isNativePlatform()) {
4626
- window.dispatchEvent(new Event('resize'));
4627
- }
4628
- picker.show();
4629
- }
4630
- catch (error) {
4631
- if (this.activePickerRequest === request) {
4632
- this.activePickerRequest = null;
4633
- this.pickerOpening = false;
4634
- this.unlockPickerInput();
4635
- this.customKeyboardService.endPickerOverlay();
4636
- }
4637
- console.warn('Unable to open date/time picker:', error);
4638
- }
4639
- finally {
4640
- await keyboardDidHideListener?.remove().catch(() => {
4641
- // Listener may already be removed.
4642
- });
4643
- picker.showOverlay();
4644
- this.cdr.detectChanges();
4645
- }
4646
- }
4647
- // onPickerInputFocus(
4648
- // event: any,
4649
- // picker: any
4650
- // ): void {
4651
- // const input = (
4652
- // event?.target ||
4653
- // event?.originalEvent?.target
4654
- // ) as HTMLInputElement;
4655
- // if (
4656
- // !input ||
4657
- // input.tagName !== 'INPUT'
4658
- // ) {
4659
- // return;
4660
- // }
4661
- // this.configureDateTimeInput(input);
4662
- // /*
4663
- // * Fallback for focus caused without pointerdown.
4664
- // */
4665
- // if (this.pickerOpening) {
4666
- // this.unlockPickerInput();
4667
- // this.pickerOpening = false;
4668
- // try {
4669
- // picker.hide();
4670
- // } catch {
4671
- // // Picker may already be closing.
4672
- // }
4673
- // this.customKeyboardService
4674
- // .endPickerOverlay();
4675
- // }
4676
- // }..
4677
- onPickerInputFocus(event) {
4678
- const input = (event?.target ?? event?.originalEvent?.target);
4679
- console.log('onPickerInputFocus: ', input);
4680
- if (input?.tagName === 'INPUT' &&
4681
- this.lockedPickerInput?.input !== input) {
4682
- this.configureDateTimeInput(input);
4683
- }
4684
- }
4685
- forceNumericKeyboard(event) {
4686
- const input = (event?.target ||
4687
- event?.originalEvent?.target);
4688
- if (!input ||
4689
- input.tagName !== 'INPUT') {
4690
- return;
4691
- }
4692
- this.configureDateTimeInput(input);
4693
- }
4694
- configureDateTimeInput(input) {
4695
- input.setAttribute('inputmode', 'numeric');
4696
- input.setAttribute('pattern', '[0-9:]*');
4697
- input.setAttribute('autocomplete', 'off');
4698
- }
4699
- lockPickerInput(input) {
4700
- this.unlockPickerInput();
4701
- this.lockedPickerInput = {
4702
- input,
4703
- originalReadOnly: input.readOnly,
4704
- originalInputMode: input.getAttribute('inputmode')
4705
- };
4706
- /*
4707
- * Prevent PrimeNG focus changes from reopening
4708
- * the native keyboard.
4709
- */
4710
- input.readOnly = true;
4711
- input.setAttribute('inputmode', 'none');
4712
- }
4713
- unlockPickerInput() {
4714
- const locked = this.lockedPickerInput;
4715
- if (!locked) {
4716
- return;
4717
- }
4718
- locked.input.readOnly =
4719
- locked.originalReadOnly;
4720
- if (locked.originalInputMode === null) {
4721
- locked.input.removeAttribute('inputmode');
4722
- }
4723
- else {
4724
- locked.input.setAttribute('inputmode', locked.originalInputMode);
4725
- }
4726
- this.lockedPickerInput = null;
4727
- }
4728
- blockPickerIconClick(event) {
4729
- event.preventDefault();
4730
- event.stopPropagation();
4731
- event.stopImmediatePropagation();
4732
- }
4733
- onPickerContainerPointerDown(event, picker) {
4734
- const target = event.target;
4735
- console.log('onPickerContainerPointerDown: ', target);
4736
- if (!(target instanceof HTMLInputElement)) {
4737
- return;
4738
- }
4739
- this.configureDateTimeInput(target);
4740
- console.log('pickerOpening: ', this.pickerOpening);
4741
- if (!this.pickerOpening) {
4742
- return;
4743
- }
4744
- /*
4745
- * User tapped the input while the picker was open.
4746
- */
4747
- this.activePickerRequest = null;
4748
- this.unlockPickerInput();
4749
- this.pickerOpening = false;
4750
- try {
4751
- picker.hide();
4752
- picker.hideOverlay();
4753
- }
4754
- catch {
4755
- // Picker may already be closing.
4756
- }
4757
- this.customKeyboardService.endPickerOverlay();
4758
- }
4759
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: ModelComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1$3.DynamicDialogRef }, { token: i1$3.DynamicDialogConfig }, { token: UtilityService }, { token: CustomKeyboardService }], target: i0.ɵɵFactoryTarget.Component });
4760
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.2", type: ModelComponent, isStandalone: true, selector: "vl-model", inputs: { errorMessages: "errorMessages", modalItemInfo: "modalItemInfo", selectedLanguageInfo: "selectedLanguageInfo", userInfo: "userInfo", setFormDataHandler: "setFormDataHandler" }, usesOnChanges: true, ngImport: i0, template: "<!-- Shuaib 2026-09-24\r\n - change: the model field switch uses renderFieldType so legacy checkbox fields render as boolean. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - new: a @contract comment on the widest type coverage in the toolkit, and one on the credit\r\n card case recording that the rename reached this file first -->\r\n<!-- Loader Spinner -->\r\n<div *ngIf=\"isLoading\" class=\"loader-overlay\">\r\n <div class=\"spinner\"></div>\r\n</div>\r\n<div [style]=\"getStyleWithoutClasses(modalItemInfo?.layoutSchema?.style).style\"\r\n [class]=\"modalItemInfo?.layoutSchema?.style?.classes[0]\">\r\n <form #dynamicForm=\"ngForm\"\r\n *ngIf=\"modalItemInfo?.layoutSchema?.formSections?.length > 0\"\r\n (submit)=\"$event.preventDefault()\">\r\n <div\r\n *ngFor=\"let formSection of modalItemInfo?.layoutSchema?.formSections\">\r\n <div *ngIf=\"formSection?.label?.caption\"\r\n [class]=\"formSection?.label?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(formSection?.label?.style).style\">\r\n {{formSection.label.caption}}\r\n </div>\r\n <div *ngIf=\"formSection?.formFields?.length > 0\"\r\n [class]=\"formSection?.style?.classes?.[0] || ''\"\r\n [ngStyle]=\"getSectionBodyStyle(formSection)\">\r\n <div *ngFor=\"let formField of formSection.formFields\"\r\n [ngStyle]=\"getFieldWrapperStyle(formField)\"\r\n [class]=\"formField?.style?.classes || ''\">\r\n\r\n <!-- @contract decision: the widest type coverage in the toolkit\r\n reality: every field type except the grid-only quantity renders here.\r\n When another container drops a type, this is the way out.\r\n seen: measured 2026-09-19. Note the file is model.component.html while\r\n the container is called modal in layout files - that costs a\r\n search every time -->\r\n <!-- @contract compatibility: checkbox is rendered as boolean for existing projects. -->\r\n <div [ngSwitch]=\"renderFieldType(\r\n formField?.scope?.reference &&\r\n modalItemInfo?.propertySchema?.[\r\n getScope(formField.scope.reference)\r\n ]\r\n ? modalItemInfo.propertySchema[\r\n getScope(formField.scope.reference)\r\n ]?.[\r\n formField.scope.reference.split('/').pop()\r\n ]?.type\r\n : 'default'\r\n )\">\r\n <!-- image control -->\r\n <div *ngSwitchCase=\"'image'\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\">\r\n <img [src]=\"formField.option.src\" [alt]=\"'img'\" />\r\n </div>\r\n\r\n <!-- credit card control -->\r\n <!-- @contract decision: the credit card type is spelled creditCard\r\n reality: this file was renamed on 2026-09-15, form.component.html and\r\n utility.service.ts followed on 2026-09-19. carditCard is read\r\n nowhere any more.\r\n failure: a data object still declaring carditCard renders empty. -->\r\n <div *ngSwitchCase=\"'creditCard'\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [class]=\"formField.style?.classes[0]\">\r\n <div id=\"card-element\">\r\n <!-- a Stripe element will be inserted here -->\r\n </div>\r\n <!-- Used to display form errors -->\r\n <div id=\"card-errors\"\r\n class=\"displayError.textContent!=='': 'alert alert-danger mt-1'\">\r\n </div>\r\n </div>\r\n\r\n <!-- string control -->\r\n <div *ngSwitchCase=\"'string'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-label>\r\n <vl-textbox [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-textbox>\r\n </div>\r\n <!-- Error Message -->\r\n <!-- <div *ngIf=\"inputField?.touched && inputField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\" *ngIf=\"inputField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages, \"required\")}}</p>\r\n <p class=\"text-danger\" *ngIf=\"inputField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div> -->\r\n </div>\r\n </div>\r\n\r\n <!-- number control -->\r\n <div *ngSwitchCase=\"'number'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <h5>{{\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }} {{\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.value\r\n |\r\n currency:formField.option.currency :true:\r\n formField.option?.decimalPoint}}\r\n </h5>\r\n </span>\r\n <span *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{\r\n formField.label?.value\r\n ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <input type=\"number\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\r\n [vlCustomKeyboard]=\"formField?.option?.keyboardType\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"formField.scope?.reference\"\r\n [placeholder]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.placeholder\" />\r\n </div>\r\n </span>\r\n </div>\r\n\r\n <!-- DateRange control -->\r\n <div *ngSwitchCase=\"'dateRange'\"\r\n class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{\r\n formField.label?.value\r\n ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <nz-range-picker #dateRangeField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"></nz-range-picker>\r\n </div>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"dateRangeField?.touched && dateRangeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['required']\">\r\n {{formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"form-group mb-3\">\r\n <span\r\n *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string\r\n [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n {{ getLabel(formField) }}\r\n </label>\r\n\r\n <span>\r\n <div class=\"picker-field\">\r\n <p-datepicker\r\n #datePicker\r\n #dateField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n [showIcon]=\"false\"\r\n [showOnFocus]=\"false\"\r\n appendTo=\"body\"\r\n [autoZIndex]=\"true\"\r\n [baseZIndex]=\"10000\"\r\n panelStyleClass=\"model-datepicker-panel\"\r\n (pointerdown)=\"onPickerContainerPointerDown($event, datePicker)\"\r\n (onFocus)=\"onPickerInputFocus($event)\"\r\n (onShow)=\"onPickerShown()\"\r\n (onClose)=\"onPickerClose()\"\r\n (ngModelChange)=\"dateChangeEvent(formField, $event)\"\r\n (onInput)=\"onDateTyping($event)\"\r\n [dateFormat]=\"\r\n modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ]?.format || 'yy-mm-dd'\r\n \"\r\n [(ngModel)]=\"\r\n modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"formField?.scope?.style?.classes?.[0] || ''\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\">\r\n </p-datepicker>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"picker-field__icon\"\r\n aria-label=\"Open calendar\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n (pointerdown)=\"openPickerFromIcon($event, datePicker)\"\r\n (click)=\"blockPickerIconClick($event)\">\r\n <i class=\"pi pi-calendar\"></i>\r\n </button>\r\n </div>\r\n\r\n <div\r\n *ngIf=\"dateField?.touched && dateField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['required']\">\r\n {{ setErroMessage(formField,\r\n errorMessages, 'required') }}\r\n </p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['email']\">\r\n {{\r\n (formField.label?.value\r\n ? formField.label?.value\r\n : modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption)\r\n + ' ' + errorMessages.pattern\r\n }}\r\n </p>\r\n </div>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Time control -->\r\n <div *ngSwitchCase=\"'time'\" class=\"form-group mb-3\">\r\n <span\r\n *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string\r\n [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n {{ getLabel(formField) }}\r\n </label>\r\n\r\n <span>\r\n <div class=\"picker-field\">\r\n <p-datepicker\r\n #timePicker\r\n #timeField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [timeOnly]=\"true\"\r\n hourFormat=\"24\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n [showIcon]=\"false\"\r\n [showOnFocus]=\"false\"\r\n appendTo=\"body\"\r\n [autoZIndex]=\"true\"\r\n [baseZIndex]=\"10000\"\r\n panelStyleClass=\"model-datepicker-panel\"\r\n (pointerdown)=\"onPickerContainerPointerDown($event, timePicker)\"\r\n (onFocus)=\"onPickerInputFocus($event)\"\r\n (onShow)=\"onTimePanelShow(formField)\"\r\n (onClose)=\"onPickerClose()\"\r\n (onInput)=\"onTimeTyping($event)\"\r\n (ngModelChange)=\"timeChangeEvent(formField, $event)\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [(ngModel)]=\"\r\n modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"formField?.scope?.style?.classes?.[0] || ''\">\r\n </p-datepicker>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"picker-field__icon\"\r\n aria-label=\"Open time picker\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n (pointerdown)=\"openPickerFromIcon($event, timePicker)\"\r\n (click)=\"blockPickerIconClick($event)\">\r\n <i class=\"pi pi-clock\"></i>\r\n </button>\r\n </div>\r\n\r\n <div\r\n *ngIf=\"timeField?.touched && timeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['required']\">\r\n {{ setErroMessage(formField,\r\n errorMessages, 'required') }}\r\n </p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['email']\">\r\n {{\r\n (formField.label?.value\r\n ? formField.label?.value\r\n : modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption)\r\n + ' ' + errorMessages.pattern\r\n }}\r\n </p>\r\n </div>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Autocomplete control -->\r\n <div *ngSwitchCase=\"'autocomplete'\"\r\n class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-label>\r\n\r\n <span>\r\n <p-autocomplete\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [class]=\"formField?.scope?.style?.classes ? formField?.scope?.style?.classes[0] : ''\"\r\n [suggestions]=\"matchData\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"formField?.option?.autocomplete?.response?.label ? formField.option.autocomplete.response.label : 'label'\"\r\n [placeholder]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.placeholder\"\r\n (completeMethod)=\"autocompChangeEvent($event, formField)\"\r\n (ngModelChange)=\"autocompFocusOutEvent($event, formField)\"\r\n [vlCustomKeyboard]=\"formField?.option?.keyboardType\">\r\n </p-autocomplete>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"autocompleteField?.touched && autocompleteField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['required']\">\r\n {{setErroMessage(formField,\r\n errorMessages, \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' +\r\n errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- checkBox control -->\r\n <div *ngSwitchCase=\"'boolean'\" class=\"form-group mb-3\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0] : ''\">\r\n <span *ngIf=\"formField.option?.postfixCaption\">\r\n <label class=\"form-check-label\"><input\r\n type=\"checkbox\" class=\"form-check-input\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"formField.scope?.reference\"\r\n (ngModelChange)=\"invokeSetformData()\" />{{\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n </span>\r\n <div *ngIf=\"!formField.option?.postfixCaption\"\r\n [class]=\"formField?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label class=\"form-check-label\">{{\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}\r\n <input type=\"checkbox\"\r\n class=\"form-check-input\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"formField.scope?.reference\" /></label>\r\n </div>\r\n </div>\r\n\r\n <!-- dropdown control -->\r\n <div *ngSwitchCase=\"'dropdown'\" class=\"form-group mb-3\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0] : ''\">\r\n <span *ngIf=\"formField.option?.postfixCaption\">\r\n <select id=\"dynamic-dropdown\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\r\n #dropdownField=\"ngModel\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\">\r\n <option\r\n *ngFor=\"let item of modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.data\"\r\n [value]=\"formField?.option?.optionValue ? formField.option.optionValue : 'value'\">\r\n {{ formField?.option?.optionLabel ?\r\n formField.option.optionLabel :\r\n 'label'}}</option>\r\n </select>\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\">{{modalItemInfo.propertySchema[getScope(formField.scope)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n </span>\r\n <div *ngIf=\"!formField.option?.postfixCaption\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\">{{modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n\r\n <nz-select nzShowSearch nzAllowClear\r\n nzPlaceHolder=\"Select\"\r\n #dropdownField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\">\r\n <nz-option\r\n *ngFor=\"let item of modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.data\"\r\n [nzLabel]=\"item[formField?.option?.optionLabel ? formField.option.optionLabel : 'label']\"\r\n [nzValue]=\"item[formField?.option?.optionValue ? formField.option.optionValue : 'value']\"></nz-option>\r\n </nz-select>\r\n </div>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"dropdownField?.touched && dropdownField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dropdownField?.errors?.['required']\">\r\n {{formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dropdownField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- button control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button type=\"button\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField.style).style\"\r\n (click)=\"invokeFormBtnClick(formField.option, modalItemInfo.propertySchema, dynamicForm)\"\r\n class=\"btn btn-primary btn-block\">\r\n {{ getButtonCaption(formField) }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </form>\r\n</div>\r\n", styles: [":host ::ng-deep .custom-datepicker .p-datepicker-dropdown{height:100%;border-radius:4px}:host ::ng-deep .custom-datepicker .p-datepicker-input{border-radius:4px}::ng-deep .p-autocomplete-list{background-color:#fff}:host ::ng-deep .custom-autocomplete{display:flex;width:100%}:host ::ng-deep .custom-autocomplete .p-autocomplete-input{flex:1;width:100%;min-width:0;border:none!important;outline:none!important;box-shadow:none!important;background:transparent!important}:host ::ng-deep .custom-autocomplete .p-autocomplete-dropdown{height:100%;border-radius:4px}:host ::ng-deep .custom-autocomplete .p-autocomplete-input:focus,:host ::ng-deep .custom-autocomplete .p-autocomplete-input:focus-visible,:host ::ng-deep .custom-autocomplete .p-inputtext:enabled:focus{border:none!important;outline:none!important;box-shadow:none!important}.responsive-modal-grid{width:100%;min-width:0;box-sizing:border-box}@media screen and (max-width:767px){.responsive-modal-grid{grid-template-columns:repeat(12,minmax(0,1fr))!important;gap:10px!important}.responsive-modal-field{grid-column:1 / -1!important;width:100%!important;max-width:none!important;min-width:0!important}.responsive-modal-action:nth-last-child(2){grid-column:1 / span 6!important}.responsive-modal-action:last-child{grid-column:7 / span 6!important}.responsive-modal-action{width:100%!important;max-width:none!important;min-width:0!important;justify-self:stretch!important;box-sizing:border-box!important}.responsive-modal-action button{display:block!important;width:100%!important;max-width:none!important;min-width:0!important;margin:0!important;box-sizing:border-box!important}}:host{--modal-field-focus-color: var(--p-primary-color, #10b981);--modal-field-focus-shadow: 0 0 0 2px rgba(16, 185, 129, .16)}:host ::ng-deep input:not([type=checkbox]):not(:disabled):focus,:host ::ng-deep textarea:not(:disabled):focus{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .custom-autocomplete:focus-within{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .custom-autocomplete:focus-within .p-autocomplete-input{border:none!important;outline:none!important;box-shadow:none!important}:host ::ng-deep select:not(:disabled):focus{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .ant-select-focused:not(.ant-select-disabled) .ant-select-selector,:host ::ng-deep nz-select:not(.ant-select-disabled):focus-within .ant-select-selector{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .p-select:not(.p-disabled).p-focus,:host ::ng-deep .p-select:not(.p-disabled):focus-within,:host ::ng-deep .p-dropdown:not(.p-disabled).p-focus,:host ::ng-deep .p-dropdown:not(.p-disabled):focus-within{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .custom-datepicker:focus-within .p-datepicker-input{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep input:disabled,:host ::ng-deep textarea:disabled,:host ::ng-deep select:disabled,:host ::ng-deep .p-disabled,:host ::ng-deep .ant-select-disabled .ant-select-selector{box-shadow:none!important}.picker-field{position:relative}.picker-field__icon{position:absolute;right:0;top:0;height:100%;width:2.5rem;border:0;background:transparent;cursor:pointer}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i5.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i5.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.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: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: NzSelectModule }, { kind: "component", type: i3.NzOptionComponent, selector: "nz-option", inputs: ["nzTitle", "nzLabel", "nzValue", "nzKey", "nzDisabled", "nzHide", "nzCustomContent"], exportAs: ["nzOption"] }, { kind: "component", type: i3.NzSelectComponent, selector: "nz-select", inputs: ["nzId", "nzSize", "nzStatus", "nzVariant", "nzOptionHeightPx", "nzOptionOverflowSize", "nzDropdownClassName", "nzDropdownMatchSelectWidth", "nzDropdownStyle", "nzNotFoundContent", "nzPlaceHolder", "nzPlacement", "nzMaxTagCount", "nzDropdownRender", "nzCustomTemplate", "nzPrefix", "nzSuffixIcon", "nzClearIcon", "nzRemoveIcon", "nzMenuItemSelectedIcon", "nzTokenSeparators", "nzMaxTagPlaceholder", "nzMaxMultipleCount", "nzMode", "nzFilterOption", "compareWith", "nzAllowClear", "nzShowSearch", "nzLoading", "nzAutoFocus", "nzAutoClearSearchValue", "nzServerSearch", "nzDisabled", "nzOpen", "nzSelectOnTab", "nzBackdrop", "nzOptions", "nzShowArrow"], outputs: ["nzOnSearch", "nzScrollToBottom", "nzOpenChange", "nzBlur", "nzFocus", "nzOnClear"], exportAs: ["nzSelect"] }, { kind: "ngmodule", type: NzDatePickerModule }, { kind: "component", type: i4.NzDatePickerComponent, selector: "nz-date-picker,nz-week-picker,nz-month-picker,nz-quarter-picker,nz-year-picker,nz-range-picker", inputs: ["nzInline", "nzAllowClear", "nzAutoFocus", "nzDisabled", "nzInputReadOnly", "nzOpen", "nzDisabledDate", "nzLocale", "nzPlaceHolder", "nzPopupStyle", "nzDropdownClassName", "nzSize", "nzStatus", "nzFormat", "nzVariant", "nzDateRender", "nzDisabledTime", "nzRenderExtraFooter", "nzShowToday", "nzMode", "nzShowNow", "nzRanges", "nzDefaultPickerValue", "nzSeparator", "nzSuffixIcon", "nzBackdrop", "nzId", "nzPlacement", "nzShowWeekNumber", "nzShowTime"], outputs: ["nzOnPanelChange", "nzOnCalendarChange", "nzOnOk", "nzOnOpenChange"], exportAs: ["nzDatePicker"] }, { kind: "directive", type: i4.NzRangePickerComponent, selector: "nz-range-picker", exportAs: ["nzRangePicker"] }, { kind: "ngmodule", type: DatePickerModule }, { kind: "component", type: i12.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: AutoCompleteModule }, { kind: "component", type: i18.AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "minQueryLength", "delay", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "addOnTab", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "addOnBlur", "separator", "appendTo", "motionOptions"], outputs: ["completeMethod", "onSelect", "onUnselect", "onAdd", "onFocus", "onBlur", "onDropdownClick", "onClear", "onInputKeydown", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "component", type: Label, selector: "vl-label", inputs: ["formField", "modalItemInfo"] }, { kind: "component", type: String$1, selector: "vl-string", inputs: ["formField", "modalItemInfo"] }, { kind: "component", type: Textbox, selector: "vl-textbox", inputs: ["formField", "modalItemInfo"] }, { kind: "directive", type: CustomKeyboardDirective, selector: "[vlCustomKeyboard]", inputs: ["vlCustomKeyboard"] }, { kind: "pipe", type: i5.CurrencyPipe, name: "currency" }] });
5178
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: ModelComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1$3.DynamicDialogRef }, { token: i1$3.DynamicDialogConfig }, { token: UtilityService }], target: i0.ɵɵFactoryTarget.Component });
5179
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.2", type: ModelComponent, isStandalone: true, selector: "vl-model", inputs: { errorMessages: "errorMessages", modalItemInfo: "modalItemInfo", selectedLanguageInfo: "selectedLanguageInfo", userInfo: "userInfo", setFormDataHandler: "setFormDataHandler" }, usesOnChanges: true, ngImport: i0, template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\r\n - change: number, autocomplete, date and time fields take the toolkit keyboard of their\r\n attribute type (keyboardTypeFor) instead of only an explicit option.keyboardType\r\n - change: the date picker docks at the bottom on the devices (model-datepicker-panel--docked);\r\n the time picker stays attached to its field in a compact form (model-timepicker-panel);\r\n both report which picker closed (onPickerClose(picker)) -->\r\n\r\n<!-- Shuaib 2026-09-24\r\n - change: the model field switch uses renderFieldType so legacy checkbox fields render as boolean. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - new: a @contract comment on the widest type coverage in the toolkit, and one on the credit\r\n card case recording that the rename reached this file first -->\r\n<!-- Loader Spinner -->\r\n<div *ngIf=\"isLoading\" class=\"loader-overlay\">\r\n <div class=\"spinner\"></div>\r\n</div>\r\n<div [style]=\"getStyleWithoutClasses(modalItemInfo?.layoutSchema?.style).style\"\r\n [class]=\"modalItemInfo?.layoutSchema?.style?.classes[0]\">\r\n <form #dynamicForm=\"ngForm\"\r\n *ngIf=\"modalItemInfo?.layoutSchema?.formSections?.length > 0\"\r\n (submit)=\"$event.preventDefault()\">\r\n <div\r\n *ngFor=\"let formSection of modalItemInfo?.layoutSchema?.formSections\">\r\n <div *ngIf=\"formSection?.label?.caption\"\r\n [class]=\"formSection?.label?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(formSection?.label?.style).style\">\r\n {{formSection.label.caption}}\r\n </div>\r\n <div *ngIf=\"formSection?.formFields?.length > 0\"\r\n [class]=\"formSection?.style?.classes?.[0] || ''\"\r\n [ngStyle]=\"getSectionBodyStyle(formSection)\">\r\n <div *ngFor=\"let formField of formSection.formFields\"\r\n [ngStyle]=\"getFieldWrapperStyle(formField)\"\r\n [class]=\"formField?.style?.classes || ''\">\r\n\r\n <!-- @contract decision: the widest type coverage in the toolkit\r\n reality: every field type except the grid-only quantity renders here.\r\n When another container drops a type, this is the way out.\r\n seen: measured 2026-09-19. Note the file is model.component.html while\r\n the container is called modal in layout files - that costs a\r\n search every time -->\r\n <!-- @contract compatibility: checkbox is rendered as boolean for existing projects. -->\r\n <div [ngSwitch]=\"renderFieldType(\r\n formField?.scope?.reference &&\r\n modalItemInfo?.propertySchema?.[\r\n getScope(formField.scope.reference)\r\n ]\r\n ? modalItemInfo.propertySchema[\r\n getScope(formField.scope.reference)\r\n ]?.[\r\n formField.scope.reference.split('/').pop()\r\n ]?.type\r\n : 'default'\r\n )\">\r\n <!-- image control -->\r\n <div *ngSwitchCase=\"'image'\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\">\r\n <img [src]=\"formField.option.src\" [alt]=\"'img'\" />\r\n </div>\r\n\r\n <!-- credit card control -->\r\n <!-- @contract decision: the credit card type is spelled creditCard\r\n reality: this file was renamed on 2026-09-15, form.component.html and\r\n utility.service.ts followed on 2026-09-19. carditCard is read\r\n nowhere any more.\r\n failure: a data object still declaring carditCard renders empty. -->\r\n <div *ngSwitchCase=\"'creditCard'\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [class]=\"formField.style?.classes[0]\">\r\n <div id=\"card-element\">\r\n <!-- a Stripe element will be inserted here -->\r\n </div>\r\n <!-- Used to display form errors -->\r\n <div id=\"card-errors\"\r\n class=\"displayError.textContent!=='': 'alert alert-danger mt-1'\">\r\n </div>\r\n </div>\r\n\r\n <!-- string control -->\r\n <div *ngSwitchCase=\"'string'\" class=\"form-group mb-3\">\r\n @if(formField.option && formField.option.control &&\r\n !formField.option.protected){\r\n @switch (formField.option.control) {\r\n @case ('textInput') {\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-label>\r\n <vl-textbox [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-textbox>\r\n </div>\r\n <!-- Error Message -->\r\n <!-- <div *ngIf=\"inputField?.touched && inputField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\" *ngIf=\"inputField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages, \"required\")}}</p>\r\n <p class=\"text-danger\" *ngIf=\"inputField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div> -->\r\n }\r\n\r\n @case ('password') {\r\n <span>Switch not supported yet</span>\r\n\r\n }\r\n @case ('combobox') {\r\n <span>Combobox not supported yet</span>\r\n }\r\n @case('textArea'){\r\n <span>TextArea not supported yet</span>\r\n }\r\n @case ('switch') {\r\n <span>Switch not supported yet</span>\r\n }\r\n\r\n @case('radiobutton') {\r\n <span>Radiobutton not supported yet</span>\r\n }\r\n\r\n @case ('dropdown') {\r\n <span *ngIf=\"formField.option?.postfixCaption\">\r\n <select id=\"dynamic-dropdown\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\r\n #dropdownField=\"ngModel\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\">\r\n <option\r\n *ngFor=\"let item of modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.data\"\r\n [value]=\"formField?.option?.optionValue ? formField.option.optionValue : 'value'\">\r\n {{ formField?.option?.optionLabel ?\r\n formField.option.optionLabel :\r\n 'label'}}</option>\r\n </select>\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\">{{modalItemInfo.propertySchema[getScope(formField.scope)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n </span>\r\n <div *ngIf=\"!formField.option?.postfixCaption\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\">{{modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n\r\n <nz-select nzShowSearch nzAllowClear\r\n nzPlaceHolder=\"Select\"\r\n #dropdownField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\">\r\n <nz-option\r\n *ngFor=\"let item of modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.data\"\r\n [nzLabel]=\"item[formField?.option?.optionLabel ? formField.option.optionLabel : 'label']\"\r\n [nzValue]=\"item[formField?.option?.optionValue ? formField.option.optionValue : 'value']\"></nz-option>\r\n </nz-select>\r\n </div>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"dropdownField?.touched && dropdownField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dropdownField?.errors?.['required']\">\r\n {{formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dropdownField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n }\r\n\r\n @case ('autocomplete') {\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-label>\r\n\r\n <span>\r\n <p-autocomplete\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [class]=\"formField?.scope?.style?.classes ? formField?.scope?.style?.classes[0] : ''\"\r\n [suggestions]=\"matchData\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"formField?.option?.autocomplete?.response?.label ? formField.option.autocomplete.response.label : 'label'\"\r\n [placeholder]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.placeholder\"\r\n (completeMethod)=\"autocompChangeEvent($event, formField)\"\r\n (ngModelChange)=\"autocompFocusOutEvent($event, formField)\"\r\n [vlCustomKeyboard]=\"keyboardTypeFor(formField)\">\r\n </p-autocomplete>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"autocompleteField?.touched && autocompleteField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['required']\">\r\n {{setErroMessage(formField,\r\n errorMessages, \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' +\r\n errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n </div>\r\n }\r\n }\r\n }@else{\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-label>\r\n <vl-textbox [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-textbox>\r\n </div>\r\n <!-- Error Message -->\r\n <!-- <div *ngIf=\"inputField?.touched && inputField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\" *ngIf=\"inputField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages, \"required\")}}</p>\r\n <p class=\"text-danger\" *ngIf=\"inputField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div> -->\r\n </div>\r\n }\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n\r\n </div>\r\n\r\n <!-- number control -->\r\n <div *ngSwitchCase=\"'number'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <h5>{{\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }} {{\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.value\r\n |\r\n currency:formField.option.currency :true:\r\n formField.option?.decimalPoint}}\r\n </h5>\r\n </span>\r\n <span *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{\r\n formField.label?.value\r\n ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <input type=\"number\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\r\n [vlCustomKeyboard]=\"keyboardTypeFor(formField)\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"formField.scope?.reference\"\r\n [placeholder]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.placeholder\" />\r\n </div>\r\n </span>\r\n </div>\r\n\r\n <!-- DateRange control -->\r\n <div *ngSwitchCase=\"'dateRange'\"\r\n class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{\r\n formField.label?.value\r\n ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <nz-range-picker #dateRangeField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"></nz-range-picker>\r\n </div>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"dateRangeField?.touched && dateRangeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['required']\">\r\n {{formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"form-group mb-3\">\r\n <span\r\n *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string\r\n [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n {{ getLabel(formField) }}\r\n </label>\r\n\r\n <span>\r\n <div class=\"picker-field\">\r\n <p-datepicker\r\n #datePicker\r\n #dateField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n [showIcon]=\"false\"\r\n [showOnFocus]=\"false\"\r\n appendTo=\"body\"\r\n [autoZIndex]=\"true\"\r\n [baseZIndex]=\"10000\"\r\n [panelStyleClass]=\"picker.datePanelClass\"\r\n [motionOptions]=\"picker.dockedMotion\"\r\n [vlCustomKeyboard]=\"keyboardTypeFor(formField)\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, datePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(datePicker)\"\r\n (ngModelChange)=\"dateChangeEvent(formField, $event)\"\r\n (onInput)=\"onDateTyping($event)\"\r\n [dateFormat]=\"\r\n modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ]?.format || 'yy-mm-dd'\r\n \"\r\n [(ngModel)]=\"\r\n modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"formField?.scope?.style?.classes?.[0] || ''\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\">\r\n </p-datepicker>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"picker-field__icon\"\r\n aria-label=\"Open calendar\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n (pointerdown)=\"picker.openFromIcon($event, datePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-calendar\"></i>\r\n </button>\r\n </div>\r\n\r\n <div\r\n *ngIf=\"dateField?.touched && dateField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['required']\">\r\n {{ setErroMessage(formField,\r\n errorMessages, 'required') }}\r\n </p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['email']\">\r\n {{\r\n (formField.label?.value\r\n ? formField.label?.value\r\n : modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption)\r\n + ' ' + errorMessages.pattern\r\n }}\r\n </p>\r\n </div>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Time control -->\r\n <div *ngSwitchCase=\"'time'\" class=\"form-group mb-3\">\r\n <span\r\n *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string\r\n [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n {{ getLabel(formField) }}\r\n </label>\r\n\r\n <span>\r\n <div class=\"picker-field\">\r\n <p-datepicker\r\n #timePicker\r\n #timeField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [timeOnly]=\"true\"\r\n hourFormat=\"24\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n [showIcon]=\"false\"\r\n [showOnFocus]=\"false\"\r\n appendTo=\"body\"\r\n [autoZIndex]=\"true\"\r\n [baseZIndex]=\"10000\"\r\n [panelStyleClass]=\"picker.timePanelClass\"\r\n [vlCustomKeyboard]=\"keyboardTypeFor(formField)\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, timePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"onTimePanelShow(formField)\"\r\n (onClose)=\"picker.onClose(timePicker)\"\r\n (onInput)=\"onTimeTyping($event)\"\r\n (ngModelChange)=\"timeChangeEvent(formField, $event)\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [(ngModel)]=\"\r\n modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"formField?.scope?.style?.classes?.[0] || ''\">\r\n </p-datepicker>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"picker-field__icon\"\r\n aria-label=\"Open time picker\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n (pointerdown)=\"picker.openFromIcon($event, timePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-clock\"></i>\r\n </button>\r\n </div>\r\n\r\n <div\r\n *ngIf=\"timeField?.touched && timeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['required']\">\r\n {{ setErroMessage(formField,\r\n errorMessages, 'required') }}\r\n </p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['email']\">\r\n {{\r\n (formField.label?.value\r\n ? formField.label?.value\r\n : modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption)\r\n + ' ' + errorMessages.pattern\r\n }}\r\n </p>\r\n </div>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Autocomplete control -->\r\n <div *ngSwitchCase=\"'autocomplete'\"\r\n class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-label>\r\n\r\n <span>\r\n <p-autocomplete\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [class]=\"formField?.scope?.style?.classes ? formField?.scope?.style?.classes[0] : ''\"\r\n [suggestions]=\"matchData\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"formField?.option?.autocomplete?.response?.label ? formField.option.autocomplete.response.label : 'label'\"\r\n [placeholder]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.placeholder\"\r\n (completeMethod)=\"autocompChangeEvent($event, formField)\"\r\n (ngModelChange)=\"autocompFocusOutEvent($event, formField)\"\r\n [vlCustomKeyboard]=\"keyboardTypeFor(formField)\">\r\n </p-autocomplete>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"autocompleteField?.touched && autocompleteField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['required']\">\r\n {{setErroMessage(formField,\r\n errorMessages, \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' +\r\n errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- checkBox control -->\r\n <div *ngSwitchCase=\"'boolean'\" class=\"form-group mb-3\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0] : ''\">\r\n <span *ngIf=\"formField.option?.postfixCaption\">\r\n <label class=\"form-check-label\"><input\r\n type=\"checkbox\" class=\"form-check-input\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"formField.scope?.reference\"\r\n (ngModelChange)=\"invokeSetformData()\" />{{\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n </span>\r\n <div *ngIf=\"!formField.option?.postfixCaption\"\r\n [class]=\"formField?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label class=\"form-check-label\">{{\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}\r\n <input type=\"checkbox\"\r\n class=\"form-check-input\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"formField.scope?.reference\" /></label>\r\n </div>\r\n </div>\r\n\r\n <!-- dropdown control -->\r\n <div *ngSwitchCase=\"'dropdown'\" class=\"form-group mb-3\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0] : ''\">\r\n <span *ngIf=\"formField.option?.postfixCaption\">\r\n <select id=\"dynamic-dropdown\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\r\n #dropdownField=\"ngModel\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\">\r\n <option\r\n *ngFor=\"let item of modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.data\"\r\n [value]=\"formField?.option?.optionValue ? formField.option.optionValue : 'value'\">\r\n {{ formField?.option?.optionLabel ?\r\n formField.option.optionLabel :\r\n 'label'}}</option>\r\n </select>\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\">{{modalItemInfo.propertySchema[getScope(formField.scope)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n </span>\r\n <div *ngIf=\"!formField.option?.postfixCaption\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\">{{modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n\r\n <nz-select nzShowSearch nzAllowClear\r\n nzPlaceHolder=\"Select\"\r\n #dropdownField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\">\r\n <nz-option\r\n *ngFor=\"let item of modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.data\"\r\n [nzLabel]=\"item[formField?.option?.optionLabel ? formField.option.optionLabel : 'label']\"\r\n [nzValue]=\"item[formField?.option?.optionValue ? formField.option.optionValue : 'value']\"></nz-option>\r\n </nz-select>\r\n </div>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"dropdownField?.touched && dropdownField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dropdownField?.errors?.['required']\">\r\n {{formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dropdownField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- button control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button type=\"button\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField.style).style\"\r\n (click)=\"invokeFormBtnClick(formField.option, modalItemInfo.propertySchema, dynamicForm)\"\r\n class=\"btn btn-primary btn-block\">\r\n {{ getButtonCaption(formField) }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </form>\r\n</div>\r\n", styles: [":host ::ng-deep .custom-datepicker .p-datepicker-dropdown{height:100%;border-radius:4px}:host ::ng-deep .custom-datepicker .p-datepicker-input{border-radius:4px}::ng-deep .p-autocomplete-list{background-color:#fff}:host ::ng-deep .custom-autocomplete{display:flex;width:100%}:host ::ng-deep .custom-autocomplete .p-autocomplete-input{flex:1;width:100%;min-width:0;border:none!important;outline:none!important;box-shadow:none!important;background:transparent!important}:host ::ng-deep .custom-autocomplete .p-autocomplete-dropdown{height:100%;border-radius:4px}:host ::ng-deep .custom-autocomplete .p-autocomplete-input:focus,:host ::ng-deep .custom-autocomplete .p-autocomplete-input:focus-visible,:host ::ng-deep .custom-autocomplete .p-inputtext:enabled:focus{border:none!important;outline:none!important;box-shadow:none!important}.responsive-modal-grid{width:100%;min-width:0;box-sizing:border-box}@media screen and (max-width:767px){.responsive-modal-grid{grid-template-columns:repeat(12,minmax(0,1fr))!important;gap:10px!important}.responsive-modal-field{grid-column:1 / -1!important;width:100%!important;max-width:none!important;min-width:0!important}.responsive-modal-action:nth-last-child(2){grid-column:1 / span 6!important}.responsive-modal-action:last-child{grid-column:7 / span 6!important}.responsive-modal-action{width:100%!important;max-width:none!important;min-width:0!important;justify-self:stretch!important;box-sizing:border-box!important}.responsive-modal-action button{display:block!important;width:100%!important;max-width:none!important;min-width:0!important;margin:0!important;box-sizing:border-box!important}}:host{--modal-field-focus-color: var(--p-primary-color, #10b981);--modal-field-focus-shadow: 0 0 0 2px rgba(16, 185, 129, .16)}:host ::ng-deep input:not([type=checkbox]):not(:disabled):focus,:host ::ng-deep textarea:not(:disabled):focus{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .custom-autocomplete:focus-within{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .custom-autocomplete:focus-within .p-autocomplete-input{border:none!important;outline:none!important;box-shadow:none!important}:host ::ng-deep select:not(:disabled):focus{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .ant-select-focused:not(.ant-select-disabled) .ant-select-selector,:host ::ng-deep nz-select:not(.ant-select-disabled):focus-within .ant-select-selector{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .p-select:not(.p-disabled).p-focus,:host ::ng-deep .p-select:not(.p-disabled):focus-within,:host ::ng-deep .p-dropdown:not(.p-disabled).p-focus,:host ::ng-deep .p-dropdown:not(.p-disabled):focus-within{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .custom-datepicker:focus-within .p-datepicker-input{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep input:disabled,:host ::ng-deep textarea:disabled,:host ::ng-deep select:disabled,:host ::ng-deep .p-disabled,:host ::ng-deep .ant-select-disabled .ant-select-selector{box-shadow:none!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i5.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i5.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.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: i2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: NzSelectModule }, { kind: "component", type: i3.NzOptionComponent, selector: "nz-option", inputs: ["nzTitle", "nzLabel", "nzValue", "nzKey", "nzDisabled", "nzHide", "nzCustomContent"], exportAs: ["nzOption"] }, { kind: "component", type: i3.NzSelectComponent, selector: "nz-select", inputs: ["nzId", "nzSize", "nzStatus", "nzVariant", "nzOptionHeightPx", "nzOptionOverflowSize", "nzDropdownClassName", "nzDropdownMatchSelectWidth", "nzDropdownStyle", "nzNotFoundContent", "nzPlaceHolder", "nzPlacement", "nzMaxTagCount", "nzDropdownRender", "nzCustomTemplate", "nzPrefix", "nzSuffixIcon", "nzClearIcon", "nzRemoveIcon", "nzMenuItemSelectedIcon", "nzTokenSeparators", "nzMaxTagPlaceholder", "nzMaxMultipleCount", "nzMode", "nzFilterOption", "compareWith", "nzAllowClear", "nzShowSearch", "nzLoading", "nzAutoFocus", "nzAutoClearSearchValue", "nzServerSearch", "nzDisabled", "nzOpen", "nzSelectOnTab", "nzBackdrop", "nzOptions", "nzShowArrow"], outputs: ["nzOnSearch", "nzScrollToBottom", "nzOpenChange", "nzBlur", "nzFocus", "nzOnClear"], exportAs: ["nzSelect"] }, { kind: "ngmodule", type: NzDatePickerModule }, { kind: "component", type: i4.NzDatePickerComponent, selector: "nz-date-picker,nz-week-picker,nz-month-picker,nz-quarter-picker,nz-year-picker,nz-range-picker", inputs: ["nzInline", "nzAllowClear", "nzAutoFocus", "nzDisabled", "nzInputReadOnly", "nzOpen", "nzDisabledDate", "nzLocale", "nzPlaceHolder", "nzPopupStyle", "nzDropdownClassName", "nzSize", "nzStatus", "nzFormat", "nzVariant", "nzDateRender", "nzDisabledTime", "nzRenderExtraFooter", "nzShowToday", "nzMode", "nzShowNow", "nzRanges", "nzDefaultPickerValue", "nzSeparator", "nzSuffixIcon", "nzBackdrop", "nzId", "nzPlacement", "nzShowWeekNumber", "nzShowTime"], outputs: ["nzOnPanelChange", "nzOnCalendarChange", "nzOnOk", "nzOnOpenChange"], exportAs: ["nzDatePicker"] }, { kind: "directive", type: i4.NzRangePickerComponent, selector: "nz-range-picker", exportAs: ["nzRangePicker"] }, { kind: "ngmodule", type: DatePickerModule }, { kind: "component", type: i12.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: AutoCompleteModule }, { kind: "component", type: i18.AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "minQueryLength", "delay", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "addOnTab", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "addOnBlur", "separator", "appendTo", "motionOptions"], outputs: ["completeMethod", "onSelect", "onUnselect", "onAdd", "onFocus", "onBlur", "onDropdownClick", "onClear", "onInputKeydown", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "component", type: Label, selector: "vl-label", inputs: ["formField", "modalItemInfo"] }, { kind: "component", type: String$1, selector: "vl-string", inputs: ["formField", "modalItemInfo"] }, { kind: "component", type: Textbox, selector: "vl-textbox", inputs: ["formField", "modalItemInfo"] }, { kind: "directive", type: CustomKeyboardDirective, selector: "[vlCustomKeyboard]", inputs: ["vlCustomKeyboard"] }, { kind: "pipe", type: i5.CurrencyPipe, name: "currency" }] });
4761
5180
  }
4762
5181
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: ModelComponent, decorators: [{
4763
5182
  type: Component,
@@ -4773,8 +5192,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
4773
5192
  String$1,
4774
5193
  Textbox,
4775
5194
  CustomKeyboardDirective,
4776
- ], template: "<!-- Shuaib 2026-09-24\r\n - change: the model field switch uses renderFieldType so legacy checkbox fields render as boolean. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - new: a @contract comment on the widest type coverage in the toolkit, and one on the credit\r\n card case recording that the rename reached this file first -->\r\n<!-- Loader Spinner -->\r\n<div *ngIf=\"isLoading\" class=\"loader-overlay\">\r\n <div class=\"spinner\"></div>\r\n</div>\r\n<div [style]=\"getStyleWithoutClasses(modalItemInfo?.layoutSchema?.style).style\"\r\n [class]=\"modalItemInfo?.layoutSchema?.style?.classes[0]\">\r\n <form #dynamicForm=\"ngForm\"\r\n *ngIf=\"modalItemInfo?.layoutSchema?.formSections?.length > 0\"\r\n (submit)=\"$event.preventDefault()\">\r\n <div\r\n *ngFor=\"let formSection of modalItemInfo?.layoutSchema?.formSections\">\r\n <div *ngIf=\"formSection?.label?.caption\"\r\n [class]=\"formSection?.label?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(formSection?.label?.style).style\">\r\n {{formSection.label.caption}}\r\n </div>\r\n <div *ngIf=\"formSection?.formFields?.length > 0\"\r\n [class]=\"formSection?.style?.classes?.[0] || ''\"\r\n [ngStyle]=\"getSectionBodyStyle(formSection)\">\r\n <div *ngFor=\"let formField of formSection.formFields\"\r\n [ngStyle]=\"getFieldWrapperStyle(formField)\"\r\n [class]=\"formField?.style?.classes || ''\">\r\n\r\n <!-- @contract decision: the widest type coverage in the toolkit\r\n reality: every field type except the grid-only quantity renders here.\r\n When another container drops a type, this is the way out.\r\n seen: measured 2026-09-19. Note the file is model.component.html while\r\n the container is called modal in layout files - that costs a\r\n search every time -->\r\n <!-- @contract compatibility: checkbox is rendered as boolean for existing projects. -->\r\n <div [ngSwitch]=\"renderFieldType(\r\n formField?.scope?.reference &&\r\n modalItemInfo?.propertySchema?.[\r\n getScope(formField.scope.reference)\r\n ]\r\n ? modalItemInfo.propertySchema[\r\n getScope(formField.scope.reference)\r\n ]?.[\r\n formField.scope.reference.split('/').pop()\r\n ]?.type\r\n : 'default'\r\n )\">\r\n <!-- image control -->\r\n <div *ngSwitchCase=\"'image'\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\">\r\n <img [src]=\"formField.option.src\" [alt]=\"'img'\" />\r\n </div>\r\n\r\n <!-- credit card control -->\r\n <!-- @contract decision: the credit card type is spelled creditCard\r\n reality: this file was renamed on 2026-09-15, form.component.html and\r\n utility.service.ts followed on 2026-09-19. carditCard is read\r\n nowhere any more.\r\n failure: a data object still declaring carditCard renders empty. -->\r\n <div *ngSwitchCase=\"'creditCard'\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [class]=\"formField.style?.classes[0]\">\r\n <div id=\"card-element\">\r\n <!-- a Stripe element will be inserted here -->\r\n </div>\r\n <!-- Used to display form errors -->\r\n <div id=\"card-errors\"\r\n class=\"displayError.textContent!=='': 'alert alert-danger mt-1'\">\r\n </div>\r\n </div>\r\n\r\n <!-- string control -->\r\n <div *ngSwitchCase=\"'string'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-label>\r\n <vl-textbox [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-textbox>\r\n </div>\r\n <!-- Error Message -->\r\n <!-- <div *ngIf=\"inputField?.touched && inputField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\" *ngIf=\"inputField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages, \"required\")}}</p>\r\n <p class=\"text-danger\" *ngIf=\"inputField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div> -->\r\n </div>\r\n </div>\r\n\r\n <!-- number control -->\r\n <div *ngSwitchCase=\"'number'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <h5>{{\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }} {{\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.value\r\n |\r\n currency:formField.option.currency :true:\r\n formField.option?.decimalPoint}}\r\n </h5>\r\n </span>\r\n <span *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{\r\n formField.label?.value\r\n ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <input type=\"number\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\r\n [vlCustomKeyboard]=\"formField?.option?.keyboardType\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"formField.scope?.reference\"\r\n [placeholder]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.placeholder\" />\r\n </div>\r\n </span>\r\n </div>\r\n\r\n <!-- DateRange control -->\r\n <div *ngSwitchCase=\"'dateRange'\"\r\n class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{\r\n formField.label?.value\r\n ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <nz-range-picker #dateRangeField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"></nz-range-picker>\r\n </div>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"dateRangeField?.touched && dateRangeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['required']\">\r\n {{formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"form-group mb-3\">\r\n <span\r\n *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string\r\n [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n {{ getLabel(formField) }}\r\n </label>\r\n\r\n <span>\r\n <div class=\"picker-field\">\r\n <p-datepicker\r\n #datePicker\r\n #dateField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n [showIcon]=\"false\"\r\n [showOnFocus]=\"false\"\r\n appendTo=\"body\"\r\n [autoZIndex]=\"true\"\r\n [baseZIndex]=\"10000\"\r\n panelStyleClass=\"model-datepicker-panel\"\r\n (pointerdown)=\"onPickerContainerPointerDown($event, datePicker)\"\r\n (onFocus)=\"onPickerInputFocus($event)\"\r\n (onShow)=\"onPickerShown()\"\r\n (onClose)=\"onPickerClose()\"\r\n (ngModelChange)=\"dateChangeEvent(formField, $event)\"\r\n (onInput)=\"onDateTyping($event)\"\r\n [dateFormat]=\"\r\n modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ]?.format || 'yy-mm-dd'\r\n \"\r\n [(ngModel)]=\"\r\n modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"formField?.scope?.style?.classes?.[0] || ''\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\">\r\n </p-datepicker>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"picker-field__icon\"\r\n aria-label=\"Open calendar\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n (pointerdown)=\"openPickerFromIcon($event, datePicker)\"\r\n (click)=\"blockPickerIconClick($event)\">\r\n <i class=\"pi pi-calendar\"></i>\r\n </button>\r\n </div>\r\n\r\n <div\r\n *ngIf=\"dateField?.touched && dateField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['required']\">\r\n {{ setErroMessage(formField,\r\n errorMessages, 'required') }}\r\n </p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['email']\">\r\n {{\r\n (formField.label?.value\r\n ? formField.label?.value\r\n : modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption)\r\n + ' ' + errorMessages.pattern\r\n }}\r\n </p>\r\n </div>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Time control -->\r\n <div *ngSwitchCase=\"'time'\" class=\"form-group mb-3\">\r\n <span\r\n *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string\r\n [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n {{ getLabel(formField) }}\r\n </label>\r\n\r\n <span>\r\n <div class=\"picker-field\">\r\n <p-datepicker\r\n #timePicker\r\n #timeField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [timeOnly]=\"true\"\r\n hourFormat=\"24\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n [showIcon]=\"false\"\r\n [showOnFocus]=\"false\"\r\n appendTo=\"body\"\r\n [autoZIndex]=\"true\"\r\n [baseZIndex]=\"10000\"\r\n panelStyleClass=\"model-datepicker-panel\"\r\n (pointerdown)=\"onPickerContainerPointerDown($event, timePicker)\"\r\n (onFocus)=\"onPickerInputFocus($event)\"\r\n (onShow)=\"onTimePanelShow(formField)\"\r\n (onClose)=\"onPickerClose()\"\r\n (onInput)=\"onTimeTyping($event)\"\r\n (ngModelChange)=\"timeChangeEvent(formField, $event)\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [(ngModel)]=\"\r\n modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"formField?.scope?.style?.classes?.[0] || ''\">\r\n </p-datepicker>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"picker-field__icon\"\r\n aria-label=\"Open time picker\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n (pointerdown)=\"openPickerFromIcon($event, timePicker)\"\r\n (click)=\"blockPickerIconClick($event)\">\r\n <i class=\"pi pi-clock\"></i>\r\n </button>\r\n </div>\r\n\r\n <div\r\n *ngIf=\"timeField?.touched && timeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['required']\">\r\n {{ setErroMessage(formField,\r\n errorMessages, 'required') }}\r\n </p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['email']\">\r\n {{\r\n (formField.label?.value\r\n ? formField.label?.value\r\n : modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption)\r\n + ' ' + errorMessages.pattern\r\n }}\r\n </p>\r\n </div>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Autocomplete control -->\r\n <div *ngSwitchCase=\"'autocomplete'\"\r\n class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-label>\r\n\r\n <span>\r\n <p-autocomplete\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [class]=\"formField?.scope?.style?.classes ? formField?.scope?.style?.classes[0] : ''\"\r\n [suggestions]=\"matchData\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"formField?.option?.autocomplete?.response?.label ? formField.option.autocomplete.response.label : 'label'\"\r\n [placeholder]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.placeholder\"\r\n (completeMethod)=\"autocompChangeEvent($event, formField)\"\r\n (ngModelChange)=\"autocompFocusOutEvent($event, formField)\"\r\n [vlCustomKeyboard]=\"formField?.option?.keyboardType\">\r\n </p-autocomplete>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"autocompleteField?.touched && autocompleteField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['required']\">\r\n {{setErroMessage(formField,\r\n errorMessages, \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' +\r\n errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- checkBox control -->\r\n <div *ngSwitchCase=\"'boolean'\" class=\"form-group mb-3\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0] : ''\">\r\n <span *ngIf=\"formField.option?.postfixCaption\">\r\n <label class=\"form-check-label\"><input\r\n type=\"checkbox\" class=\"form-check-input\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"formField.scope?.reference\"\r\n (ngModelChange)=\"invokeSetformData()\" />{{\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n </span>\r\n <div *ngIf=\"!formField.option?.postfixCaption\"\r\n [class]=\"formField?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label class=\"form-check-label\">{{\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}\r\n <input type=\"checkbox\"\r\n class=\"form-check-input\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"formField.scope?.reference\" /></label>\r\n </div>\r\n </div>\r\n\r\n <!-- dropdown control -->\r\n <div *ngSwitchCase=\"'dropdown'\" class=\"form-group mb-3\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0] : ''\">\r\n <span *ngIf=\"formField.option?.postfixCaption\">\r\n <select id=\"dynamic-dropdown\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\r\n #dropdownField=\"ngModel\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\">\r\n <option\r\n *ngFor=\"let item of modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.data\"\r\n [value]=\"formField?.option?.optionValue ? formField.option.optionValue : 'value'\">\r\n {{ formField?.option?.optionLabel ?\r\n formField.option.optionLabel :\r\n 'label'}}</option>\r\n </select>\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\">{{modalItemInfo.propertySchema[getScope(formField.scope)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n </span>\r\n <div *ngIf=\"!formField.option?.postfixCaption\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\">{{modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n\r\n <nz-select nzShowSearch nzAllowClear\r\n nzPlaceHolder=\"Select\"\r\n #dropdownField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\">\r\n <nz-option\r\n *ngFor=\"let item of modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.data\"\r\n [nzLabel]=\"item[formField?.option?.optionLabel ? formField.option.optionLabel : 'label']\"\r\n [nzValue]=\"item[formField?.option?.optionValue ? formField.option.optionValue : 'value']\"></nz-option>\r\n </nz-select>\r\n </div>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"dropdownField?.touched && dropdownField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dropdownField?.errors?.['required']\">\r\n {{formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dropdownField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- button control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button type=\"button\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField.style).style\"\r\n (click)=\"invokeFormBtnClick(formField.option, modalItemInfo.propertySchema, dynamicForm)\"\r\n class=\"btn btn-primary btn-block\">\r\n {{ getButtonCaption(formField) }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </form>\r\n</div>\r\n", styles: [":host ::ng-deep .custom-datepicker .p-datepicker-dropdown{height:100%;border-radius:4px}:host ::ng-deep .custom-datepicker .p-datepicker-input{border-radius:4px}::ng-deep .p-autocomplete-list{background-color:#fff}:host ::ng-deep .custom-autocomplete{display:flex;width:100%}:host ::ng-deep .custom-autocomplete .p-autocomplete-input{flex:1;width:100%;min-width:0;border:none!important;outline:none!important;box-shadow:none!important;background:transparent!important}:host ::ng-deep .custom-autocomplete .p-autocomplete-dropdown{height:100%;border-radius:4px}:host ::ng-deep .custom-autocomplete .p-autocomplete-input:focus,:host ::ng-deep .custom-autocomplete .p-autocomplete-input:focus-visible,:host ::ng-deep .custom-autocomplete .p-inputtext:enabled:focus{border:none!important;outline:none!important;box-shadow:none!important}.responsive-modal-grid{width:100%;min-width:0;box-sizing:border-box}@media screen and (max-width:767px){.responsive-modal-grid{grid-template-columns:repeat(12,minmax(0,1fr))!important;gap:10px!important}.responsive-modal-field{grid-column:1 / -1!important;width:100%!important;max-width:none!important;min-width:0!important}.responsive-modal-action:nth-last-child(2){grid-column:1 / span 6!important}.responsive-modal-action:last-child{grid-column:7 / span 6!important}.responsive-modal-action{width:100%!important;max-width:none!important;min-width:0!important;justify-self:stretch!important;box-sizing:border-box!important}.responsive-modal-action button{display:block!important;width:100%!important;max-width:none!important;min-width:0!important;margin:0!important;box-sizing:border-box!important}}:host{--modal-field-focus-color: var(--p-primary-color, #10b981);--modal-field-focus-shadow: 0 0 0 2px rgba(16, 185, 129, .16)}:host ::ng-deep input:not([type=checkbox]):not(:disabled):focus,:host ::ng-deep textarea:not(:disabled):focus{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .custom-autocomplete:focus-within{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .custom-autocomplete:focus-within .p-autocomplete-input{border:none!important;outline:none!important;box-shadow:none!important}:host ::ng-deep select:not(:disabled):focus{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .ant-select-focused:not(.ant-select-disabled) .ant-select-selector,:host ::ng-deep nz-select:not(.ant-select-disabled):focus-within .ant-select-selector{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .p-select:not(.p-disabled).p-focus,:host ::ng-deep .p-select:not(.p-disabled):focus-within,:host ::ng-deep .p-dropdown:not(.p-disabled).p-focus,:host ::ng-deep .p-dropdown:not(.p-disabled):focus-within{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .custom-datepicker:focus-within .p-datepicker-input{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep input:disabled,:host ::ng-deep textarea:disabled,:host ::ng-deep select:disabled,:host ::ng-deep .p-disabled,:host ::ng-deep .ant-select-disabled .ant-select-selector{box-shadow:none!important}.picker-field{position:relative}.picker-field__icon{position:absolute;right:0;top:0;height:100%;width:2.5rem;border:0;background:transparent;cursor:pointer}\n"] }]
4777
- }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1$3.DynamicDialogRef }, { type: i1$3.DynamicDialogConfig }, { type: UtilityService }, { type: CustomKeyboardService }], propDecorators: { errorMessages: [{
5195
+ ], template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\r\n - change: number, autocomplete, date and time fields take the toolkit keyboard of their\r\n attribute type (keyboardTypeFor) instead of only an explicit option.keyboardType\r\n - change: the date picker docks at the bottom on the devices (model-datepicker-panel--docked);\r\n the time picker stays attached to its field in a compact form (model-timepicker-panel);\r\n both report which picker closed (onPickerClose(picker)) -->\r\n\r\n<!-- Shuaib 2026-09-24\r\n - change: the model field switch uses renderFieldType so legacy checkbox fields render as boolean. -->\r\n\r\n<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-09-19\r\n - new: a @contract comment on the widest type coverage in the toolkit, and one on the credit\r\n card case recording that the rename reached this file first -->\r\n<!-- Loader Spinner -->\r\n<div *ngIf=\"isLoading\" class=\"loader-overlay\">\r\n <div class=\"spinner\"></div>\r\n</div>\r\n<div [style]=\"getStyleWithoutClasses(modalItemInfo?.layoutSchema?.style).style\"\r\n [class]=\"modalItemInfo?.layoutSchema?.style?.classes[0]\">\r\n <form #dynamicForm=\"ngForm\"\r\n *ngIf=\"modalItemInfo?.layoutSchema?.formSections?.length > 0\"\r\n (submit)=\"$event.preventDefault()\">\r\n <div\r\n *ngFor=\"let formSection of modalItemInfo?.layoutSchema?.formSections\">\r\n <div *ngIf=\"formSection?.label?.caption\"\r\n [class]=\"formSection?.label?.style?.classes[0]\"\r\n [style]=\"getStyleWithoutClasses(formSection?.label?.style).style\">\r\n {{formSection.label.caption}}\r\n </div>\r\n <div *ngIf=\"formSection?.formFields?.length > 0\"\r\n [class]=\"formSection?.style?.classes?.[0] || ''\"\r\n [ngStyle]=\"getSectionBodyStyle(formSection)\">\r\n <div *ngFor=\"let formField of formSection.formFields\"\r\n [ngStyle]=\"getFieldWrapperStyle(formField)\"\r\n [class]=\"formField?.style?.classes || ''\">\r\n\r\n <!-- @contract decision: the widest type coverage in the toolkit\r\n reality: every field type except the grid-only quantity renders here.\r\n When another container drops a type, this is the way out.\r\n seen: measured 2026-09-19. Note the file is model.component.html while\r\n the container is called modal in layout files - that costs a\r\n search every time -->\r\n <!-- @contract compatibility: checkbox is rendered as boolean for existing projects. -->\r\n <div [ngSwitch]=\"renderFieldType(\r\n formField?.scope?.reference &&\r\n modalItemInfo?.propertySchema?.[\r\n getScope(formField.scope.reference)\r\n ]\r\n ? modalItemInfo.propertySchema[\r\n getScope(formField.scope.reference)\r\n ]?.[\r\n formField.scope.reference.split('/').pop()\r\n ]?.type\r\n : 'default'\r\n )\">\r\n <!-- image control -->\r\n <div *ngSwitchCase=\"'image'\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\">\r\n <img [src]=\"formField.option.src\" [alt]=\"'img'\" />\r\n </div>\r\n\r\n <!-- credit card control -->\r\n <!-- @contract decision: the credit card type is spelled creditCard\r\n reality: this file was renamed on 2026-09-15, form.component.html and\r\n utility.service.ts followed on 2026-09-19. carditCard is read\r\n nowhere any more.\r\n failure: a data object still declaring carditCard renders empty. -->\r\n <div *ngSwitchCase=\"'creditCard'\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [class]=\"formField.style?.classes[0]\">\r\n <div id=\"card-element\">\r\n <!-- a Stripe element will be inserted here -->\r\n </div>\r\n <!-- Used to display form errors -->\r\n <div id=\"card-errors\"\r\n class=\"displayError.textContent!=='': 'alert alert-danger mt-1'\">\r\n </div>\r\n </div>\r\n\r\n <!-- string control -->\r\n <div *ngSwitchCase=\"'string'\" class=\"form-group mb-3\">\r\n @if(formField.option && formField.option.control &&\r\n !formField.option.protected){\r\n @switch (formField.option.control) {\r\n @case ('textInput') {\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-label>\r\n <vl-textbox [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-textbox>\r\n </div>\r\n <!-- Error Message -->\r\n <!-- <div *ngIf=\"inputField?.touched && inputField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\" *ngIf=\"inputField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages, \"required\")}}</p>\r\n <p class=\"text-danger\" *ngIf=\"inputField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div> -->\r\n }\r\n\r\n @case ('password') {\r\n <span>Switch not supported yet</span>\r\n\r\n }\r\n @case ('combobox') {\r\n <span>Combobox not supported yet</span>\r\n }\r\n @case('textArea'){\r\n <span>TextArea not supported yet</span>\r\n }\r\n @case ('switch') {\r\n <span>Switch not supported yet</span>\r\n }\r\n\r\n @case('radiobutton') {\r\n <span>Radiobutton not supported yet</span>\r\n }\r\n\r\n @case ('dropdown') {\r\n <span *ngIf=\"formField.option?.postfixCaption\">\r\n <select id=\"dynamic-dropdown\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\r\n #dropdownField=\"ngModel\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\">\r\n <option\r\n *ngFor=\"let item of modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.data\"\r\n [value]=\"formField?.option?.optionValue ? formField.option.optionValue : 'value'\">\r\n {{ formField?.option?.optionLabel ?\r\n formField.option.optionLabel :\r\n 'label'}}</option>\r\n </select>\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\">{{modalItemInfo.propertySchema[getScope(formField.scope)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n </span>\r\n <div *ngIf=\"!formField.option?.postfixCaption\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\">{{modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n\r\n <nz-select nzShowSearch nzAllowClear\r\n nzPlaceHolder=\"Select\"\r\n #dropdownField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\">\r\n <nz-option\r\n *ngFor=\"let item of modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.data\"\r\n [nzLabel]=\"item[formField?.option?.optionLabel ? formField.option.optionLabel : 'label']\"\r\n [nzValue]=\"item[formField?.option?.optionValue ? formField.option.optionValue : 'value']\"></nz-option>\r\n </nz-select>\r\n </div>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"dropdownField?.touched && dropdownField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dropdownField?.errors?.['required']\">\r\n {{formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dropdownField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n }\r\n\r\n @case ('autocomplete') {\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-label>\r\n\r\n <span>\r\n <p-autocomplete\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [class]=\"formField?.scope?.style?.classes ? formField?.scope?.style?.classes[0] : ''\"\r\n [suggestions]=\"matchData\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"formField?.option?.autocomplete?.response?.label ? formField.option.autocomplete.response.label : 'label'\"\r\n [placeholder]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.placeholder\"\r\n (completeMethod)=\"autocompChangeEvent($event, formField)\"\r\n (ngModelChange)=\"autocompFocusOutEvent($event, formField)\"\r\n [vlCustomKeyboard]=\"keyboardTypeFor(formField)\">\r\n </p-autocomplete>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"autocompleteField?.touched && autocompleteField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['required']\">\r\n {{setErroMessage(formField,\r\n errorMessages, \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' +\r\n errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n </div>\r\n }\r\n }\r\n }@else{\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-label>\r\n <vl-textbox [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-textbox>\r\n </div>\r\n <!-- Error Message -->\r\n <!-- <div *ngIf=\"inputField?.touched && inputField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\" *ngIf=\"inputField?.errors?.['required']\">\r\n {{setErroMessage(formField, errorMessages, \"required\")}}</p>\r\n <p class=\"text-danger\" *ngIf=\"inputField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div> -->\r\n </div>\r\n }\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n\r\n </div>\r\n\r\n <!-- number control -->\r\n <div *ngSwitchCase=\"'number'\" class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <h5>{{\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }} {{\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.value\r\n |\r\n currency:formField.option.currency :true:\r\n formField.option?.decimalPoint}}\r\n </h5>\r\n </span>\r\n <span *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{\r\n formField.label?.value\r\n ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <input type=\"number\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\r\n [vlCustomKeyboard]=\"keyboardTypeFor(formField)\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"formField.scope?.reference\"\r\n [placeholder]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.placeholder\" />\r\n </div>\r\n </span>\r\n </div>\r\n\r\n <!-- DateRange control -->\r\n <div *ngSwitchCase=\"'dateRange'\"\r\n class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">{{\r\n formField.label?.value\r\n ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n <nz-range-picker #dateRangeField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"></nz-range-picker>\r\n </div>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"dateRangeField?.touched && dateRangeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['required']\">\r\n {{formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateRangeField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Date control -->\r\n <div *ngSwitchCase=\"'date'\" class=\"form-group mb-3\">\r\n <span\r\n *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string\r\n [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n {{ getLabel(formField) }}\r\n </label>\r\n\r\n <span>\r\n <div class=\"picker-field\">\r\n <p-datepicker\r\n #datePicker\r\n #dateField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n [showIcon]=\"false\"\r\n [showOnFocus]=\"false\"\r\n appendTo=\"body\"\r\n [autoZIndex]=\"true\"\r\n [baseZIndex]=\"10000\"\r\n [panelStyleClass]=\"picker.datePanelClass\"\r\n [motionOptions]=\"picker.dockedMotion\"\r\n [vlCustomKeyboard]=\"keyboardTypeFor(formField)\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, datePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"picker.onShown()\"\r\n (onClose)=\"picker.onClose(datePicker)\"\r\n (ngModelChange)=\"dateChangeEvent(formField, $event)\"\r\n (onInput)=\"onDateTyping($event)\"\r\n [dateFormat]=\"\r\n modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ]?.format || 'yy-mm-dd'\r\n \"\r\n [(ngModel)]=\"\r\n modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"formField?.scope?.style?.classes?.[0] || ''\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\">\r\n </p-datepicker>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"picker-field__icon\"\r\n aria-label=\"Open calendar\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n (pointerdown)=\"picker.openFromIcon($event, datePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-calendar\"></i>\r\n </button>\r\n </div>\r\n\r\n <div\r\n *ngIf=\"dateField?.touched && dateField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['required']\">\r\n {{ setErroMessage(formField,\r\n errorMessages, 'required') }}\r\n </p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dateField?.errors?.['email']\">\r\n {{\r\n (formField.label?.value\r\n ? formField.label?.value\r\n : modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption)\r\n + ' ' + errorMessages.pattern\r\n }}\r\n </p>\r\n </div>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Time control -->\r\n <div *ngSwitchCase=\"'time'\" class=\"form-group mb-3\">\r\n <span\r\n *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string\r\n [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n {{ getLabel(formField) }}\r\n </label>\r\n\r\n <span>\r\n <div class=\"picker-field\">\r\n <p-datepicker\r\n #timePicker\r\n #timeField=\"ngModel\"\r\n styleClass=\"custom-datepicker\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n [timeOnly]=\"true\"\r\n hourFormat=\"24\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n [showIcon]=\"false\"\r\n [showOnFocus]=\"false\"\r\n appendTo=\"body\"\r\n [autoZIndex]=\"true\"\r\n [baseZIndex]=\"10000\"\r\n [panelStyleClass]=\"picker.timePanelClass\"\r\n [vlCustomKeyboard]=\"keyboardTypeFor(formField)\"\r\n (pointerdown)=\"picker.onFieldPointerDown($event, timePicker)\"\r\n (onFocus)=\"picker.onFieldFocus($event)\"\r\n (onShow)=\"onTimePanelShow(formField)\"\r\n (onClose)=\"picker.onClose(timePicker)\"\r\n (onInput)=\"onTimeTyping($event)\"\r\n (ngModelChange)=\"timeChangeEvent(formField, $event)\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [(ngModel)]=\"\r\n modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ].value\r\n \"\r\n [class]=\"formField?.scope?.style?.classes?.[0] || ''\">\r\n </p-datepicker>\r\n\r\n <button\r\n type=\"button\"\r\n class=\"picker-field__icon\"\r\n aria-label=\"Open time picker\"\r\n [disabled]=\"formField?.option?.disabled\"\r\n (pointerdown)=\"picker.openFromIcon($event, timePicker)\"\r\n (click)=\"picker.blockIconClick($event)\">\r\n <i class=\"pi pi-clock\"></i>\r\n </button>\r\n </div>\r\n\r\n <div\r\n *ngIf=\"timeField?.touched && timeField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['required']\">\r\n {{ setErroMessage(formField,\r\n errorMessages, 'required') }}\r\n </p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"timeField?.errors?.['email']\">\r\n {{\r\n (formField.label?.value\r\n ? formField.label?.value\r\n : modalItemInfo.propertySchema[\r\n getScope(formField.scope?.reference)\r\n ][\r\n formField.scope?.reference.split('/').pop()\r\n ]?.caption)\r\n + ' ' + errorMessages.pattern\r\n }}\r\n </p>\r\n </div>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Autocomplete control -->\r\n <div *ngSwitchCase=\"'autocomplete'\"\r\n class=\"form-group mb-3\">\r\n <span *ngIf=\"formField.option.protected\"\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\"\r\n [class]=\"formField?.label?.style.classes ? formField?.label?.style?.classes[0] : ''\">\r\n <vl-string [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-string>\r\n </span>\r\n <div *ngIf=\"!formField.option.protected\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <vl-label [formField]=\"formField\"\r\n [modalItemInfo]=\"modalItemInfo\">\r\n </vl-label>\r\n\r\n <span>\r\n <p-autocomplete\r\n #autocompleteField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [name]=\"formField.scope?.reference\"\r\n styleClass=\"custom-autocomplete\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [style]=\"getStyleWithoutClasses(formField?.scope?.style).style\"\r\n [class]=\"formField?.scope?.style?.classes ? formField?.scope?.style?.classes[0] : ''\"\r\n [suggestions]=\"matchData\"\r\n [dropdown]=\"true\"\r\n [optionLabel]=\"formField?.option?.autocomplete?.response?.label ? formField.option.autocomplete.response.label : 'label'\"\r\n [placeholder]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.placeholder\"\r\n (completeMethod)=\"autocompChangeEvent($event, formField)\"\r\n (ngModelChange)=\"autocompFocusOutEvent($event, formField)\"\r\n [vlCustomKeyboard]=\"keyboardTypeFor(formField)\">\r\n </p-autocomplete>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"autocompleteField?.touched && autocompleteField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['required']\">\r\n {{setErroMessage(formField,\r\n errorMessages, \"required\")}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"autocompleteField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' +\r\n errorMessages.pattern}}</p>\r\n </div>\r\n </span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- checkBox control -->\r\n <div *ngSwitchCase=\"'boolean'\" class=\"form-group mb-3\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0] : ''\">\r\n <span *ngIf=\"formField.option?.postfixCaption\">\r\n <label class=\"form-check-label\"><input\r\n type=\"checkbox\" class=\"form-check-input\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"formField.scope?.reference\"\r\n (ngModelChange)=\"invokeSetformData()\" />{{\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n </span>\r\n <div *ngIf=\"!formField.option?.postfixCaption\"\r\n [class]=\"formField?.layout == 'horizontal' ? 'horizontal-layout' : 'row'\">\r\n <label class=\"form-check-label\">{{\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}\r\n <input type=\"checkbox\"\r\n class=\"form-check-input\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\"\r\n [name]=\"formField.scope?.reference\" /></label>\r\n </div>\r\n </div>\r\n\r\n <!-- dropdown control -->\r\n <div *ngSwitchCase=\"'dropdown'\" class=\"form-group mb-3\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0] : ''\">\r\n <span *ngIf=\"formField.option?.postfixCaption\">\r\n <select id=\"dynamic-dropdown\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\r\n #dropdownField=\"ngModel\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\">\r\n <option\r\n *ngFor=\"let item of modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.data\"\r\n [value]=\"formField?.option?.optionValue ? formField.option.optionValue : 'value'\">\r\n {{ formField?.option?.optionLabel ?\r\n formField.option.optionLabel :\r\n 'label'}}</option>\r\n </select>\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\">{{modalItemInfo.propertySchema[getScope(formField.scope)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n </span>\r\n <div *ngIf=\"!formField.option?.postfixCaption\">\r\n <div\r\n [ngStyle]=\"getFieldInnerLayoutStyle(formField)\">\r\n <label\r\n [style]=\"getStyleWithoutClasses(formField?.label?.style).style\">{{modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n }}</label>\r\n\r\n <nz-select nzShowSearch nzAllowClear\r\n nzPlaceHolder=\"Select\"\r\n #dropdownField=\"ngModel\"\r\n [required]=\"formField.option.required\"\r\n [class]=\"formField.style?.classes ? formField.style?.classes[0]: ''\"\r\n [style]=\"getStyleWithoutClasses(formField?.style).style\"\r\n [name]=\"formField.scope?.reference\"\r\n [(ngModel)]=\"modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()].value\">\r\n <nz-option\r\n *ngFor=\"let item of modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.data\"\r\n [nzLabel]=\"item[formField?.option?.optionLabel ? formField.option.optionLabel : 'label']\"\r\n [nzValue]=\"item[formField?.option?.optionValue ? formField.option.optionValue : 'value']\"></nz-option>\r\n </nz-select>\r\n </div>\r\n <!-- Error Message -->\r\n <div\r\n *ngIf=\"dropdownField?.touched && dropdownField?.invalid\"\r\n [style]=\"getStyleWithoutClasses(formField?.validation?.style).style\">\r\n <p class=\"text-danger\"\r\n *ngIf=\"dropdownField?.errors?.['required']\">\r\n {{formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption\r\n +\r\n ' ' + errorMessages.required}}</p>\r\n <p class=\"text-danger\"\r\n *ngIf=\"dropdownField?.errors?.['email']\">\r\n {{(formField.label?.value ?\r\n formField.label?.value :\r\n modalItemInfo.propertySchema[getScope(formField.scope?.reference)][formField.scope?.reference.split('/').pop()]?.caption)\r\n +\r\n ' ' + errorMessages.pattern}}</p>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- button control -->\r\n <div *ngSwitchCase=\"'default'\">\r\n <button type=\"button\"\r\n [ngStyle]=\"getStyleWithoutClasses(formField.style).style\"\r\n (click)=\"invokeFormBtnClick(formField.option, modalItemInfo.propertySchema, dynamicForm)\"\r\n class=\"btn btn-primary btn-block\">\r\n {{ getButtonCaption(formField) }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </form>\r\n</div>\r\n", styles: [":host ::ng-deep .custom-datepicker .p-datepicker-dropdown{height:100%;border-radius:4px}:host ::ng-deep .custom-datepicker .p-datepicker-input{border-radius:4px}::ng-deep .p-autocomplete-list{background-color:#fff}:host ::ng-deep .custom-autocomplete{display:flex;width:100%}:host ::ng-deep .custom-autocomplete .p-autocomplete-input{flex:1;width:100%;min-width:0;border:none!important;outline:none!important;box-shadow:none!important;background:transparent!important}:host ::ng-deep .custom-autocomplete .p-autocomplete-dropdown{height:100%;border-radius:4px}:host ::ng-deep .custom-autocomplete .p-autocomplete-input:focus,:host ::ng-deep .custom-autocomplete .p-autocomplete-input:focus-visible,:host ::ng-deep .custom-autocomplete .p-inputtext:enabled:focus{border:none!important;outline:none!important;box-shadow:none!important}.responsive-modal-grid{width:100%;min-width:0;box-sizing:border-box}@media screen and (max-width:767px){.responsive-modal-grid{grid-template-columns:repeat(12,minmax(0,1fr))!important;gap:10px!important}.responsive-modal-field{grid-column:1 / -1!important;width:100%!important;max-width:none!important;min-width:0!important}.responsive-modal-action:nth-last-child(2){grid-column:1 / span 6!important}.responsive-modal-action:last-child{grid-column:7 / span 6!important}.responsive-modal-action{width:100%!important;max-width:none!important;min-width:0!important;justify-self:stretch!important;box-sizing:border-box!important}.responsive-modal-action button{display:block!important;width:100%!important;max-width:none!important;min-width:0!important;margin:0!important;box-sizing:border-box!important}}:host{--modal-field-focus-color: var(--p-primary-color, #10b981);--modal-field-focus-shadow: 0 0 0 2px rgba(16, 185, 129, .16)}:host ::ng-deep input:not([type=checkbox]):not(:disabled):focus,:host ::ng-deep textarea:not(:disabled):focus{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .custom-autocomplete:focus-within{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .custom-autocomplete:focus-within .p-autocomplete-input{border:none!important;outline:none!important;box-shadow:none!important}:host ::ng-deep select:not(:disabled):focus{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .ant-select-focused:not(.ant-select-disabled) .ant-select-selector,:host ::ng-deep nz-select:not(.ant-select-disabled):focus-within .ant-select-selector{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .p-select:not(.p-disabled).p-focus,:host ::ng-deep .p-select:not(.p-disabled):focus-within,:host ::ng-deep .p-dropdown:not(.p-disabled).p-focus,:host ::ng-deep .p-dropdown:not(.p-disabled):focus-within{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep .custom-datepicker:focus-within .p-datepicker-input{border-color:var(--modal-field-focus-color)!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow)!important}:host ::ng-deep input:disabled,:host ::ng-deep textarea:disabled,:host ::ng-deep select:disabled,:host ::ng-deep .p-disabled,:host ::ng-deep .ant-select-disabled .ant-select-selector{box-shadow:none!important}\n"] }]
5196
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1$3.DynamicDialogRef }, { type: i1$3.DynamicDialogConfig }, { type: UtilityService }], propDecorators: { errorMessages: [{
4778
5197
  type: Input
4779
5198
  }], modalItemInfo: [{
4780
5199
  type: Input
@@ -4899,6 +5318,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
4899
5318
  args: [{ selector: 'vl-time', standalone: true, template: "<p>time works!</p>\r\n" }]
4900
5319
  }] });
4901
5320
 
5321
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07
5322
+ // - new: refreshTokens. The refresh token was stored after the login but never used, so an expired
5323
+ // access token always meant a new login. It now asks Keycloak for new tokens first; only when the
5324
+ // refresh token has expired too the user has to sign in again
5325
+ // - new: login asks for offline_access when login.mobile.offlineAccess is set, and signs in without
5326
+ // it if Keycloak refuses the scope
5327
+ // - change: the login no longer writes the token response and the user claims (which include
5328
+ // password fields) to the console, and it awaits storing the tokens before reloading; the logout
5329
+ // URL (it carries the id token) is not logged either
4902
5330
  class MobileKeycloakService {
4903
5331
  config;
4904
5332
  http;
@@ -4923,17 +5351,35 @@ class MobileKeycloakService {
4923
5351
  const cfg = this.config.getKeycloakMobileConfig();
4924
5352
  const discoveryUrl = `${cfg.issuer}/.well-known/openid-configuration`;
4925
5353
  const discovery = await (await fetch(discoveryUrl)).json();
4926
- const result = await OAuth2Client.authenticate({
5354
+ const authenticate = (scope) => OAuth2Client.authenticate({
4927
5355
  appId: cfg.clientId, // <--- Keycloak clientId goes here
4928
5356
  authorizationBaseUrl: discovery.authorization_endpoint,
4929
5357
  accessTokenEndpoint: discovery.token_endpoint,
4930
5358
  responseType: 'code',
4931
5359
  redirectUrl: cfg.redirectUri,
4932
- scope: cfg.scope,
5360
+ scope,
4933
5361
  pkceEnabled: true,
4934
- logsEnabled: true
5362
+ logsEnabled: false
4935
5363
  });
4936
- console.log('result-KEyClaok: ', result);
5364
+ /*
5365
+ * With login.mobile.offlineAccess the app asks for an offline token: the sign-in then lasts as
5366
+ * long as the app is used at least once per "Offline Session Idle" (30 days in realm p5)
5367
+ * instead of the 5 days of a normal refresh token. If Keycloak refuses the scope - role or
5368
+ * client scope removed - the login is repeated without it rather than failing.
5369
+ */
5370
+ const offlineScope = this.withOfflineAccess(cfg.scope);
5371
+ let result;
5372
+ try {
5373
+ result = await authenticate(cfg.offlineAccess ? offlineScope : cfg.scope);
5374
+ }
5375
+ catch (error) {
5376
+ const cancelled = /USER_CANCELLED/i.test(String(error?.message ?? error));
5377
+ if (!cfg.offlineAccess || cancelled) {
5378
+ throw error;
5379
+ }
5380
+ console.warn('Keycloak refused offline_access, signing in without it');
5381
+ result = await authenticate(cfg.scope);
5382
+ }
4937
5383
  this.tokens = {
4938
5384
  access_token: result?.access_token_response?.access_token ? result?.access_token_response?.access_token : result.access_token,
4939
5385
  refresh_token: result?.access_token_response?.refresh_token,
@@ -4944,10 +5390,10 @@ class MobileKeycloakService {
4944
5390
  let keycloakLoginUser = "";
4945
5391
  if (this.tokens.access_token) {
4946
5392
  keycloakLoginUser = await this.getUserInfo(this.tokens.access_token, cfg.issuer);
4947
- console.log('keycloakLoginUser: ', keycloakLoginUser);
4948
5393
  if (keycloakLoginUser) {
4949
5394
  this.tokens['issuer'] = cfg.issuer;
4950
- this.utilityService.setLocalStorage("KeycloakInfo", this.tokens);
5395
+ // Awaited: the reload below must not cut off the (asynchronous) keychain write.
5396
+ await this.utilityService.setLocalStorage("KeycloakInfo", this.tokens);
4951
5397
  if (this.platform.ANDROID || this.platform.IOS) {
4952
5398
  window.location.reload();
4953
5399
  }
@@ -4955,6 +5401,57 @@ class MobileKeycloakService {
4955
5401
  }
4956
5402
  return keycloakLoginUser;
4957
5403
  }
5404
+ /** The configured scope plus offline_access, without duplicating it. */
5405
+ withOfflineAccess(scope) {
5406
+ const parts = (scope ?? 'openid').split(/\s+/).filter(Boolean);
5407
+ if (!parts.includes('offline_access')) {
5408
+ parts.push('offline_access');
5409
+ }
5410
+ return parts.join(' ');
5411
+ }
5412
+ /**
5413
+ * New tokens from the refresh token (grant_type=refresh_token). Returns them in the stored token
5414
+ * shape, or null when Keycloak refuses - then the caller falls back to the login.
5415
+ */
5416
+ async refreshTokens(stored) {
5417
+ const cfg = this.config.getKeycloakMobileConfig();
5418
+ const issuer = stored?.issuer || cfg?.issuer;
5419
+ if (!stored?.refresh_token || !issuer || !cfg?.clientId) {
5420
+ return null;
5421
+ }
5422
+ try {
5423
+ const discovery = await (await fetch(`${issuer}/.well-known/openid-configuration`)).json();
5424
+ const response = await fetch(discovery.token_endpoint, {
5425
+ method: 'POST',
5426
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
5427
+ body: new URLSearchParams({
5428
+ grant_type: 'refresh_token',
5429
+ refresh_token: stored.refresh_token,
5430
+ client_id: cfg.clientId
5431
+ }).toString()
5432
+ });
5433
+ if (!response.ok) {
5434
+ return null;
5435
+ }
5436
+ const result = await response.json();
5437
+ if (!result?.access_token) {
5438
+ return null;
5439
+ }
5440
+ this.tokens = {
5441
+ access_token: result.access_token,
5442
+ refresh_token: result.refresh_token ?? stored.refresh_token,
5443
+ id_token: result.id_token ?? stored.id_token,
5444
+ expires_in: result.expires_in,
5445
+ token_type: result.token_type,
5446
+ issuer
5447
+ };
5448
+ return this.tokens;
5449
+ }
5450
+ catch (error) {
5451
+ console.warn('Keycloak token refresh failed');
5452
+ return null;
5453
+ }
5454
+ }
4958
5455
  async getUserInfo(accessToken, issuer) {
4959
5456
  try {
4960
5457
  const headers = new HttpHeaders({
@@ -4987,7 +5484,6 @@ class MobileKeycloakService {
4987
5484
  `?id_token_hint=${encodeURIComponent(idToken)}` +
4988
5485
  `&client_id=${encodeURIComponent(cfg.clientId)}` +
4989
5486
  `&post_logout_redirect_uri=${encodeURIComponent(cfg.logoutRedirectUri)}`;
4990
- console.log('Logout URL:', endSessionUrl);
4991
5487
  await Browser.open({ url: endSessionUrl });
4992
5488
  this.tokens = null;
4993
5489
  await this.utilityService.removeLocalStorage('KeycloakInfo');
@@ -5003,6 +5499,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
5003
5499
  }]
5004
5500
  }], ctorParameters: () => [{ type: ConfigService }, { type: i1$1.HttpClient }, { type: UtilityService }, { type: i1.Platform }] });
5005
5501
 
5502
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07
5503
+ // - change: menu items that arrive after the language was loaded are translated too; with the
5504
+ // Face ID lock the items came later and the menu stayed English
5505
+ // - new: languageChange carries languageCode next to the translations. The custom keyboards
5506
+ // need the language to show its letters (ä ö ü ß for German); the translation file alone
5507
+ // does not say which language it is
5006
5508
  // Shuaib 2026-09-24
5007
5509
  // - new: derive the signed-in user from a JWT for jwtToken projects.
5008
5510
  // - change: pass the selected project's Okta issuer to getLoginUser.
@@ -5027,6 +5529,8 @@ class SideMenuComponent {
5027
5529
  menuItemsCopy = [];
5028
5530
  settingMenu = {};
5029
5531
  setLanguage = false;
5532
+ /** Translations of the last language change, for menu items that arrive after it. */
5533
+ lastMenuTranslations = null;
5030
5534
  tenantKey = '';
5031
5535
  scrollPagination = false;
5032
5536
  oktaAuthStateSubscription;
@@ -5212,6 +5716,14 @@ class SideMenuComponent {
5212
5716
  this.menuItems?.length) {
5213
5717
  this.menuItemsCopy =
5214
5718
  JSON.parse(JSON.stringify(this.menuItems));
5719
+ /*
5720
+ * The language can be loaded before the menu items arrive - e.g. while the app waits for
5721
+ * Face ID - and then had nothing to translate: the menu stayed English while the forms
5722
+ * were German. Apply the last loaded language to items that come later.
5723
+ */
5724
+ if (this.lastMenuTranslations) {
5725
+ this.applyMenuTranslations(this.lastMenuTranslations);
5726
+ }
5215
5727
  void this.setProjectSetting();
5216
5728
  }
5217
5729
  if (changes['settings'] ||
@@ -5691,9 +6203,12 @@ class SideMenuComponent {
5691
6203
  }
5692
6204
  this.languageChange.emit({
5693
6205
  selectedLanguage,
5694
- errorMessages
6206
+ errorMessages,
6207
+ // The custom keyboards pick their letter layout from it ("de.json" -> "de").
6208
+ languageCode: lang.replace(/\.json$/i, '').split(/[-_]/)[0].toLowerCase()
5695
6209
  });
5696
6210
  this.setLanguage = true;
6211
+ this.lastMenuTranslations = selectedLanguage;
5697
6212
  this.applyMenuTranslations(selectedLanguage);
5698
6213
  this.applySettingTranslations(selectedLanguage);
5699
6214
  }
@@ -5952,22 +6467,104 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
5952
6467
  }]
5953
6468
  }], ctorParameters: () => [{ type: i1.Platform }] });
5954
6469
 
6470
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07
6471
+ // - new: letter layouts per language and a symbol page for the custom keyboards. The keyboard had
6472
+ // one hard-wired QWERTY without umlauts or punctuation, so German text such as a comment could
6473
+ // not be typed. A project can bring its own layout in its language file (keyboardLayout)
6474
+ const LETTER_LAYOUTS = {
6475
+ en: [
6476
+ ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'],
6477
+ ['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l'],
6478
+ ['z', 'x', 'c', 'v', 'b', 'n', 'm']
6479
+ ],
6480
+ de: [
6481
+ ['q', 'w', 'e', 'r', 't', 'z', 'u', 'i', 'o', 'p', 'ü'],
6482
+ ['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ö', 'ä'],
6483
+ ['y', 'x', 'c', 'v', 'b', 'n', 'm', 'ß']
6484
+ ],
6485
+ fr: [
6486
+ ['a', 'z', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'],
6487
+ ['q', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm'],
6488
+ ['w', 'x', 'c', 'v', 'b', 'n', 'é', 'è', 'à', 'ç']
6489
+ ],
6490
+ es: [
6491
+ ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'],
6492
+ ['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'ñ'],
6493
+ ['z', 'x', 'c', 'v', 'b', 'n', 'm', 'á', 'é', 'ó']
6494
+ ],
6495
+ it: [
6496
+ ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'],
6497
+ ['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'à'],
6498
+ ['z', 'x', 'c', 'v', 'b', 'n', 'm', 'è', 'ò', 'ù']
6499
+ ]
6500
+ };
6501
+ const DIGIT_ROW = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'];
6502
+ /** Second page behind the ?123 key: everything a free text needs besides letters. */
6503
+ const SYMBOL_ROWS = [
6504
+ DIGIT_ROW,
6505
+ ['-', '/', ':', ';', '(', ')', '€', '&', '@', '"'],
6506
+ ['.', ',', '?', '!', "'", '#', '%', '+', '=', '_'],
6507
+ ['*', '$', '§', '°', '<', '>', '[', ']', '\\', '~']
6508
+ ];
6509
+ /**
6510
+ * Letter rows for a language. A layout the project defines wins: its language file may carry
6511
+ * keyboardLayout as rows of keys, either arrays or strings ("qwertzuiopü").
6512
+ */
6513
+ function getLetterRows(languageCode, projectLayout) {
6514
+ const fromProject = normalizeProjectLayout(projectLayout);
6515
+ if (fromProject) {
6516
+ return fromProject;
6517
+ }
6518
+ const code = (languageCode ?? '').toLowerCase().split(/[-_]/)[0];
6519
+ return LETTER_LAYOUTS[code] ?? LETTER_LAYOUTS['en'];
6520
+ }
6521
+ function normalizeProjectLayout(layout) {
6522
+ if (!Array.isArray(layout) || layout.length === 0) {
6523
+ return null;
6524
+ }
6525
+ const rows = layout
6526
+ .map(row => typeof row === 'string'
6527
+ ? Array.from(row)
6528
+ : Array.isArray(row)
6529
+ ? row.filter(key => typeof key === 'string' && key.length > 0)
6530
+ : [])
6531
+ .map(row => row.map(key => key.toLowerCase()))
6532
+ .filter(row => row.length > 0);
6533
+ return rows.length > 0 ? rows : null;
6534
+ }
6535
+
6536
+ // Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07
6537
+ // - change: the letter rows follow the project language (QWERTZ with ä ö ü ß for German) instead
6538
+ // of one fixed QWERTY; the language comes from the project's language selection
6539
+ // - new: a ?123 page with digits and punctuation for string fields, so a comment can hold
6540
+ // "Termin 14:30, Raum 2-B." - before only A-Z, 0-9 and space could be entered
5955
6541
  class CustomKeyboardComponent {
5956
6542
  keyboard = inject(CustomKeyboardService);
5957
6543
  uppercase = signal(true, ...(ngDevMode ? [{ debugName: "uppercase" }] : /* istanbul ignore next */ []));
5958
- digitRow = [
5959
- '1', '2', '3', '4', '5',
5960
- '6', '7', '8', '9', '0'
5961
- ];
5962
- letterRows = [
5963
- ['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'],
5964
- ['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L'],
5965
- ['Z', 'X', 'C', 'V', 'B', 'N', 'M']
5966
- ];
6544
+ /** true while the ?123 page is shown instead of the letters. */
6545
+ symbols = signal(false, ...(ngDevMode ? [{ debugName: "symbols" }] : /* istanbul ignore next */ []));
6546
+ languageCode = signal(null, ...(ngDevMode ? [{ debugName: "languageCode" }] : /* istanbul ignore next */ []));
6547
+ projectLayout = signal(null, ...(ngDevMode ? [{ debugName: "projectLayout" }] : /* istanbul ignore next */ []));
6548
+ constructor() {
6549
+ inject(UtilityService)
6550
+ .currentProjectlanguage$
6551
+ .pipe(takeUntilDestroyed(inject(DestroyRef)))
6552
+ .subscribe((uiLang) => {
6553
+ if (!uiLang) {
6554
+ return;
6555
+ }
6556
+ this.languageCode.set(uiLang.languageCode ?? null);
6557
+ this.projectLayout.set(uiLang.selectedLanguage?.keyboardLayout ?? null);
6558
+ });
6559
+ }
5967
6560
  get keyboardType() {
5968
6561
  return (this.keyboard.activeSession()?.type ??
5969
6562
  null);
5970
6563
  }
6564
+ /** The symbol page exists where free text is typed. */
6565
+ get hasSymbolPage() {
6566
+ return this.keyboardType === 'string';
6567
+ }
5971
6568
  get rows() {
5972
6569
  const type = this.keyboardType;
5973
6570
  if (!type) {
@@ -5990,29 +6587,33 @@ class CustomKeyboardComponent {
5990
6587
  ['0']
5991
6588
  ];
5992
6589
  }
5993
- const letterRows = this.letterRows.map(row => row.map(character => {
5994
- if (type === 'email') {
5995
- return character.toLowerCase();
5996
- }
5997
- return this.uppercase()
5998
- ? character
5999
- : character.toLowerCase();
6000
- }));
6001
6590
  if (type === 'email') {
6002
6591
  return [
6003
- this.digitRow,
6004
- ...letterRows,
6592
+ DIGIT_ROW,
6593
+ ...getLetterRows('en'),
6005
6594
  ['@', '.', '_', '-', '+']
6006
6595
  ];
6007
6596
  }
6597
+ if (this.hasSymbolPage && this.symbols()) {
6598
+ return SYMBOL_ROWS;
6599
+ }
6600
+ const letterRows = getLetterRows(this.languageCode(), this.projectLayout())
6601
+ .map(row => row.map(character => this.uppercase()
6602
+ ? this.toUpper(character)
6603
+ : character));
6008
6604
  if (type === 'string') {
6009
6605
  return [
6010
- this.digitRow,
6606
+ DIGIT_ROW,
6011
6607
  ...letterRows
6012
6608
  ];
6013
6609
  }
6014
6610
  return letterRows;
6015
6611
  }
6612
+ /** ß has no single-letter capital in common use ("SS"), so it keeps its form. */
6613
+ toUpper(character) {
6614
+ const upper = character.toLocaleUpperCase(this.languageCode() ?? undefined);
6615
+ return upper.length === 1 ? upper : character;
6616
+ }
6016
6617
  enterKey(key) {
6017
6618
  this.keyboard.insert(key);
6018
6619
  }
@@ -6022,13 +6623,16 @@ class CustomKeyboardComponent {
6022
6623
  toggleCase() {
6023
6624
  this.uppercase.update(value => !value);
6024
6625
  }
6626
+ toggleSymbols() {
6627
+ this.symbols.update(value => !value);
6628
+ }
6025
6629
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: CustomKeyboardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6026
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.2", type: CustomKeyboardComponent, isStandalone: true, selector: "vl-custom-keyboard", ngImport: i0, template: "@if (keyboard.activeSession(); as session) {\r\n<section class=\"custom-keyboard\" data-vl-custom-keyboard [class.custom-keyboard--numeric]=\"\r\n session.type === 'numeric' ||\r\n session.type === 'date' ||\r\n session.type === 'time'\" [class.custom-keyboard--opening]=\"keyboard.interactionBlocked()\"\r\n [attr.aria-label]=\"session.type + ' keyboard'\" (pointerdown)=\"$event.preventDefault()\">\r\n\r\n @for (row of rows; track $index) {\r\n <div class=\"custom-keyboard__row\">\r\n @for (key of row; track key) {\r\n <button type=\"button\" class=\"custom-keyboard__key\" (click)=\"enterKey(key)\">\r\n {{ key }}\r\n </button>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- One mobile-style action row for every keyboard type -->\r\n <div class=\"custom-keyboard__actions\" [class.custom-keyboard__actions--compact]=\"\r\n session.type !== 'alpha' &&\r\n session.type !== 'string'\r\n \">\r\n\r\n @if (\r\n session.type === 'alpha' ||\r\n session.type === 'string'\r\n ) {\r\n <button type=\"button\" class=\"custom-keyboard__action-button custom-keyboard__shift\" [class.active]=\"uppercase()\"\r\n aria-label=\"Change letter case\" title=\"Shift\" (click)=\"toggleCase()\">\r\n <i class=\"pi pi-arrow-up\"></i>\r\n </button>\r\n\r\n <button type=\"button\" class=\"custom-keyboard__action-button custom-keyboard__space\" (click)=\"enterSpace()\">\r\n Space\r\n </button>\r\n }\r\n\r\n <button type=\"button\" class=\"custom-keyboard__action-button custom-keyboard__delete\"\r\n aria-label=\"Delete previous character\" title=\"Delete\" (click)=\"keyboard.backspace()\">\r\n <i class=\"pi pi-delete-left\"></i>\r\n </button>\r\n\r\n <button type=\"button\" class=\"custom-keyboard__action-button custom-keyboard__done\" aria-label=\"Done\"\r\n title=\"Done\" (click)=\"keyboard.close()\">\r\n <i class=\"pi pi-check\"></i>\r\n </button>\r\n </div>\r\n</section>\r\n}", styles: [":host{display:contents}.custom-keyboard{position:fixed;right:0;bottom:0;left:0;z-index:200000;display:block;width:100%;max-height:min(52dvh,390px);padding:8px 6px calc(8px + env(safe-area-inset-bottom,0px));background:#d1d5db;box-shadow:0 -6px 20px #0f172a38;box-sizing:border-box;overflow-x:hidden;overflow-y:auto;user-select:none;-webkit-user-select:none;touch-action:manipulation;-webkit-overflow-scrolling:touch}.custom-keyboard__row{display:flex;justify-content:center;gap:4px;width:100%;margin-bottom:6px}.custom-keyboard__key{display:flex;flex:1 1 0;width:auto;max-width:52px;min-width:0;height:42px;align-items:center;justify-content:center;padding:0;color:#111827;font-size:16px;font-weight:700;line-height:1;background:#fff;border:0;border-radius:5px;box-shadow:0 1px 2px #0f172a47;cursor:pointer;appearance:none;-webkit-appearance:none;touch-action:manipulation;transition:transform 80ms ease,background-color 80ms ease}.custom-keyboard__key:active{background:#e6f4ff;transform:scale(.96)}.custom-keyboard--numeric .custom-keyboard__key{max-width:92px;height:46px;font-size:18px}.custom-keyboard__actions{display:grid;grid-template-columns:46px minmax(90px,1fr) 46px 54px;gap:6px;width:100%;margin-top:4px;padding:0 4px}.custom-keyboard__actions--compact{grid-template-columns:minmax(52px,1fr) minmax(52px,1fr)}.custom-keyboard__action-button{display:flex;width:100%;min-width:0;height:42px;align-items:center;justify-content:center;padding:0 8px;color:#172033;font-size:15px;font-weight:700;line-height:1;border:0;border-radius:6px;box-shadow:0 1px 2px #0f172a40;cursor:pointer;appearance:none;-webkit-appearance:none;touch-action:manipulation;transition:transform 80ms ease,background-color 80ms ease}.custom-keyboard__action-button i{font-size:17px;line-height:1;pointer-events:none}.custom-keyboard__action-button:active{transform:scale(.96)}.custom-keyboard__shift{color:#172033;background:#aeb6c2}.custom-keyboard__shift.active{color:#fff;background:#64748b}.custom-keyboard__space{color:#172033;background:#fff}.custom-keyboard__delete{color:#172033;background:#aeb6c2}.custom-keyboard__done{color:#fff;background:#1890ff}.custom-keyboard__key:focus-visible,.custom-keyboard__action-button:focus-visible{outline:3px solid rgba(24,144,255,.35);outline-offset:1px}@media(max-height:650px){.custom-keyboard{max-height:58dvh;padding-top:6px}.custom-keyboard__row{gap:3px;margin-bottom:4px}.custom-keyboard__key{height:35px;font-size:14px}.custom-keyboard--numeric .custom-keyboard__key{height:38px;font-size:16px}.custom-keyboard__action-button{height:36px}}@media(max-width:360px){.custom-keyboard{padding-right:4px;padding-left:4px}.custom-keyboard__row{gap:3px}.custom-keyboard__key{font-size:14px}.custom-keyboard__actions{grid-template-columns:42px minmax(70px,1fr) 42px 48px;gap:4px;padding-right:2px;padding-left:2px}.custom-keyboard__actions--compact{grid-template-columns:minmax(48px,1fr) minmax(48px,1fr)}}.custom-keyboard--opening .custom-keyboard__key,.custom-keyboard--opening .custom-keyboard__action-button{pointer-events:none}.custom-keyboard--opening{cursor:default}\n"] });
6630
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.2", type: CustomKeyboardComponent, isStandalone: true, selector: "vl-custom-keyboard", ngImport: i0, template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\r\n - new: a ?123 / ABC key switches string keyboards between letters and the symbol page -->\r\n@if (keyboard.activeSession(); as session) {\r\n<section class=\"custom-keyboard\" data-vl-custom-keyboard [class.custom-keyboard--numeric]=\"\r\n session.type === 'numeric' ||\r\n session.type === 'date' ||\r\n session.type === 'time'\" [class.custom-keyboard--opening]=\"keyboard.interactionBlocked()\"\r\n [attr.aria-label]=\"session.type + ' keyboard'\" (pointerdown)=\"$event.preventDefault()\">\r\n\r\n @for (row of rows; track $index) {\r\n <div class=\"custom-keyboard__row\">\r\n @for (key of row; track key) {\r\n <button type=\"button\" class=\"custom-keyboard__key\" (click)=\"enterKey(key)\">\r\n {{ key }}\r\n </button>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- One mobile-style action row for every keyboard type -->\r\n <div class=\"custom-keyboard__actions\" [class.custom-keyboard__actions--compact]=\"\r\n session.type !== 'alpha' &&\r\n session.type !== 'string'\r\n \" [class.custom-keyboard__actions--symbols]=\"hasSymbolPage\">\r\n\r\n @if (\r\n session.type === 'alpha' ||\r\n session.type === 'string'\r\n ) {\r\n <button type=\"button\" class=\"custom-keyboard__action-button custom-keyboard__shift\" [class.active]=\"uppercase()\"\r\n [disabled]=\"symbols()\" aria-label=\"Change letter case\" title=\"Shift\" (click)=\"toggleCase()\">\r\n <i class=\"pi pi-arrow-up\"></i>\r\n </button>\r\n\r\n @if (hasSymbolPage) {\r\n <button type=\"button\" class=\"custom-keyboard__action-button custom-keyboard__page\"\r\n [attr.aria-label]=\"symbols() ? 'Letters' : 'Digits and symbols'\" (click)=\"toggleSymbols()\">\r\n {{ symbols() ? 'ABC' : '?123' }}\r\n </button>\r\n }\r\n\r\n <button type=\"button\" class=\"custom-keyboard__action-button custom-keyboard__space\" (click)=\"enterSpace()\">\r\n Space\r\n </button>\r\n }\r\n\r\n <button type=\"button\" class=\"custom-keyboard__action-button custom-keyboard__delete\"\r\n aria-label=\"Delete previous character\" title=\"Delete\" (click)=\"keyboard.backspace()\">\r\n <i class=\"pi pi-delete-left\"></i>\r\n </button>\r\n\r\n <button type=\"button\" class=\"custom-keyboard__action-button custom-keyboard__done\" aria-label=\"Done\"\r\n title=\"Done\" (click)=\"keyboard.close()\">\r\n <i class=\"pi pi-check\"></i>\r\n </button>\r\n </div>\r\n</section>\r\n}\r\n", styles: [":host{display:contents}.custom-keyboard{position:fixed;right:0;bottom:0;left:0;z-index:200000;display:block;width:100%;max-height:min(52dvh,390px);padding:8px 6px calc(8px + env(safe-area-inset-bottom,0px));background:#d1d5db;box-shadow:0 -6px 20px #0f172a38;box-sizing:border-box;overflow-x:hidden;overflow-y:auto;user-select:none;-webkit-user-select:none;touch-action:manipulation;-webkit-overflow-scrolling:touch}.custom-keyboard__row{display:flex;justify-content:center;gap:4px;width:100%;margin-bottom:6px}.custom-keyboard__key{display:flex;flex:1 1 0;width:auto;max-width:52px;min-width:0;height:42px;align-items:center;justify-content:center;padding:0;color:#111827;font-size:16px;font-weight:700;line-height:1;background:#fff;border:0;border-radius:5px;box-shadow:0 1px 2px #0f172a47;cursor:pointer;appearance:none;-webkit-appearance:none;touch-action:manipulation;transition:transform 80ms ease,background-color 80ms ease}.custom-keyboard__key:active{background:#e6f4ff;transform:scale(.96)}.custom-keyboard--numeric .custom-keyboard__key{max-width:92px;height:46px;font-size:18px}.custom-keyboard__actions{display:grid;grid-template-columns:46px minmax(90px,1fr) 46px 54px;gap:6px;width:100%;margin-top:4px;padding:0 4px}.custom-keyboard__actions--compact{grid-template-columns:minmax(52px,1fr) minmax(52px,1fr)}.custom-keyboard__actions--symbols{grid-template-columns:46px 54px minmax(70px,1fr) 46px 54px}.custom-keyboard__action-button{display:flex;width:100%;min-width:0;height:42px;align-items:center;justify-content:center;padding:0 8px;color:#172033;font-size:15px;font-weight:700;line-height:1;border:0;border-radius:6px;box-shadow:0 1px 2px #0f172a40;cursor:pointer;appearance:none;-webkit-appearance:none;touch-action:manipulation;transition:transform 80ms ease,background-color 80ms ease}.custom-keyboard__action-button i{font-size:17px;line-height:1;pointer-events:none}.custom-keyboard__action-button:active{transform:scale(.96)}.custom-keyboard__shift{color:#172033;background:#aeb6c2}.custom-keyboard__shift.active{color:#fff;background:#64748b}.custom-keyboard__space{color:#172033;background:#fff}.custom-keyboard__delete{color:#172033;background:#aeb6c2}.custom-keyboard__done{color:#fff;background:#1890ff}.custom-keyboard__page{color:#172033;font-size:14px;background:#aeb6c2}.custom-keyboard__shift:disabled{opacity:.45}.custom-keyboard__key:focus-visible,.custom-keyboard__action-button:focus-visible{outline:3px solid rgba(24,144,255,.35);outline-offset:1px}@media(max-height:650px){.custom-keyboard{max-height:58dvh;padding-top:6px}.custom-keyboard__row{gap:3px;margin-bottom:4px}.custom-keyboard__key{height:35px;font-size:14px}.custom-keyboard--numeric .custom-keyboard__key{height:38px;font-size:16px}.custom-keyboard__action-button{height:36px}}@media(max-width:360px){.custom-keyboard{padding-right:4px;padding-left:4px}.custom-keyboard__row{gap:3px}.custom-keyboard__key{font-size:14px}.custom-keyboard__actions{grid-template-columns:42px minmax(70px,1fr) 42px 48px;gap:4px;padding-right:2px;padding-left:2px}.custom-keyboard__actions--compact{grid-template-columns:minmax(48px,1fr) minmax(48px,1fr)}.custom-keyboard__actions--symbols{grid-template-columns:40px 48px minmax(56px,1fr) 40px 46px}}.custom-keyboard--opening .custom-keyboard__key,.custom-keyboard--opening .custom-keyboard__action-button{pointer-events:none}.custom-keyboard--opening{cursor:default}::ng-deep .picker-field{position:relative}::ng-deep .picker-field__icon{position:absolute;right:0;top:0;height:100%;width:2.5rem;border:0;background:transparent;cursor:pointer}::ng-deep .picker-field--active input{border-color:var(--modal-field-focus-color, var(--p-primary-color, #10b981))!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow, 0 0 0 2px rgba(16, 185, 129, .16))!important}::ng-deep .p-datepicker-panel.model-datepicker-panel--docked,::ng-deep .model-datepicker-panel--docked{position:fixed!important;inset:auto 0 0!important;width:100%!important;min-width:100%!important;max-width:100%!important;max-height:min(52dvh,420px)!important;margin:0!important;padding-bottom:env(safe-area-inset-bottom,0px)!important;overflow-y:auto!important;transform:none!important;border-radius:12px 12px 0 0!important;box-shadow:0 -6px 20px #0f172a38!important;box-sizing:border-box!important}::ng-deep .model-timepicker-panel.p-datepicker-panel:not(.model-datepicker-panel--docked){width:auto!important;min-width:0!important;padding:4px 8px!important;border-radius:8px!important}::ng-deep .model-timepicker-panel .p-datepicker-time-picker{gap:4px!important;padding:0!important;border:0!important}::ng-deep .model-timepicker-panel .p-datepicker-hour-picker,::ng-deep .model-timepicker-panel .p-datepicker-minute-picker{gap:0!important}::ng-deep .model-timepicker-panel .p-datepicker-hour-picker span,::ng-deep .model-timepicker-panel .p-datepicker-minute-picker span,::ng-deep .model-timepicker-panel .p-datepicker-separator{font-size:1rem!important;line-height:1.2!important}::ng-deep .model-timepicker-panel .p-datepicker-increment-button,::ng-deep .model-timepicker-panel .p-datepicker-decrement-button{width:2rem!important;height:1.75rem!important}\n"] });
6027
6631
  }
6028
6632
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: CustomKeyboardComponent, decorators: [{
6029
6633
  type: Component,
6030
- args: [{ selector: 'vl-custom-keyboard', standalone: true, template: "@if (keyboard.activeSession(); as session) {\r\n<section class=\"custom-keyboard\" data-vl-custom-keyboard [class.custom-keyboard--numeric]=\"\r\n session.type === 'numeric' ||\r\n session.type === 'date' ||\r\n session.type === 'time'\" [class.custom-keyboard--opening]=\"keyboard.interactionBlocked()\"\r\n [attr.aria-label]=\"session.type + ' keyboard'\" (pointerdown)=\"$event.preventDefault()\">\r\n\r\n @for (row of rows; track $index) {\r\n <div class=\"custom-keyboard__row\">\r\n @for (key of row; track key) {\r\n <button type=\"button\" class=\"custom-keyboard__key\" (click)=\"enterKey(key)\">\r\n {{ key }}\r\n </button>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- One mobile-style action row for every keyboard type -->\r\n <div class=\"custom-keyboard__actions\" [class.custom-keyboard__actions--compact]=\"\r\n session.type !== 'alpha' &&\r\n session.type !== 'string'\r\n \">\r\n\r\n @if (\r\n session.type === 'alpha' ||\r\n session.type === 'string'\r\n ) {\r\n <button type=\"button\" class=\"custom-keyboard__action-button custom-keyboard__shift\" [class.active]=\"uppercase()\"\r\n aria-label=\"Change letter case\" title=\"Shift\" (click)=\"toggleCase()\">\r\n <i class=\"pi pi-arrow-up\"></i>\r\n </button>\r\n\r\n <button type=\"button\" class=\"custom-keyboard__action-button custom-keyboard__space\" (click)=\"enterSpace()\">\r\n Space\r\n </button>\r\n }\r\n\r\n <button type=\"button\" class=\"custom-keyboard__action-button custom-keyboard__delete\"\r\n aria-label=\"Delete previous character\" title=\"Delete\" (click)=\"keyboard.backspace()\">\r\n <i class=\"pi pi-delete-left\"></i>\r\n </button>\r\n\r\n <button type=\"button\" class=\"custom-keyboard__action-button custom-keyboard__done\" aria-label=\"Done\"\r\n title=\"Done\" (click)=\"keyboard.close()\">\r\n <i class=\"pi pi-check\"></i>\r\n </button>\r\n </div>\r\n</section>\r\n}", styles: [":host{display:contents}.custom-keyboard{position:fixed;right:0;bottom:0;left:0;z-index:200000;display:block;width:100%;max-height:min(52dvh,390px);padding:8px 6px calc(8px + env(safe-area-inset-bottom,0px));background:#d1d5db;box-shadow:0 -6px 20px #0f172a38;box-sizing:border-box;overflow-x:hidden;overflow-y:auto;user-select:none;-webkit-user-select:none;touch-action:manipulation;-webkit-overflow-scrolling:touch}.custom-keyboard__row{display:flex;justify-content:center;gap:4px;width:100%;margin-bottom:6px}.custom-keyboard__key{display:flex;flex:1 1 0;width:auto;max-width:52px;min-width:0;height:42px;align-items:center;justify-content:center;padding:0;color:#111827;font-size:16px;font-weight:700;line-height:1;background:#fff;border:0;border-radius:5px;box-shadow:0 1px 2px #0f172a47;cursor:pointer;appearance:none;-webkit-appearance:none;touch-action:manipulation;transition:transform 80ms ease,background-color 80ms ease}.custom-keyboard__key:active{background:#e6f4ff;transform:scale(.96)}.custom-keyboard--numeric .custom-keyboard__key{max-width:92px;height:46px;font-size:18px}.custom-keyboard__actions{display:grid;grid-template-columns:46px minmax(90px,1fr) 46px 54px;gap:6px;width:100%;margin-top:4px;padding:0 4px}.custom-keyboard__actions--compact{grid-template-columns:minmax(52px,1fr) minmax(52px,1fr)}.custom-keyboard__action-button{display:flex;width:100%;min-width:0;height:42px;align-items:center;justify-content:center;padding:0 8px;color:#172033;font-size:15px;font-weight:700;line-height:1;border:0;border-radius:6px;box-shadow:0 1px 2px #0f172a40;cursor:pointer;appearance:none;-webkit-appearance:none;touch-action:manipulation;transition:transform 80ms ease,background-color 80ms ease}.custom-keyboard__action-button i{font-size:17px;line-height:1;pointer-events:none}.custom-keyboard__action-button:active{transform:scale(.96)}.custom-keyboard__shift{color:#172033;background:#aeb6c2}.custom-keyboard__shift.active{color:#fff;background:#64748b}.custom-keyboard__space{color:#172033;background:#fff}.custom-keyboard__delete{color:#172033;background:#aeb6c2}.custom-keyboard__done{color:#fff;background:#1890ff}.custom-keyboard__key:focus-visible,.custom-keyboard__action-button:focus-visible{outline:3px solid rgba(24,144,255,.35);outline-offset:1px}@media(max-height:650px){.custom-keyboard{max-height:58dvh;padding-top:6px}.custom-keyboard__row{gap:3px;margin-bottom:4px}.custom-keyboard__key{height:35px;font-size:14px}.custom-keyboard--numeric .custom-keyboard__key{height:38px;font-size:16px}.custom-keyboard__action-button{height:36px}}@media(max-width:360px){.custom-keyboard{padding-right:4px;padding-left:4px}.custom-keyboard__row{gap:3px}.custom-keyboard__key{font-size:14px}.custom-keyboard__actions{grid-template-columns:42px minmax(70px,1fr) 42px 48px;gap:4px;padding-right:2px;padding-left:2px}.custom-keyboard__actions--compact{grid-template-columns:minmax(48px,1fr) minmax(48px,1fr)}}.custom-keyboard--opening .custom-keyboard__key,.custom-keyboard--opening .custom-keyboard__action-button{pointer-events:none}.custom-keyboard--opening{cursor:default}\n"] }]
6031
- }] });
6634
+ args: [{ selector: 'vl-custom-keyboard', standalone: true, template: "<!-- Faramarz Yousefian and Agent Toni (Claude Opus 5) 2026-10-07\r\n - new: a ?123 / ABC key switches string keyboards between letters and the symbol page -->\r\n@if (keyboard.activeSession(); as session) {\r\n<section class=\"custom-keyboard\" data-vl-custom-keyboard [class.custom-keyboard--numeric]=\"\r\n session.type === 'numeric' ||\r\n session.type === 'date' ||\r\n session.type === 'time'\" [class.custom-keyboard--opening]=\"keyboard.interactionBlocked()\"\r\n [attr.aria-label]=\"session.type + ' keyboard'\" (pointerdown)=\"$event.preventDefault()\">\r\n\r\n @for (row of rows; track $index) {\r\n <div class=\"custom-keyboard__row\">\r\n @for (key of row; track key) {\r\n <button type=\"button\" class=\"custom-keyboard__key\" (click)=\"enterKey(key)\">\r\n {{ key }}\r\n </button>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- One mobile-style action row for every keyboard type -->\r\n <div class=\"custom-keyboard__actions\" [class.custom-keyboard__actions--compact]=\"\r\n session.type !== 'alpha' &&\r\n session.type !== 'string'\r\n \" [class.custom-keyboard__actions--symbols]=\"hasSymbolPage\">\r\n\r\n @if (\r\n session.type === 'alpha' ||\r\n session.type === 'string'\r\n ) {\r\n <button type=\"button\" class=\"custom-keyboard__action-button custom-keyboard__shift\" [class.active]=\"uppercase()\"\r\n [disabled]=\"symbols()\" aria-label=\"Change letter case\" title=\"Shift\" (click)=\"toggleCase()\">\r\n <i class=\"pi pi-arrow-up\"></i>\r\n </button>\r\n\r\n @if (hasSymbolPage) {\r\n <button type=\"button\" class=\"custom-keyboard__action-button custom-keyboard__page\"\r\n [attr.aria-label]=\"symbols() ? 'Letters' : 'Digits and symbols'\" (click)=\"toggleSymbols()\">\r\n {{ symbols() ? 'ABC' : '?123' }}\r\n </button>\r\n }\r\n\r\n <button type=\"button\" class=\"custom-keyboard__action-button custom-keyboard__space\" (click)=\"enterSpace()\">\r\n Space\r\n </button>\r\n }\r\n\r\n <button type=\"button\" class=\"custom-keyboard__action-button custom-keyboard__delete\"\r\n aria-label=\"Delete previous character\" title=\"Delete\" (click)=\"keyboard.backspace()\">\r\n <i class=\"pi pi-delete-left\"></i>\r\n </button>\r\n\r\n <button type=\"button\" class=\"custom-keyboard__action-button custom-keyboard__done\" aria-label=\"Done\"\r\n title=\"Done\" (click)=\"keyboard.close()\">\r\n <i class=\"pi pi-check\"></i>\r\n </button>\r\n </div>\r\n</section>\r\n}\r\n", styles: [":host{display:contents}.custom-keyboard{position:fixed;right:0;bottom:0;left:0;z-index:200000;display:block;width:100%;max-height:min(52dvh,390px);padding:8px 6px calc(8px + env(safe-area-inset-bottom,0px));background:#d1d5db;box-shadow:0 -6px 20px #0f172a38;box-sizing:border-box;overflow-x:hidden;overflow-y:auto;user-select:none;-webkit-user-select:none;touch-action:manipulation;-webkit-overflow-scrolling:touch}.custom-keyboard__row{display:flex;justify-content:center;gap:4px;width:100%;margin-bottom:6px}.custom-keyboard__key{display:flex;flex:1 1 0;width:auto;max-width:52px;min-width:0;height:42px;align-items:center;justify-content:center;padding:0;color:#111827;font-size:16px;font-weight:700;line-height:1;background:#fff;border:0;border-radius:5px;box-shadow:0 1px 2px #0f172a47;cursor:pointer;appearance:none;-webkit-appearance:none;touch-action:manipulation;transition:transform 80ms ease,background-color 80ms ease}.custom-keyboard__key:active{background:#e6f4ff;transform:scale(.96)}.custom-keyboard--numeric .custom-keyboard__key{max-width:92px;height:46px;font-size:18px}.custom-keyboard__actions{display:grid;grid-template-columns:46px minmax(90px,1fr) 46px 54px;gap:6px;width:100%;margin-top:4px;padding:0 4px}.custom-keyboard__actions--compact{grid-template-columns:minmax(52px,1fr) minmax(52px,1fr)}.custom-keyboard__actions--symbols{grid-template-columns:46px 54px minmax(70px,1fr) 46px 54px}.custom-keyboard__action-button{display:flex;width:100%;min-width:0;height:42px;align-items:center;justify-content:center;padding:0 8px;color:#172033;font-size:15px;font-weight:700;line-height:1;border:0;border-radius:6px;box-shadow:0 1px 2px #0f172a40;cursor:pointer;appearance:none;-webkit-appearance:none;touch-action:manipulation;transition:transform 80ms ease,background-color 80ms ease}.custom-keyboard__action-button i{font-size:17px;line-height:1;pointer-events:none}.custom-keyboard__action-button:active{transform:scale(.96)}.custom-keyboard__shift{color:#172033;background:#aeb6c2}.custom-keyboard__shift.active{color:#fff;background:#64748b}.custom-keyboard__space{color:#172033;background:#fff}.custom-keyboard__delete{color:#172033;background:#aeb6c2}.custom-keyboard__done{color:#fff;background:#1890ff}.custom-keyboard__page{color:#172033;font-size:14px;background:#aeb6c2}.custom-keyboard__shift:disabled{opacity:.45}.custom-keyboard__key:focus-visible,.custom-keyboard__action-button:focus-visible{outline:3px solid rgba(24,144,255,.35);outline-offset:1px}@media(max-height:650px){.custom-keyboard{max-height:58dvh;padding-top:6px}.custom-keyboard__row{gap:3px;margin-bottom:4px}.custom-keyboard__key{height:35px;font-size:14px}.custom-keyboard--numeric .custom-keyboard__key{height:38px;font-size:16px}.custom-keyboard__action-button{height:36px}}@media(max-width:360px){.custom-keyboard{padding-right:4px;padding-left:4px}.custom-keyboard__row{gap:3px}.custom-keyboard__key{font-size:14px}.custom-keyboard__actions{grid-template-columns:42px minmax(70px,1fr) 42px 48px;gap:4px;padding-right:2px;padding-left:2px}.custom-keyboard__actions--compact{grid-template-columns:minmax(48px,1fr) minmax(48px,1fr)}.custom-keyboard__actions--symbols{grid-template-columns:40px 48px minmax(56px,1fr) 40px 46px}}.custom-keyboard--opening .custom-keyboard__key,.custom-keyboard--opening .custom-keyboard__action-button{pointer-events:none}.custom-keyboard--opening{cursor:default}::ng-deep .picker-field{position:relative}::ng-deep .picker-field__icon{position:absolute;right:0;top:0;height:100%;width:2.5rem;border:0;background:transparent;cursor:pointer}::ng-deep .picker-field--active input{border-color:var(--modal-field-focus-color, var(--p-primary-color, #10b981))!important;outline:none!important;box-shadow:var(--modal-field-focus-shadow, 0 0 0 2px rgba(16, 185, 129, .16))!important}::ng-deep .p-datepicker-panel.model-datepicker-panel--docked,::ng-deep .model-datepicker-panel--docked{position:fixed!important;inset:auto 0 0!important;width:100%!important;min-width:100%!important;max-width:100%!important;max-height:min(52dvh,420px)!important;margin:0!important;padding-bottom:env(safe-area-inset-bottom,0px)!important;overflow-y:auto!important;transform:none!important;border-radius:12px 12px 0 0!important;box-shadow:0 -6px 20px #0f172a38!important;box-sizing:border-box!important}::ng-deep .model-timepicker-panel.p-datepicker-panel:not(.model-datepicker-panel--docked){width:auto!important;min-width:0!important;padding:4px 8px!important;border-radius:8px!important}::ng-deep .model-timepicker-panel .p-datepicker-time-picker{gap:4px!important;padding:0!important;border:0!important}::ng-deep .model-timepicker-panel .p-datepicker-hour-picker,::ng-deep .model-timepicker-panel .p-datepicker-minute-picker{gap:0!important}::ng-deep .model-timepicker-panel .p-datepicker-hour-picker span,::ng-deep .model-timepicker-panel .p-datepicker-minute-picker span,::ng-deep .model-timepicker-panel .p-datepicker-separator{font-size:1rem!important;line-height:1.2!important}::ng-deep .model-timepicker-panel .p-datepicker-increment-button,::ng-deep .model-timepicker-panel .p-datepicker-decrement-button{width:2rem!important;height:1.75rem!important}\n"] }]
6635
+ }], ctorParameters: () => [] });
6032
6636
 
6033
6637
  /*
6034
6638
  * Public API Surface of view-toolkit
@@ -6038,5 +6642,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
6038
6642
  * Generated bundle index. Do not edit.
6039
6643
  */
6040
6644
 
6041
- export { AppVersionResetService, CardComponent, ConfigService, Couchdb, CustomKeyboardComponent, CustomKeyboardDirective, CustomKeyboardService, FormComponent, GridComponent, HeaderMenuComponent, LogsComponent, MobileKeycloakService, ModelComponent, NetworkLogService, NotificationService, PaginationComponent, RequestService, SideMenuComponent, UtilityService, ViewToolkitModule, normalizeCustomKeyboardType };
6645
+ export { AppVersionResetService, CardComponent, ConfigService, Couchdb, CustomKeyboardComponent, CustomKeyboardDirective, CustomKeyboardService, FormComponent, GridComponent, HeaderMenuComponent, LogsComponent, MobileKeycloakService, ModelComponent, NetworkLogService, NotificationService, PaginationComponent, RequestService, SideMenuComponent, UtilityService, ViewToolkitModule, normalizeCustomKeyboardType, resolveCustomKeyboardType };
6042
6646
  //# sourceMappingURL=yomotech-view-toolkit.mjs.map