@fuentis/phoenix-ui 0.0.9-alpha.588 → 0.0.9-alpha.590

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.
@@ -2900,7 +2900,11 @@ const DEFAULT_MORE_ACTIONS = {
2900
2900
  label: '',
2901
2901
  items: [
2902
2902
  { key: 'EXPORT_PDF', label: 'ACTION.EXPORT_PDF', icon: 'pi pi-file-pdf' },
2903
- { key: 'EXPORT_EXCEL', label: 'ACTION.EXPORT_EXCEL', icon: 'pi pi-file-excel' },
2903
+ {
2904
+ key: 'EXPORT_EXCEL',
2905
+ label: 'ACTION.EXPORT_EXCEL',
2906
+ icon: 'pi pi-file-excel',
2907
+ },
2904
2908
  ],
2905
2909
  };
2906
2910
  class TableComponent {
@@ -3154,7 +3158,9 @@ class TableComponent {
3154
3158
  this.columnTypeMap = {};
3155
3159
  for (const col of this.columns)
3156
3160
  this.columnTypeMap[col.field] = col.columnType;
3157
- const nextSelected = this.selectedColumnsInput?.length ? this.selectedColumnsInput : this.columns;
3161
+ const nextSelected = this.selectedColumnsInput?.length
3162
+ ? this.selectedColumnsInput
3163
+ : this.columns;
3158
3164
  // microtask to avoid change detection timing issues
3159
3165
  Promise.resolve().then(() => {
3160
3166
  this.selectedColumns = [...nextSelected];
@@ -3255,29 +3261,31 @@ class TableComponent {
3255
3261
  const s = cfg?.initialSort;
3256
3262
  // NEW style
3257
3263
  if (Array.isArray(s))
3258
- return s.filter(x => !!x?.field);
3264
+ return s.filter((x) => !!x?.field);
3259
3265
  if (s && typeof s === 'object' && s.field)
3260
3266
  return [s];
3261
3267
  // LEGACY fallback
3262
3268
  if (cfg?.initialSortField) {
3263
- return [{
3269
+ return [
3270
+ {
3264
3271
  field: cfg.initialSortField,
3265
3272
  order: (cfg.initialSortOrder ?? 1),
3266
- }];
3273
+ },
3274
+ ];
3267
3275
  }
3268
3276
  return [];
3269
3277
  }
3270
3278
  /**
3271
- * Applies initial sort once:
3272
- * - Uses tableConfiguration.initialSortField if provided
3273
- * - Otherwise uses the first available column field
3274
- * - Always ASC (order = 1)
3275
- *
3276
- * Runs only when:
3277
- * - we have data
3278
- * - we have columns/selectedColumns
3279
- * - it hasn't been applied yet for current dataset
3280
- */
3279
+ * Applies initial sort once:
3280
+ * - Uses tableConfiguration.initialSortField if provided
3281
+ * - Otherwise uses the first available column field
3282
+ * - Always ASC (order = 1)
3283
+ *
3284
+ * Runs only when:
3285
+ * - we have data
3286
+ * - we have columns/selectedColumns
3287
+ * - it hasn't been applied yet for current dataset
3288
+ */
3281
3289
  applyInitialSortIfNeeded() {
3282
3290
  if (this.initialSortApplied)
3283
3291
  return;
@@ -3288,18 +3296,20 @@ class TableComponent {
3288
3296
  this.initialSortApplied = true;
3289
3297
  return;
3290
3298
  }
3291
- const cols = (this.selectedColumns?.length ? this.selectedColumns : this.columns) ?? [];
3299
+ const cols = (this.selectedColumns?.length ? this.selectedColumns : this.columns) ??
3300
+ [];
3292
3301
  if (!cols.length)
3293
3302
  return;
3294
3303
  // 1) normalize config
3295
3304
  const cfgMeta = this.normalizeInitialSort(this.tableConfiguration);
3296
3305
  // 2) if nothing configured -> fallback first column ASC
3297
3306
  const fallbackField = cols[0]?.field;
3298
- const wantedMeta = (cfgMeta.length ? cfgMeta : [{ field: fallbackField, order: 1 }])
3299
- .filter(m => !!m?.field);
3307
+ const wantedMeta = (cfgMeta.length ? cfgMeta : [{ field: fallbackField, order: 1 }]).filter((m) => !!m?.field);
3300
3308
  // 3) keep only fields that exist in columns
3301
- const validMeta = wantedMeta.filter(m => cols.some((c) => c?.field === m.field));
3302
- const finalMeta = validMeta.length ? validMeta : [{ field: fallbackField, order: 1 }];
3309
+ const validMeta = wantedMeta.filter((m) => cols.some((c) => c?.field === m.field));
3310
+ const finalMeta = validMeta.length
3311
+ ? validMeta
3312
+ : [{ field: fallbackField, order: 1 }];
3303
3313
  if (!finalMeta[0]?.field) {
3304
3314
  this.tableData = this.allData;
3305
3315
  this.totalRecords = this.tableData.length;
@@ -3327,7 +3337,9 @@ class TableComponent {
3327
3337
  onSearch(query) {
3328
3338
  const trimmed = (query ?? '').trim().toLowerCase();
3329
3339
  this.searchQuery = trimmed;
3330
- const baseData = Object.keys(this.currentFilters).length > 0 ? this.getFilteredData() : this.originalData;
3340
+ const baseData = Object.keys(this.currentFilters).length > 0
3341
+ ? this.getFilteredData()
3342
+ : this.originalData;
3331
3343
  const searchableFields = (this.columns ?? [])
3332
3344
  .map((c) => (c?.field ?? '').trim())
3333
3345
  .filter((f) => !!f);
@@ -3373,7 +3385,9 @@ class TableComponent {
3373
3385
  return undefined;
3374
3386
  if (!path.includes('.'))
3375
3387
  return obj[path];
3376
- return path.split('.').reduce((acc, key) => (acc == null ? undefined : acc[key]), obj);
3388
+ return path
3389
+ .split('.')
3390
+ .reduce((acc, key) => (acc == null ? undefined : acc[key]), obj);
3377
3391
  }
3378
3392
  matchesQuery(value, query) {
3379
3393
  if (value === null || value === undefined)
@@ -3400,18 +3414,31 @@ class TableComponent {
3400
3414
  return this.originalData.filter((item) => {
3401
3415
  return Object.keys(this.currentFilters).every((key) => {
3402
3416
  const filterValue = this.currentFilters[key];
3403
- if (!filterValue || (Array.isArray(filterValue) && filterValue.length === 0))
3417
+ if (!filterValue ||
3418
+ (Array.isArray(filterValue) && filterValue.length === 0))
3404
3419
  return true;
3405
3420
  const itemValue = this.getNestedValue(item, key);
3421
+ // Ako je itemValue niz (kao u scopes)
3422
+ if (Array.isArray(itemValue)) {
3423
+ return filterValue.some((option) => {
3424
+ const optionKey = option?.key || option;
3425
+ return itemValue.some((val) => val?.name?.toString().toLowerCase() ===
3426
+ optionKey?.toString().toLowerCase());
3427
+ });
3428
+ }
3429
+ // Ako je filterValue niz
3406
3430
  if (Array.isArray(filterValue)) {
3407
3431
  return filterValue.some((option) => {
3408
3432
  const optionKey = option?.key || option;
3409
- return itemValue?.toString().toLowerCase() === optionKey?.toString().toLowerCase();
3433
+ return (itemValue?.toString().toLowerCase() ===
3434
+ optionKey?.toString().toLowerCase());
3410
3435
  });
3411
3436
  }
3437
+ // Single filter
3412
3438
  const singleFilterValue = filterValue?.key || filterValue;
3413
3439
  return singleFilterValue
3414
- ? itemValue?.toString().toLowerCase() === singleFilterValue.toString().toLowerCase()
3440
+ ? itemValue?.toString().toLowerCase() ===
3441
+ singleFilterValue.toString().toLowerCase()
3415
3442
  : true;
3416
3443
  });
3417
3444
  });
@@ -3478,7 +3505,10 @@ class TableComponent {
3478
3505
  return '';
3479
3506
  }
3480
3507
  compareStringsNatural(a, b) {
3481
- return a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' });
3508
+ return a.localeCompare(b, undefined, {
3509
+ numeric: true,
3510
+ sensitivity: 'base',
3511
+ });
3482
3512
  }
3483
3513
  // ============================================================
3484
3514
  // SELECTION / UI ACTIONS
@@ -3522,7 +3552,9 @@ class TableComponent {
3522
3552
  * Used by checkbox template to disable selection on specific rows.
3523
3553
  */
3524
3554
  isRowSelectable(event) {
3525
- return typeof event.data?.canSelect === 'boolean' ? event.data.canSelect : true;
3555
+ return typeof event.data?.canSelect === 'boolean'
3556
+ ? event.data.canSelect
3557
+ : true;
3526
3558
  }
3527
3559
  /**
3528
3560
  * Some tables allow click on first N columns to open details.
@@ -3545,7 +3577,9 @@ class TableComponent {
3545
3577
  getNested(obj, path) {
3546
3578
  if (!obj || !path)
3547
3579
  return null;
3548
- return path.split('.').reduce((o, k) => (o != null ? o[k] : null), obj);
3580
+ return path
3581
+ .split('.')
3582
+ .reduce((o, k) => (o != null ? o[k] : null), obj);
3549
3583
  }
3550
3584
  /**
3551
3585
  * Applies column selection coming from table-caption (array of field names).
@@ -3734,6 +3768,55 @@ function noDangerousCharsValidator() {
3734
3768
  };
3735
3769
  }
3736
3770
 
3771
+ var CompareOperatorType;
3772
+ (function (CompareOperatorType) {
3773
+ CompareOperatorType["LESS_OR_EQUAL"] = "LESS_OR_EQUAL";
3774
+ CompareOperatorType["LESS"] = "LESS";
3775
+ CompareOperatorType["EQUAL"] = "EQUAL";
3776
+ CompareOperatorType["MORE_OR_EQUAL"] = "MORE_OR_EQUAL";
3777
+ CompareOperatorType["MORE"] = "MORE";
3778
+ })(CompareOperatorType || (CompareOperatorType = {}));
3779
+ function dateCompareValidator(fieldToCompare, comparison, errMessage) {
3780
+ return (control) => {
3781
+ if (!control.parent)
3782
+ return null; // ako form group ne postoji
3783
+ const compareControl = control.parent.get(fieldToCompare);
3784
+ if (!compareControl)
3785
+ return null; // polje za poređenje ne postoji
3786
+ const controlValue = control.value ? new Date(control.value) : null;
3787
+ const compareValue = compareControl.value
3788
+ ? new Date(compareControl.value)
3789
+ : null;
3790
+ if (!controlValue || !compareValue)
3791
+ return null; // nema vrednosti, ne validira
3792
+ let valid = true;
3793
+ switch (comparison) {
3794
+ case CompareOperatorType.LESS:
3795
+ valid = controlValue < compareValue;
3796
+ break;
3797
+ case CompareOperatorType.LESS_OR_EQUAL:
3798
+ valid = controlValue <= compareValue;
3799
+ break;
3800
+ case CompareOperatorType.EQUAL:
3801
+ valid = controlValue.getTime() === compareValue.getTime();
3802
+ break;
3803
+ case CompareOperatorType.MORE_OR_EQUAL:
3804
+ valid = controlValue >= compareValue;
3805
+ break;
3806
+ case CompareOperatorType.MORE:
3807
+ valid = controlValue > compareValue;
3808
+ break;
3809
+ }
3810
+ if (!valid) {
3811
+ return {
3812
+ dateCompare: errMessage ||
3813
+ `Datum ne zadovoljava uslov ${comparison} u odnosu na ${fieldToCompare}`,
3814
+ };
3815
+ }
3816
+ return null; // validno
3817
+ };
3818
+ }
3819
+
3737
3820
  class MetaFormService {
3738
3821
  router = inject(Router);
3739
3822
  formEvent = new Subject();
@@ -3808,8 +3891,7 @@ class MetaFormAbstract {
3808
3891
  onFormCancel = new EventEmitter();
3809
3892
  formActive$;
3810
3893
  formSub$;
3811
- constructor(fb, metaService, translateService, http // private env: EnvService
3812
- ) {
3894
+ constructor(fb, metaService, translateService, http) {
3813
3895
  this.fb = fb;
3814
3896
  this.metaService = metaService;
3815
3897
  this.translateService = translateService;
@@ -3823,10 +3905,7 @@ class MetaFormAbstract {
3823
3905
  }
3824
3906
  createForm(controls) {
3825
3907
  controls?.forEach((control) => {
3826
- this.metaForm.addControl(control.configuration?.key, this.fb.control(getFieldType(control)
3827
- ///updateOn: blur only for controls with async validators. If added for all each tab/step toggle removes form value. Check why
3828
- // control.asyncValidators && { updateOn: 'blur' },
3829
- ));
3908
+ this.metaForm.addControl(control.configuration?.key, this.fb.control(getFieldType(control)));
3830
3909
  //add validators
3831
3910
  this.addControlValidators(control);
3832
3911
  //disable non editable fields
@@ -3864,6 +3943,11 @@ class MetaFormAbstract {
3864
3943
  validators.push(timePeriod(this.translateService.currentLang, control.validators.tpMin));
3865
3944
  control.validators.dueDate && validators.push(startDueDateValidator);
3866
3945
  }
3946
+ if (control.validators?.compare) {
3947
+ const compareConfig = control.validators.compare;
3948
+ validators.push(dateCompareValidator(compareConfig.fieldToCompare, // napomena: ispravi typo 'feildToCompare'
3949
+ compareConfig.comparisonOperator, compareConfig.errMessage));
3950
+ }
3867
3951
  if (control?.mandatory) {
3868
3952
  validators.push(Validators.required);
3869
3953
  }
@@ -4386,6 +4470,7 @@ var ErrorType;
4386
4470
  ErrorType["MAX"] = "max";
4387
4471
  ErrorType["MIN"] = "min";
4388
4472
  ErrorType["DANGEROUS_CHARS"] = "dangerousChars";
4473
+ ErrorType["DATE_COMPARE"] = "dateCompare";
4389
4474
  })(ErrorType || (ErrorType = {}));
4390
4475
 
4391
4476
  class InlineFieldError {
@@ -4437,6 +4522,7 @@ class InlineFieldError {
4437
4522
  // return 'ENTRY OK';
4438
4523
  // }
4439
4524
  if (control?.errors) {
4525
+ console.log('error type', Object.keys(control.errors)[0]);
4440
4526
  switch (Object.keys(control.errors)[0]) {
4441
4527
  case ErrorType.REQUIRED:
4442
4528
  return this.translateService.instant('VALIDATION_MESSAGE.THIS_FIELD_IS_REQUIRED');
@@ -4469,6 +4555,8 @@ class InlineFieldError {
4469
4555
  return this.translateService.instant('VALIDATION_MESSAGE.VALUE_SHOULD_MATCH_TIMEPATTERN_LIMIT_MIN');
4470
4556
  case ErrorType.BOTH_DATES:
4471
4557
  return this.translateService.instant('VALIDATION_MESSAGE.BOTH_START_END_DATE_REQUIRED');
4558
+ case ErrorType.DATE_COMPARE:
4559
+ return this.translateService.instant(control.errors['dateCompare']);
4472
4560
  // add more cases for other validators as needed
4473
4561
  case ErrorType.PATTERN:
4474
4562
  const re = '^(https?://)?([\\da-z.-]+)\\.([a-z.]{2,6})[/\\w .-]*/?$';