@iapps/ng-dhis2-ui 1.1.56 → 1.1.58

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.
@@ -8,7 +8,7 @@ import { NgxDhis2HttpClientService } from '@iapps/ngx-dhis2-http-client';
8
8
  import * as React from 'react';
9
9
  import React__default, { useState, useCallback, useRef, useEffect, createContext, useContext, useMemo } from 'react';
10
10
  import * as ReactDOM from 'react-dom/client';
11
- import { catchError, of, map, zip, firstValueFrom, Subject, takeUntil } from 'rxjs';
11
+ import { catchError, of, map, zip, firstValueFrom, filter, Subject, takeUntil } from 'rxjs';
12
12
  import i18n from '@dhis2/d2-i18n';
13
13
  import { colors, theme, spacers } from '@dhis2/ui-constants';
14
14
  import PropTypes from 'prop-types';
@@ -34,8 +34,8 @@ import * as i2 from '@angular/forms';
34
34
  import { Validators, UntypedFormControl, UntypedFormGroup, FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
35
35
  import { toObservable } from '@angular/core/rxjs-interop';
36
36
  import { format } from 'date-fns';
37
+ import { DataQueryFilter, DataFilterCondition, ProgramRuleEngine } from '@iapps/d2-web-sdk';
37
38
  import moment from 'moment';
38
- import { ProgramRuleEngine } from '@iapps/d2-web-sdk';
39
39
 
40
40
  class SharedDhis2UiModule {
41
41
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.8", ngImport: i0, type: SharedDhis2UiModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
@@ -4065,11 +4065,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.8", ngImpor
4065
4065
  // Use of this source code is governed by a BSD-style
4066
4066
  // license that can be found in the LICENSE file.
4067
4067
  const useFieldValidation = (props) => {
4068
- const { form, touched, field, value } = props;
4068
+ const { form, touched, field, value, initialError, recordExistError } = props;
4069
4069
  const hasError = useMemo(() => {
4070
+ if (recordExistError) {
4071
+ return true;
4072
+ }
4073
+ if (initialError && value) {
4074
+ return true;
4075
+ }
4070
4076
  return touched && (form.get(field.id) || form.get(field.key))?.invalid;
4071
- }, [value, touched]);
4077
+ }, [value, touched, initialError, recordExistError]);
4072
4078
  const validationError = useMemo(() => {
4079
+ if (recordExistError) {
4080
+ return recordExistError;
4081
+ }
4082
+ if (initialError && value) {
4083
+ return initialError;
4084
+ }
4073
4085
  if (!touched) {
4074
4086
  return undefined;
4075
4087
  }
@@ -4080,25 +4092,26 @@ const useFieldValidation = (props) => {
4080
4092
  if (error['min']) {
4081
4093
  return `${field.label || 'Value'} should not be less than ${error['min'].min}!`;
4082
4094
  }
4083
- else if (error['max']) {
4095
+ if (error['max']) {
4084
4096
  return `${field.label || 'Value'} should not be greater than ${error['max'].max}!`;
4085
4097
  }
4086
- else if (error['pattern']) {
4098
+ if (error['pattern']) {
4087
4099
  if (field.type === 'email') {
4088
4100
  return `The email address provided is not valid`;
4089
4101
  }
4090
- else if (field.type === 'tel') {
4102
+ if (field.type === 'tel') {
4091
4103
  return 'Phone number provided is not valid';
4092
4104
  }
4093
- else {
4094
- return `${field.label || 'Value'} should have appropriate format`;
4095
- }
4105
+ return `${field.label || 'Value'} should have appropriate format`;
4106
+ }
4107
+ if (error['customError']) {
4108
+ return error['customError'];
4096
4109
  }
4097
4110
  return error['required']
4098
4111
  ? `${field.label || 'Value'} is required`
4099
4112
  : undefined;
4100
- }, [hasError]);
4101
- return { hasError, validationError };
4113
+ }, [touched, value, initialError, recordExistError]);
4114
+ return { validationError, hasError };
4102
4115
  };
4103
4116
 
4104
4117
  // Copyright 2025 UDSM DHIS2 Lab. All rights reserved.
@@ -4134,6 +4147,7 @@ class FormField {
4134
4147
  this.programStage = options.programStage;
4135
4148
  this.program = options.program;
4136
4149
  this.trackedEntityInstance = options.trackedEntityInstance;
4150
+ this.trackedEntityType = options.trackedEntityType;
4137
4151
  this.hasOptions = options.hasOptions;
4138
4152
  this.dependentField = options.dependentField;
4139
4153
  this.displayInList = options.displayInList;
@@ -4319,6 +4333,8 @@ class FieldUtil {
4319
4333
  return 'number';
4320
4334
  case FieldType.PHONE_NUMBER:
4321
4335
  return 'tel';
4336
+ case FieldType.EMAIL:
4337
+ return 'email';
4322
4338
  case FieldType.ORG_UNIT:
4323
4339
  return 'org-unit';
4324
4340
  case FieldType.DATE:
@@ -4435,8 +4451,11 @@ class FormUtil {
4435
4451
  const maxValidators = field.max
4436
4452
  ? [Validators.max(field.max)]
4437
4453
  : [];
4438
- const phoneNumberValidator = field.type === 'tel'
4439
- ? [Validators.pattern('\\+255[6-7][1-9][0-9]{7}$')]
4454
+ const phoneNumberValidator = field.type === 'tel' ? [] : [];
4455
+ const emailValidator = field.type === 'email'
4456
+ ? [
4457
+ Validators.pattern('^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$'),
4458
+ ]
4440
4459
  : [];
4441
4460
  group[field.key] = field.required
4442
4461
  ? new UntypedFormControl({ value, disabled: field.disabled }, {
@@ -4445,6 +4464,7 @@ class FormUtil {
4445
4464
  ...minValidators,
4446
4465
  ...maxValidators,
4447
4466
  ...phoneNumberValidator,
4467
+ ...emailValidator,
4448
4468
  ],
4449
4469
  updateOn: 'change',
4450
4470
  })
@@ -4453,6 +4473,7 @@ class FormUtil {
4453
4473
  ...minValidators,
4454
4474
  ...maxValidators,
4455
4475
  ...phoneNumberValidator,
4476
+ ...emailValidator,
4456
4477
  ],
4457
4478
  });
4458
4479
  if (field.referenceKey) {
@@ -4466,6 +4487,7 @@ class FormUtil {
4466
4487
  ...minValidators,
4467
4488
  ...maxValidators,
4468
4489
  ...phoneNumberValidator,
4490
+ ...emailValidator,
4469
4491
  ],
4470
4492
  updateOn: 'change',
4471
4493
  })
@@ -4474,6 +4496,7 @@ class FormUtil {
4474
4496
  ...minValidators,
4475
4497
  ...maxValidators,
4476
4498
  ...phoneNumberValidator,
4499
+ ...emailValidator,
4477
4500
  ],
4478
4501
  });
4479
4502
  }
@@ -4692,6 +4715,9 @@ class MetadataRule {
4692
4715
  const regex = /(A|#|V)\{([^}]+)\}/g;
4693
4716
  return this.rule?.condition?.replace(regex, (match, _, ruleVariable) => {
4694
4717
  const ruleVariableObject = this.ruleVariables?.find((variable) => variable.name === ruleVariable);
4718
+ if (!ruleVariableObject) {
4719
+ return `{${ruleVariable}}`;
4720
+ }
4695
4721
  const code = camelCase(ruleVariableObject?.dataElement?.code) ||
4696
4722
  ruleVariableObject?.dataElement?.id ||
4697
4723
  camelCase(ruleVariableObject?.trackedEntityAttribute?.code) ||
@@ -4719,6 +4745,7 @@ class FormMetadataSection {
4719
4745
  sortOrder: programTrackedEntityAttribute['sortOrder'],
4720
4746
  mandatory: programTrackedEntityAttribute['mandatory'],
4721
4747
  allowFutureDate: programTrackedEntityAttribute['allowFutureDate'],
4748
+ trackedEntityType: (this.params.program['trackedEntityType'] || {})['id'],
4722
4749
  metaType: 'ATTRIBUTE',
4723
4750
  };
4724
4751
  });
@@ -5331,6 +5358,7 @@ class BaseFormFieldComponent extends ReactWrapperModule {
5331
5358
  this.httpClient = inject(NgxDhis2HttpClientService);
5332
5359
  this.fieldType = 'textbox';
5333
5360
  this.field = input.required();
5361
+ this.fieldError = input();
5334
5362
  this.fieldConfig = input(new FieldConfig());
5335
5363
  this.form = model.required();
5336
5364
  this.isValid = input();
@@ -5338,6 +5366,7 @@ class BaseFormFieldComponent extends ReactWrapperModule {
5338
5366
  this.value = model();
5339
5367
  this.value$ = toObservable(this.value);
5340
5368
  this.isValueAssigned$ = toObservable(this.isValueAssigned);
5369
+ this.fieldError$ = toObservable(this.fieldError);
5341
5370
  this.label = computed(() => {
5342
5371
  return !this.fieldConfig()?.hideLabel ? this.field().label : undefined;
5343
5372
  });
@@ -5369,13 +5398,30 @@ class BaseFormFieldComponent extends ReactWrapperModule {
5369
5398
  const [selected, setSelected] = useState();
5370
5399
  const [touched, setTouched] = useState(false);
5371
5400
  const [disabled, setDisabled] = useState(this.field()?.disabled ?? this.field()?.generated ?? false);
5401
+ const [initialError, setInitialError] = useState();
5402
+ const [recordExistError, setRecordExistError] = useState();
5403
+ const [checkingUniqueness, setCheckingUniqueness] = useState();
5372
5404
  useEffect(() => {
5373
5405
  if (this.isValueAssigned()) {
5406
+ const value = this.form().get(this.field().id)?.value ||
5407
+ this.form().get(this.field().key)?.value;
5374
5408
  setDisabled(true);
5375
- setValue(this.form().get(this.field().id)?.value ||
5376
- this.form().get(this.field().key)?.value);
5409
+ setValue(value);
5410
+ checkValueUniqueness();
5377
5411
  }
5378
5412
  }, [this.isValueAssigned()]);
5413
+ useEffect(() => {
5414
+ const fieldErrorSubscription = this.fieldError$
5415
+ .pipe(filter((error) => error !== initialError))
5416
+ .subscribe({
5417
+ next: (error) => {
5418
+ setInitialError(error);
5419
+ },
5420
+ });
5421
+ return () => {
5422
+ fieldErrorSubscription.unsubscribe();
5423
+ };
5424
+ });
5379
5425
  const onChange = (payload) => setSelected(payload.selected);
5380
5426
  const arrayValue = useMemo(() => {
5381
5427
  if (value && value.length > 0) {
@@ -5383,131 +5429,186 @@ class BaseFormFieldComponent extends ReactWrapperModule {
5383
5429
  }
5384
5430
  return [];
5385
5431
  }, [value]);
5386
- const { hasError, validationError } = useFieldValidation({
5432
+ const { validationError, hasError } = useFieldValidation({
5387
5433
  field: this.field(),
5388
5434
  form: this.form(),
5435
+ initialError,
5436
+ recordExistError,
5389
5437
  value,
5390
5438
  touched,
5391
5439
  });
5392
- switch (this.field().controlType) {
5393
- case 'textarea':
5394
- return (React__default.createElement(TextAreaField, { error: hasError, validationText: validationError, inputWidth: this.fieldConfig()?.inputWidth, required: this.field().required, name: this.field().id, label: this.label(), rows: 5, placeholder: this.placeholder(), value: value, onChange: (event) => {
5395
- this.ngZone.run(() => {
5396
- (this.form().get(this.field().id) ||
5397
- this.form().get(this.field().key))?.setValue(event.value);
5398
- this.immediateUpdate.emit({
5399
- form: this.form(),
5400
- value: event.value,
5401
- });
5440
+ const checkValueUniqueness = async () => {
5441
+ if (this.field().unique && this.field().trackedEntityType) {
5442
+ setCheckingUniqueness(true);
5443
+ setRecordExistError(undefined);
5444
+ const d2 = window.d2Web;
5445
+ try {
5446
+ const response = (await d2.trackerModule.trackedEntity
5447
+ .setTrackedEntityType(this.field().trackedEntityType)
5448
+ .setOuMode('ACCESSIBLE')
5449
+ .setFilters([
5450
+ new DataQueryFilter()
5451
+ .setAttribute(this.field().id)
5452
+ .setCondition(DataFilterCondition.Equal)
5453
+ .setValue(value),
5454
+ ])
5455
+ .get())?.data;
5456
+ setCheckingUniqueness(false);
5457
+ if (response?.length > 0) {
5458
+ const customError = `Record with this ${this.label()} is already registered`;
5459
+ (this.form().controls[this.field().id] ||
5460
+ this.form().controls[this.field().key])?.setErrors({
5461
+ customError,
5402
5462
  });
5403
- setValue(event.value);
5404
- setTouched(true);
5405
- }, onBlur: () => {
5406
- this.ngZone.run(() => {
5407
- this.update.emit({ form: this.form(), value });
5408
- });
5409
- } }));
5410
- case 'org-unit':
5411
- return (React__default.createElement(OrgUnitFormField, { label: this.label(), key: this.field().id, required: this.field().required, onSelectOrgUnit: (selectedOrgUnit) => {
5412
- this.ngZone.run(() => {
5413
- (this.form().get(this.field().id) ||
5414
- this.form().get(this.field().key))?.setValue(selectedOrgUnit);
5415
- this.update.emit({
5416
- form: this.form(),
5417
- value: selectedOrgUnit,
5463
+ setRecordExistError(customError);
5464
+ }
5465
+ }
5466
+ catch (e) {
5467
+ setCheckingUniqueness(false);
5468
+ setRecordExistError(undefined);
5469
+ }
5470
+ }
5471
+ };
5472
+ const formFieldContent = () => {
5473
+ switch (this.field().controlType) {
5474
+ case 'textarea':
5475
+ return (React__default.createElement(TextAreaField, { error: hasError, validationText: validationError, inputWidth: this.fieldConfig()?.inputWidth, required: this.field().required, name: this.field().id, label: this.label(), rows: 5, placeholder: this.placeholder(), value: value, onChange: (event) => {
5476
+ this.ngZone.run(() => {
5477
+ (this.form().get(this.field().id) ||
5478
+ this.form().get(this.field().key))?.setValue(event.value);
5479
+ this.immediateUpdate.emit({
5480
+ form: this.form(),
5481
+ value: event.value,
5482
+ });
5418
5483
  });
5419
- });
5420
- setValue(selectedOrgUnit);
5421
- setTouched(true);
5422
- }, selected: value }));
5423
- case 'transfer':
5424
- return (React__default.createElement(Transfer, { filterable: true, filterPlaceholder: "Search", selected: selected, leftHeader: React__default.createElement("div", { style: {
5425
- fontSize: 14,
5426
- padding: '8px 4px',
5427
- } }, this.field().availableOptionsLabel), rightHeader: React__default.createElement("div", { style: {
5428
- fontSize: 14,
5429
- padding: '8px 4px',
5430
- } }, this.field().selectedOptionsLabel), options: this.field().options, onChange: (event) => {
5431
- onChange({ selected: event.selected });
5432
- this.ngZone.run(() => {
5433
- (this.form().get(this.field().id) ||
5434
- this.form().get(this.field().key))?.setValue(event.selected);
5435
- });
5436
- setValue(event.selected);
5437
- setTouched(true);
5438
- this.update.emit({ form: this.form(), value });
5439
- } }));
5440
- case 'checkbox':
5441
- return (React__default.createElement(Checkbox, { checked: Boolean(value), error: hasError, label: this.label(), name: this.field().id, disabled: disabled, onChange: (event) => {
5442
- this.ngZone.run(() => {
5443
- (this.form().get(this.field().id) ||
5444
- this.form().get(this.field().key))?.setValue(event.checked);
5445
- });
5446
- setValue(event.checked);
5447
- setTouched(true);
5448
- }, onBlur: () => {
5449
- this.ngZone.run(() => {
5484
+ setValue(event.value);
5485
+ setTouched(true);
5486
+ }, onBlur: () => {
5487
+ checkValueUniqueness();
5488
+ this.ngZone.run(() => {
5489
+ this.update.emit({ form: this.form(), value });
5490
+ });
5491
+ } }));
5492
+ case 'org-unit':
5493
+ return (React__default.createElement(OrgUnitFormField, { label: this.label(), key: this.field().id, required: this.field().required, onSelectOrgUnit: (selectedOrgUnit) => {
5494
+ this.ngZone.run(() => {
5495
+ (this.form().get(this.field().id) ||
5496
+ this.form().get(this.field().key))?.setValue(selectedOrgUnit);
5497
+ this.update.emit({
5498
+ form: this.form(),
5499
+ value: selectedOrgUnit,
5500
+ });
5501
+ });
5502
+ setValue(selectedOrgUnit);
5503
+ setTouched(true);
5504
+ }, selected: value }));
5505
+ case 'transfer':
5506
+ return (React__default.createElement(Transfer, { filterable: true, filterPlaceholder: "Search", selected: selected, leftHeader: React__default.createElement("div", { style: {
5507
+ fontSize: 14,
5508
+ padding: '8px 4px',
5509
+ } }, this.field().availableOptionsLabel), rightHeader: React__default.createElement("div", { style: {
5510
+ fontSize: 14,
5511
+ padding: '8px 4px',
5512
+ } }, this.field().selectedOptionsLabel), options: this.field().options, onChange: (event) => {
5513
+ onChange({ selected: event.selected });
5514
+ this.ngZone.run(() => {
5515
+ (this.form().get(this.field().id) ||
5516
+ this.form().get(this.field().key))?.setValue(event.selected);
5517
+ });
5518
+ setValue(event.selected);
5519
+ setTouched(true);
5450
5520
  this.update.emit({ form: this.form(), value });
5451
- });
5452
- } }));
5453
- case 'dropdown':
5454
- return (React__default.createElement(SingleSelectField, { filterable: (this.field().options || []).length > 5, clearable: true, error: hasError, validationText: validationError, inputWidth: this.fieldConfig()?.inputWidth, disabled: disabled, required: this.field().required, className: "select", label: this.label(), selected: value, onChange: (event) => {
5455
- this.ngZone.run(() => {
5456
- (this.form().get(this.field().id) ||
5457
- this.form().get(this.field().key))?.setValue(event.selected);
5458
- this.update.emit({
5459
- form: this.form(),
5460
- value: event.selected,
5521
+ } }));
5522
+ case 'checkbox':
5523
+ return (React__default.createElement(Checkbox, { checked: Boolean(value), error: hasError, label: this.label(), name: this.field().id, disabled: disabled, onChange: (event) => {
5524
+ this.ngZone.run(() => {
5525
+ (this.form().get(this.field().id) ||
5526
+ this.form().get(this.field().key))?.setValue(event.checked);
5461
5527
  });
5462
- });
5463
- setValue(event.selected);
5464
- setTouched(true);
5465
- } }, (this.field().options || []).map((option) => (React__default.createElement(SingleSelectOption, { key: crypto.randomUUID() || option.key, label: option.label, value: option.value })))));
5466
- case 'multi-dropdown':
5467
- return (React__default.createElement(MultiSelectField, { clearText: "Clear", clearable: true, empty: "No data found", filterable: (this.field().options || []).length > 5, filterPlaceholder: "Type to filter options", error: hasError, validationText: validationError, inputWidth: this.fieldConfig()?.inputWidth, disabled: disabled, label: this.label(), required: this.field().required, loadingText: "Loading options", noMatchText: "No options found", onChange: (event) => {
5468
- const selectedValue = (event.selected || []).join(',');
5469
- this.ngZone.run(() => {
5470
- (this.form().get(this.field().id) ||
5471
- this.form().get(this.field().key))?.setValue(selectedValue);
5472
- this.update.emit({
5473
- form: this.form(),
5474
- value: selectedValue,
5528
+ setValue(event.checked);
5529
+ setTouched(true);
5530
+ }, onBlur: () => {
5531
+ checkValueUniqueness();
5532
+ this.ngZone.run(() => {
5533
+ this.update.emit({ form: this.form(), value });
5475
5534
  });
5476
- });
5477
- setValue(selectedValue);
5478
- setTouched(true);
5479
- }, selected: arrayValue }, (this.field().options || []).map((option) => (React__default.createElement(MultiSelectOption, { key: option.key, label: option.label, value: option.value })))));
5480
- case 'file':
5481
- return (React__default.createElement(React__default.Fragment, null,
5482
- React__default.createElement(FileUploadField, { label: this.label(), id: this.field().id, hasError: hasError, required: this.field().required, validationText: validationError, performUpload: true, uploadUrl: 'fileResources', onUploadSuccess: (fileId) => {
5535
+ } }));
5536
+ case 'dropdown':
5537
+ return (React__default.createElement(SingleSelectField, { filterable: (this.field().options || []).length > 5, clearable: true, error: hasError, validationText: validationError, inputWidth: this.fieldConfig()?.inputWidth, disabled: disabled, required: this.field().required, className: "select", label: this.label(), selected: value, onChange: (event) => {
5483
5538
  this.ngZone.run(() => {
5484
- this.form().get(this.field().id) ||
5485
- this.form().get(this.field().key)?.setValue(fileId);
5539
+ (this.form().get(this.field().id) ||
5540
+ this.form().get(this.field().key))?.setValue(event.selected);
5486
5541
  this.update.emit({
5487
5542
  form: this.form(),
5488
- value: fileId,
5543
+ value: event.selected,
5489
5544
  });
5490
5545
  });
5491
- setValue(fileId);
5546
+ setValue(event.selected);
5492
5547
  setTouched(true);
5493
- } })));
5494
- default:
5495
- return (React__default.createElement(InputField$1, { error: hasError, validationText: validationError, type: this.field().type, inputWidth: this.fieldConfig()?.inputWidth, required: this.field().required, name: this.field().id, label: this.label(), min: this.field().min?.toString(), max: this.field().max?.toString(), placeholder: this.placeholder(), value: value, readOnly: disabled, onChange: (event) => {
5496
- this.ngZone.run(() => {
5497
- (this.form().get(this.field().id) ||
5498
- this.form().get(this.field().key))?.setValue(event.value);
5499
- this.immediateUpdate.emit({
5500
- form: this.form(),
5501
- value: event.value,
5548
+ }, onBlur: () => {
5549
+ checkValueUniqueness();
5550
+ } }, (this.field().options || []).map((option) => (React__default.createElement(SingleSelectOption, { key: crypto.randomUUID() || option.key, label: option.label, value: option.value })))));
5551
+ case 'multi-dropdown':
5552
+ return (React__default.createElement(MultiSelectField, { clearText: "Clear", clearable: true, empty: "No data found", filterable: (this.field().options || []).length > 5, filterPlaceholder: "Type to filter options", error: hasError, validationText: validationError, inputWidth: this.fieldConfig()?.inputWidth, disabled: disabled, label: this.label(), required: this.field().required, loadingText: "Loading options", noMatchText: "No options found", onChange: (event) => {
5553
+ const selectedValue = (event.selected || []).join(',');
5554
+ this.ngZone.run(() => {
5555
+ (this.form().get(this.field().id) ||
5556
+ this.form().get(this.field().key))?.setValue(selectedValue);
5557
+ this.update.emit({
5558
+ form: this.form(),
5559
+ value: selectedValue,
5560
+ });
5502
5561
  });
5503
- });
5504
- setValue(event.value);
5505
- setTouched(true);
5506
- }, onBlur: () => {
5507
- this.ngZone.run(() => {
5508
- this.update.emit({ form: this.form(), value });
5509
- });
5510
- } }));
5562
+ setValue(selectedValue);
5563
+ setTouched(true);
5564
+ }, onBlur: () => {
5565
+ checkValueUniqueness();
5566
+ }, selected: arrayValue }, (this.field().options || []).map((option) => (React__default.createElement(MultiSelectOption, { key: option.key, label: option.label, value: option.value })))));
5567
+ case 'file':
5568
+ return (React__default.createElement(React__default.Fragment, null,
5569
+ React__default.createElement(FileUploadField, { label: this.label(), id: this.field().id, hasError: hasError, required: this.field().required, validationText: validationError, performUpload: true, uploadUrl: 'fileResources', onUploadSuccess: (fileId) => {
5570
+ this.ngZone.run(() => {
5571
+ this.form().get(this.field().id) ||
5572
+ this.form().get(this.field().key)?.setValue(fileId);
5573
+ this.update.emit({
5574
+ form: this.form(),
5575
+ value: fileId,
5576
+ });
5577
+ });
5578
+ setValue(fileId);
5579
+ setTouched(true);
5580
+ } })));
5581
+ default:
5582
+ return (React__default.createElement(InputField$1, { error: hasError, validationText: validationError, type: this.field().type, inputWidth: this.fieldConfig()?.inputWidth, required: this.field().required, name: this.field().id, label: this.label(), min: this.field().min?.toString(), max: this.field().max?.toString(), placeholder: this.placeholder(), value: value, readOnly: disabled, onChange: (event) => {
5583
+ this.ngZone.run(() => {
5584
+ (this.form().get(this.field().id) ||
5585
+ this.form().get(this.field().key))?.setValue(event.value);
5586
+ this.immediateUpdate.emit({
5587
+ form: this.form(),
5588
+ value: event.value,
5589
+ });
5590
+ });
5591
+ setValue(event.value);
5592
+ setTouched(true);
5593
+ }, onBlur: () => {
5594
+ checkValueUniqueness();
5595
+ this.ngZone.run(() => {
5596
+ this.update.emit({ form: this.form(), value });
5597
+ });
5598
+ } }));
5599
+ }
5600
+ };
5601
+ {
5602
+ return (React__default.createElement(React__default.Fragment, null,
5603
+ formFieldContent(),
5604
+ checkingUniqueness && (React__default.createElement("div", { style: {
5605
+ display: 'flex',
5606
+ alignItems: 'center',
5607
+ gap: 8,
5608
+ paddingTop: 8,
5609
+ } },
5610
+ React__default.createElement(CircularLoader$1, { small: true }),
5611
+ React__default.createElement("div", { style: { color: colors$1.grey800, fontSize: 14 } }, "Checking...")))));
5511
5612
  }
5512
5613
  };
5513
5614
  }
@@ -5515,7 +5616,7 @@ class BaseFormFieldComponent extends ReactWrapperModule {
5515
5616
  this.value.set(event);
5516
5617
  }
5517
5618
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.8", ngImport: i0, type: BaseFormFieldComponent, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
5518
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.1.8", type: BaseFormFieldComponent, isStandalone: true, inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, fieldConfig: { classPropertyName: "fieldConfig", publicName: "fieldConfig", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: true, transformFunction: null }, isValid: { classPropertyName: "isValid", publicName: "isValid", isSignal: true, isRequired: false, transformFunction: null }, isValueAssigned: { classPropertyName: "isValueAssigned", publicName: "isValueAssigned", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { form: "formChange", value: "valueChange", update: "update", immediateUpdate: "immediateUpdate" }, usesInheritance: true, usesOnChanges: true, ngImport: i0 }); }
5619
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.1.8", type: BaseFormFieldComponent, isStandalone: true, inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, fieldError: { classPropertyName: "fieldError", publicName: "fieldError", isSignal: true, isRequired: false, transformFunction: null }, fieldConfig: { classPropertyName: "fieldConfig", publicName: "fieldConfig", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: true, transformFunction: null }, isValid: { classPropertyName: "isValid", publicName: "isValid", isSignal: true, isRequired: false, transformFunction: null }, isValueAssigned: { classPropertyName: "isValueAssigned", publicName: "isValueAssigned", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { form: "formChange", value: "valueChange", update: "update", immediateUpdate: "immediateUpdate" }, usesInheritance: true, usesOnChanges: true, ngImport: i0 }); }
5519
5620
  }
5520
5621
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.8", ngImport: i0, type: BaseFormFieldComponent, decorators: [{
5521
5622
  type: Directive
@@ -5548,6 +5649,7 @@ class FormFieldComponent {
5548
5649
  this.immediateFieldUpdate = new EventEmitter();
5549
5650
  this.runtimeOptionChange = new EventEmitter();
5550
5651
  this.value = signal('');
5652
+ this.runtimeError = signal(undefined);
5551
5653
  this._runtimeOptions = [];
5552
5654
  this.runtimeOptions = [];
5553
5655
  this.fieldFilterControl = new FormControl();
@@ -5581,9 +5683,10 @@ class FormFieldComponent {
5581
5683
  });
5582
5684
  }
5583
5685
  #processRules(ruleActions) {
5584
- ruleActions
5585
- .filter((ruleAction) => ruleAction.field === this.field().id)
5586
- .forEach((ruleAction) => {
5686
+ const fieldRuleActions = ruleActions.filter((ruleAction) => ruleAction.field === this.field().id);
5687
+ let runtimeError;
5688
+ let isValueAssigned = false;
5689
+ fieldRuleActions.forEach((ruleAction) => {
5587
5690
  switch (ruleAction.actionType) {
5588
5691
  case 'ASSIGN': {
5589
5692
  if (ruleAction.assignedData) {
@@ -5592,16 +5695,26 @@ class FormFieldComponent {
5592
5695
  this.form
5593
5696
  ?.get(this.field().key)
5594
5697
  ?.setValue(ruleAction.assignedData);
5595
- this.isValueAssigned.set(true);
5698
+ isValueAssigned = true;
5596
5699
  this.onValueChange();
5597
5700
  }
5598
5701
  }
5599
5702
  break;
5600
5703
  }
5704
+ case 'SHOWERROR': {
5705
+ runtimeError = ruleAction.displayedContent;
5706
+ (this.form.controls[this.field().id] ||
5707
+ this.form.controls[this.field().key])?.setErrors({
5708
+ customError: runtimeError,
5709
+ });
5710
+ break;
5711
+ }
5601
5712
  default:
5602
5713
  break;
5603
5714
  }
5604
5715
  });
5716
+ this.runtimeError.set(runtimeError);
5717
+ this.isValueAssigned.set(isValueAssigned);
5605
5718
  }
5606
5719
  ngOnDestroy() {
5607
5720
  this._onDestroy.next();
@@ -5709,11 +5822,11 @@ class FormFieldComponent {
5709
5822
  this._runtimeOptions = [...options];
5710
5823
  }
5711
5824
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.8", ngImport: i0, type: FormFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
5712
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.1.8", type: FormFieldComponent, isStandalone: false, selector: "ng-dhis2-ui-form-field", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, fieldConfig: { classPropertyName: "fieldConfig", publicName: "fieldConfig", isSignal: false, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: false, isRequired: false, transformFunction: null }, isCheckBoxButton: { classPropertyName: "isCheckBoxButton", publicName: "isCheckBoxButton", isSignal: false, isRequired: false, transformFunction: null }, fieldClass: { classPropertyName: "fieldClass", publicName: "fieldClass", isSignal: false, isRequired: false, transformFunction: null }, programRuleActions: { classPropertyName: "programRuleActions", publicName: "programRuleActions", isSignal: true, isRequired: false, transformFunction: null }, dataEntities: { classPropertyName: "dataEntities", publicName: "dataEntities", isSignal: false, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: false, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { fieldUpdate: "fieldUpdate", immediateFieldUpdate: "immediateFieldUpdate", runtimeOptionChange: "runtimeOptionChange" }, usesOnChanges: true, ngImport: i0, template: "<div *ngIf=\"field()\" class=\"form-field-item\">\n <ng-dhis2-ui-form-field-item\n [field]=\"field()\"\n [isValueAssigned]=\"isValueAssigned()\"\n [fieldConfig]=\"fieldConfig\"\n [(form)]=\"form\"\n [value]=\"value()\"\n (update)=\"onValueChange()\"\n (immediateUpdate)=\"onImmediateChange($event)\"\n />\n</div>\n", styles: [".form-field-item{margin-bottom:16px}\n"], dependencies: [{ kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: FormFieldItemComponent, selector: "ng-dhis2-ui-form-field-item" }] }); }
5825
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.1.8", type: FormFieldComponent, isStandalone: false, selector: "ng-dhis2-ui-form-field", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, fieldConfig: { classPropertyName: "fieldConfig", publicName: "fieldConfig", isSignal: false, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: false, isRequired: false, transformFunction: null }, isCheckBoxButton: { classPropertyName: "isCheckBoxButton", publicName: "isCheckBoxButton", isSignal: false, isRequired: false, transformFunction: null }, fieldClass: { classPropertyName: "fieldClass", publicName: "fieldClass", isSignal: false, isRequired: false, transformFunction: null }, programRuleActions: { classPropertyName: "programRuleActions", publicName: "programRuleActions", isSignal: true, isRequired: false, transformFunction: null }, dataEntities: { classPropertyName: "dataEntities", publicName: "dataEntities", isSignal: false, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: false, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { fieldUpdate: "fieldUpdate", immediateFieldUpdate: "immediateFieldUpdate", runtimeOptionChange: "runtimeOptionChange" }, usesOnChanges: true, ngImport: i0, template: "<div *ngIf=\"field()\" class=\"form-field-item\">\n <ng-dhis2-ui-form-field-item\n [field]=\"field()\"\n [fieldError]=\"runtimeError()\"\n [isValueAssigned]=\"isValueAssigned()\"\n [fieldConfig]=\"fieldConfig\"\n [(form)]=\"form\"\n [value]=\"value()\"\n (update)=\"onValueChange()\"\n (immediateUpdate)=\"onImmediateChange($event)\"\n />\n</div>\n", styles: [".form-field-item{margin-bottom:16px}\n"], dependencies: [{ kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: FormFieldItemComponent, selector: "ng-dhis2-ui-form-field-item" }] }); }
5713
5826
  }
5714
5827
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.8", ngImport: i0, type: FormFieldComponent, decorators: [{
5715
5828
  type: Component,
5716
- args: [{ selector: 'ng-dhis2-ui-form-field', standalone: false, template: "<div *ngIf=\"field()\" class=\"form-field-item\">\n <ng-dhis2-ui-form-field-item\n [field]=\"field()\"\n [isValueAssigned]=\"isValueAssigned()\"\n [fieldConfig]=\"fieldConfig\"\n [(form)]=\"form\"\n [value]=\"value()\"\n (update)=\"onValueChange()\"\n (immediateUpdate)=\"onImmediateChange($event)\"\n />\n</div>\n", styles: [".form-field-item{margin-bottom:16px}\n"] }]
5829
+ args: [{ selector: 'ng-dhis2-ui-form-field', standalone: false, template: "<div *ngIf=\"field()\" class=\"form-field-item\">\n <ng-dhis2-ui-form-field-item\n [field]=\"field()\"\n [fieldError]=\"runtimeError()\"\n [isValueAssigned]=\"isValueAssigned()\"\n [fieldConfig]=\"fieldConfig\"\n [(form)]=\"form\"\n [value]=\"value()\"\n (update)=\"onValueChange()\"\n (immediateUpdate)=\"onImmediateChange($event)\"\n />\n</div>\n", styles: [".form-field-item{margin-bottom:16px}\n"] }]
5717
5830
  }], ctorParameters: () => [], propDecorators: { fieldConfig: [{
5718
5831
  type: Input
5719
5832
  }], form: [{
@@ -6647,6 +6760,7 @@ class ProgramEntryFormFieldUtil {
6647
6760
  sortOrder: programTrackedEntityAttribute.sortOrder,
6648
6761
  mandatory: programTrackedEntityAttribute.mandatory,
6649
6762
  allowFutureDate: programTrackedEntityAttribute.allowFutureDate,
6763
+ trackedEntityType: this.program.trackedEntityType?.id,
6650
6764
  metaType: 'ATTRIBUTE',
6651
6765
  stepId: null,
6652
6766
  };
@@ -6729,7 +6843,7 @@ class ProgramEntryFormFieldUtil {
6729
6843
  label: field.formName || field.name,
6730
6844
  key: field.code ? camelCase(field.code) : field.id,
6731
6845
  required: field.mandatory,
6732
- type: field.valueType,
6846
+ type: FieldUtil.getFieldType(field.valueType),
6733
6847
  options,
6734
6848
  disabled: this.#getDisabledStatus(field),
6735
6849
  order: field.sortOrder,
@@ -7000,7 +7114,7 @@ class ProgramEntryFormModule {
7000
7114
  const instance = await this.#getInstance();
7001
7115
  this.instance.set(instance);
7002
7116
  if (this.config().autoAssignedValues) {
7003
- this.#updateInstanceWithAutoAssignedValues(this.config().autoAssignedValues);
7117
+ this.#updateInstanceWithAutoAssignedValues(this.config().autoAssignedValues || []);
7004
7118
  }
7005
7119
  this.loading.set(false);
7006
7120
  }