@iapps/ng-dhis2-ui 1.2.114 → 1.2.116

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.
@@ -5456,7 +5456,17 @@ class FieldDropdown {
5456
5456
  order: this.order,
5457
5457
  };
5458
5458
  }
5459
- static getDropdownOptions(field, locale) {
5459
+ static getDropdownOptions(field, locale, extension) {
5460
+ if (extension?.organisationUnits &&
5461
+ extension.organisationUnits.length > 0) {
5462
+ return sortBy(extension.organisationUnits.map((orgUnit) => {
5463
+ return new FieldDropdown({
5464
+ key: orgUnit.id,
5465
+ value: orgUnit.id,
5466
+ label: orgUnit.name,
5467
+ });
5468
+ }), 'label');
5469
+ }
5460
5470
  if (field?.valueType === FieldType.BOOLEAN) {
5461
5471
  return [
5462
5472
  {
@@ -7636,7 +7646,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImpor
7636
7646
  // Use of this source code is governed by a BSD-style
7637
7647
  // license that can be found in the LICENSE file.
7638
7648
 
7639
- /* eslint-disable @angular-eslint/component-selector */
7640
7649
  class SelectionFiltersComponent extends ReactWrapperModule {
7641
7650
  constructor() {
7642
7651
  super(...arguments);
@@ -7651,9 +7660,20 @@ class SelectionFiltersComponent extends ReactWrapperModule {
7651
7660
  const [selected, setSelected] = useState([]);
7652
7661
  const [rootOrgUnits, setRootOrgUnits] = useState();
7653
7662
  const [config, setConfig] = useState();
7663
+ // TODO: START::: Improve approach on handling observables
7664
+ // useEffect(() => {
7665
+ // zip(this.getAppConfig(), this.getRootOrgUnits()).subscribe({
7666
+ // next: ([appConfig, rootOrgUnits]) => {
7667
+ // setConfig(appConfig);
7668
+ // setRootOrgUnits(rootOrgUnits);
7669
+ // },
7670
+ // error: (error) => console.error(error),
7671
+ // });
7672
+ // }, []);
7673
+ // TODO: END::: Improve approach on handling observables
7654
7674
  useEffect(() => {
7655
7675
  const subscription = zip(this.getAppConfig(), this.getRootOrgUnits())
7656
- .pipe(take(1))
7676
+ .pipe(take(1)) // Ensures only one emission
7657
7677
  .subscribe({
7658
7678
  next: ([appConfig, rootOrgUnits]) => {
7659
7679
  setConfig(appConfig);
@@ -7661,7 +7681,9 @@ class SelectionFiltersComponent extends ReactWrapperModule {
7661
7681
  },
7662
7682
  error: (error) => console.error(error),
7663
7683
  });
7664
- return () => subscription.unsubscribe();
7684
+ return () => {
7685
+ subscription.unsubscribe(); // Cleanup to avoid memory leaks
7686
+ };
7665
7687
  }, []);
7666
7688
  return config
7667
7689
  ? React__default.createElement(Provider, {
@@ -7669,7 +7691,7 @@ class SelectionFiltersComponent extends ReactWrapperModule {
7669
7691
  plugin: false,
7670
7692
  parentAlertsAdd: undefined,
7671
7693
  showAlertsInPlugin: false,
7672
- children: React__default.createElement(Modal$1, { position: 'middle', large: true }, React__default.createElement(ModalTitle$1, null, this.orgUnitFieldTitle ? this.orgUnitFieldTitle : 'Organisation Unit'), React__default.createElement(ModalContent$1, null, React__default.createElement(OrgUnitDimension, {
7694
+ children: React__default.createElement(Modal$1, { position: 'middle', large: true }, React__default.createElement(ModalTitle$1, null, 'Organisation unit'), React__default.createElement(ModalContent$1, null, React__default.createElement(OrgUnitDimension, {
7673
7695
  selected: selected,
7674
7696
  hideGroupSelect: this.orgUnitSelectionConfig.hideGroupSelect,
7675
7697
  hideLevelSelect: this.orgUnitSelectionConfig.hideLevelSelect,
@@ -7680,10 +7702,16 @@ class SelectionFiltersComponent extends ReactWrapperModule {
7680
7702
  orgUnitGroupPromise: this.getOrgUnitGroups(),
7681
7703
  orgUnitLevelPromise: this.getOrgUnitLevels(),
7682
7704
  roots: rootOrgUnits,
7683
- })), React__default.createElement(ModalActions$1, null, React__default.createElement(ButtonStrip$1, { end: true }, React__default.createElement(Button$1, { onClick: onCancelOrgUnit }, 'Cancel'), React__default.createElement(Button$1, {
7705
+ })), React__default.createElement(ModalActions$1, null, React__default.createElement(ButtonStrip$1, { end: true }, React__default.createElement(Button$1, {
7706
+ onClick: () => {
7707
+ onCancelOrgUnit();
7708
+ },
7709
+ }, 'Cancel'), React__default.createElement(Button$1, {
7684
7710
  primary: true,
7685
7711
  disabled: selected.length === 0,
7686
- onClick: () => onSelectOrgUnit(selected),
7712
+ onClick: () => {
7713
+ onSelectOrgUnit(selected);
7714
+ },
7687
7715
  }, 'Confirm')))),
7688
7716
  })
7689
7717
  : React__default.createElement(CircularLoader$1, { small: true });
@@ -7696,9 +7724,8 @@ class SelectionFiltersComponent extends ReactWrapperModule {
7696
7724
  allowSingleSelection: true,
7697
7725
  usageType: 'DATA_ENTRY',
7698
7726
  };
7699
- // Renamed to avoid shadowing the @Input
7700
- this.selectedOrgUnitSignal = signal(null, ...(ngDevMode ? [{ debugName: "selectedOrgUnitSignal" }] : []));
7701
- this.selectedOrgUnit$ = toObservable(this.selectedOrgUnitSignal);
7727
+ this.selectedOrgUnit = signal(null, ...(ngDevMode ? [{ debugName: "selectedOrgUnit" }] : []));
7728
+ this.selectedOrgUnit$ = toObservable(this.selectedOrgUnit);
7702
7729
  this.SelectionFiltersUI = () => {
7703
7730
  const [filters, setFilters] = useState({
7704
7731
  programAttributesFilters: this.programAttributesFilters || [],
@@ -7716,28 +7743,6 @@ class SelectionFiltersComponent extends ReactWrapperModule {
7716
7743
  const [value, setValue] = useState(null);
7717
7744
  const [showMoreFilters, setShowMoreFilters] = useState(false);
7718
7745
  const toggleMoreFilters = () => setShowMoreFilters((prev) => !prev);
7719
- useEffect(() => {
7720
- const su = this.selectedOrgUnit;
7721
- if (su) {
7722
- if (typeof su === 'object') {
7723
- const id = su.id ?? su.uid ?? su.code ?? su.value ?? undefined;
7724
- if (su.name) {
7725
- setDisplayValue(su.name);
7726
- }
7727
- if (id) {
7728
- setSelectedOrganisationUnit(id);
7729
- setFilters((prev) => ({ ...prev, organisationUnit: id }));
7730
- setValue(id);
7731
- }
7732
- }
7733
- else if (typeof su === 'string') {
7734
- setSelectedOrganisationUnit(su);
7735
- setFilters((prev) => ({ ...prev, organisationUnit: su }));
7736
- setDisplayValue(su);
7737
- setValue(su);
7738
- }
7739
- }
7740
- }, [this.selectedOrgUnit]);
7741
7746
  useEffect(() => {
7742
7747
  setFilters({
7743
7748
  programAttributesFilters: this.programAttributesFilters,
@@ -7752,8 +7757,6 @@ class SelectionFiltersComponent extends ReactWrapperModule {
7752
7757
  this.programStageDataElementFilters,
7753
7758
  this.startDate,
7754
7759
  this.endDate,
7755
- this.program,
7756
- this.organisationUnit,
7757
7760
  ]);
7758
7761
  const handleAttributeChange = (index, selectedValue) => {
7759
7762
  setFilters((prevFilters) => {
@@ -7792,36 +7795,62 @@ class SelectionFiltersComponent extends ReactWrapperModule {
7792
7795
  const getAttributeColumnSpan = (index) => {
7793
7796
  const totalItems = filters.programAttributesFilters.length;
7794
7797
  if (totalItems <= 4) {
7795
- if (totalItems === 1)
7798
+ if (totalItems === 1) {
7796
7799
  return 'span 4';
7797
- if (totalItems === 2)
7800
+ }
7801
+ if (totalItems === 2) {
7798
7802
  return 'span 2';
7799
- return 'span 1';
7803
+ }
7804
+ if (totalItems === 3) {
7805
+ return 'span 1';
7806
+ }
7807
+ if (totalItems === 4) {
7808
+ return 'span 1';
7809
+ }
7800
7810
  }
7801
7811
  else {
7802
- if (index % 2 === 0)
7812
+ const rowIndex = Math.floor(index / 2);
7813
+ if (index % 2 === 0) {
7803
7814
  return 'span 2';
7804
- if (index === totalItems - 1)
7815
+ }
7816
+ else if (index === totalItems - 1) {
7805
7817
  return 'span 4';
7806
- return 'span 2';
7818
+ }
7819
+ else {
7820
+ return 'span 2';
7821
+ }
7807
7822
  }
7823
+ return 'span 1';
7808
7824
  };
7809
7825
  const getDataElementColumnSpan = (index) => {
7810
7826
  const totalItems = filters.programStageDataElementFilters.length;
7811
7827
  if (totalItems <= 4) {
7812
- if (totalItems === 1)
7828
+ if (totalItems === 1) {
7813
7829
  return 'span 4';
7814
- if (totalItems === 2)
7830
+ }
7831
+ if (totalItems === 2) {
7815
7832
  return 'span 2';
7816
- return 'span 1';
7833
+ }
7834
+ if (totalItems === 3) {
7835
+ return 'span 1';
7836
+ }
7837
+ if (totalItems === 4) {
7838
+ return 'span 1';
7839
+ }
7817
7840
  }
7818
7841
  else {
7819
- if (index % 2 === 0)
7842
+ const rowIndex = Math.floor(index / 2);
7843
+ if (index % 2 === 0) {
7820
7844
  return 'span 2';
7821
- if (index === totalItems - 1)
7845
+ }
7846
+ else if (index === totalItems - 1) {
7822
7847
  return 'span 4';
7823
- return 'span 2';
7848
+ }
7849
+ else {
7850
+ return 'span 2';
7851
+ }
7824
7852
  }
7853
+ return 'span 1';
7825
7854
  };
7826
7855
  return React__default.createElement('div', {
7827
7856
  style: {
@@ -7834,32 +7863,40 @@ class SelectionFiltersComponent extends ReactWrapperModule {
7834
7863
  }, React__default.createElement('div', {
7835
7864
  style: {
7836
7865
  display: 'grid',
7837
- gridTemplateColumns: '1fr 1fr 1fr auto',
7866
+ gridTemplateColumns: '1fr 1fr 1fr auto', // Ensure proper button sizing
7838
7867
  gap: '16px',
7839
- // 👇 Bottom-align everything in the row so labels don't push inputs higher than the button
7840
- alignItems: 'end',
7868
+ alignItems: 'center', // Align inputs and button on the same row
7841
7869
  marginBottom: '16px',
7842
7870
  },
7843
7871
  },
7844
- // Organization Unit
7872
+ /* Organization Unit Selector */
7845
7873
  React__default.createElement('div', { style: { width: '100%' } }, React__default.createElement(InputField$1, {
7846
7874
  value: displayValue,
7847
- label: this.orgUnitFieldTitle ? this.orgUnitFieldTitle : 'Organisation Unit',
7875
+ label: 'Organization Unit',
7848
7876
  readOnly: false,
7849
7877
  onFocus: () => setShowOrgUnit(true),
7878
+ onBlur: () => {
7879
+ setTouched(true);
7880
+ },
7850
7881
  }), showOrgUnit &&
7851
7882
  React__default.createElement(this.FieldOrgUnitSelector, {
7852
- onCancelOrgUnit: () => setShowOrgUnit(false),
7883
+ onCancelOrgUnit: () => {
7884
+ setShowOrgUnit(false);
7885
+ setTouched(true);
7886
+ },
7853
7887
  onSelectOrgUnit: (selectedOrgUnits) => {
7854
7888
  if ((selectedOrgUnits || [])[0]) {
7855
7889
  const selectedOrgUnit = selectedOrgUnits[0];
7856
7890
  setDisplayValue(selectedOrgUnit.name);
7891
+ setSelected(selectedOrgUnit.id);
7857
7892
  setSelectedOrganisationUnit(selectedOrgUnit.id);
7893
+ setValue(selectedOrgUnit.id);
7858
7894
  setShowOrgUnit(false);
7895
+ setTouched(true);
7859
7896
  }
7860
7897
  },
7861
7898
  })),
7862
- // Start Date
7899
+ /* Start Date */
7863
7900
  React__default.createElement(InputField$1, {
7864
7901
  className: 'input-field',
7865
7902
  label: 'Start Date',
@@ -7867,7 +7904,7 @@ class SelectionFiltersComponent extends ReactWrapperModule {
7867
7904
  value: filters.startDate,
7868
7905
  onChange: (event) => setFilters({ ...filters, startDate: event.value }),
7869
7906
  }),
7870
- // End Date
7907
+ /* End Date */
7871
7908
  React__default.createElement(InputField$1, {
7872
7909
  className: 'input-field',
7873
7910
  label: 'End Date',
@@ -7875,15 +7912,84 @@ class SelectionFiltersComponent extends ReactWrapperModule {
7875
7912
  value: filters.endDate,
7876
7913
  onChange: (event) => setFilters({ ...filters, endDate: event.value }),
7877
7914
  }),
7878
- // Action Button (same line, bottom-aligned)
7915
+ /* Toggle Button with Hidden Label */
7879
7916
  React__default.createElement('div', {
7880
7917
  style: {
7881
7918
  display: 'flex',
7882
- justifyContent: 'flex-end',
7883
- // 👇 Ensure this cell itself bottoms out in the grid row
7884
- alignSelf: 'end',
7919
+ flexDirection: 'column',
7920
+ alignItems: 'center',
7921
+ justifyContent: 'center',
7922
+ height: '100%',
7923
+ },
7924
+ }, React__default.createElement('label', {
7925
+ style: {
7926
+ color: 'transparent', // Hides label text but keeps spacing
7927
+ fontSize: '14px',
7928
+ marginBottom: '4px',
7885
7929
  },
7886
- }, React__default.createElement(Button$1, { style: { height: '40px' }, onClick: handleSearch }, 'Generate Report'))));
7930
+ }, 'Toggle'), React__default.createElement(Button$1, {
7931
+ style: { height: '40px' }, // Match input field height
7932
+ onClick: () => setShowMoreFilters(!showMoreFilters),
7933
+ }, showMoreFilters ? 'Hide Filters' : 'More Filters')), showMoreFilters &&
7934
+ React__default.createElement(React__default.Fragment, null,
7935
+ /* Attribute Filters Grid */
7936
+ React__default.createElement('div', {
7937
+ style: {
7938
+ display: 'grid',
7939
+ gridTemplateColumns: 'repeat(4, 1fr)',
7940
+ gap: '16px',
7941
+ },
7942
+ }, filters.programAttributesFilters.map((filter, index) => React__default.createElement('div', {
7943
+ key: index,
7944
+ style: { gridColumn: getAttributeColumnSpan(index) },
7945
+ }, filter.hasOptions && filter.options.length > 0
7946
+ ? React__default.createElement(SingleSelectField, {
7947
+ className: 'single-select',
7948
+ label: filter.name || 'Select Option',
7949
+ selected: filter.value,
7950
+ onChange: (event) => handleAttributeChange(index, event.selected),
7951
+ }, filter.options.map((option, i) => React__default.createElement(SingleSelectOption, {
7952
+ key: crypto.randomUUID() || i,
7953
+ label: option.name,
7954
+ value: option.code,
7955
+ })))
7956
+ : React__default.createElement(InputField$1, {
7957
+ className: 'input-field',
7958
+ label: filter.name || 'Program name',
7959
+ type: filter.valueType,
7960
+ value: filter.value,
7961
+ onChange: (event) => handleAttributeChange(index, event.value),
7962
+ })))),
7963
+ /* Data Element Filters Grid */
7964
+ React__default.createElement('div', {
7965
+ style: {
7966
+ display: 'grid',
7967
+ gridTemplateColumns: 'repeat(4, 1fr)',
7968
+ gap: '16px',
7969
+ },
7970
+ }, filters.programStageDataElementFilters.map((filter, index) => React__default.createElement('div', {
7971
+ key: index,
7972
+ style: { gridColumn: getDataElementColumnSpan(index) },
7973
+ }, filter.hasOptions && filter.options.length > 0
7974
+ ? React__default.createElement(SingleSelectField, {
7975
+ className: 'single-select',
7976
+ label: filter.name || 'Aggregation type',
7977
+ selected: filter.value,
7978
+ onChange: (event) => handleDataElementChange(index, event.selected),
7979
+ }, filter.options.map((option, i) => React__default.createElement(SingleSelectOption, {
7980
+ key: crypto.randomUUID() || i,
7981
+ label: option.name,
7982
+ value: option.code,
7983
+ })))
7984
+ : React__default.createElement(InputField$1, {
7985
+ className: 'input-field',
7986
+ type: filter.valueType,
7987
+ label: filter.name || 'Program name',
7988
+ value: filter.value,
7989
+ onChange: (event) => handleDataElementChange(index, event.value),
7990
+ })))),
7991
+ /* Search Button */
7992
+ React__default.createElement('div', { style: { textAlign: 'right', marginTop: '16px' } }, React__default.createElement(Button$1, { onClick: handleSearch }, 'Search')))));
7887
7993
  };
7888
7994
  }
7889
7995
  async getAppConfig() {
@@ -7918,7 +8024,7 @@ class SelectionFiltersComponent extends ReactWrapperModule {
7918
8024
  onSelectOrgUnit(selectedOrgUnits) {
7919
8025
  this.showOrgUnitTree.set(false);
7920
8026
  if ((selectedOrgUnits || [])[0]) {
7921
- this.selectedOrgUnitSignal.set(selectedOrgUnits[0]);
8027
+ this.selectedOrgUnit.set(selectedOrgUnits[0]);
7922
8028
  }
7923
8029
  }
7924
8030
  getOrgUnitAttributeByUsage(usageType) {
@@ -7939,11 +8045,11 @@ class SelectionFiltersComponent extends ReactWrapperModule {
7939
8045
  this.render();
7940
8046
  }
7941
8047
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: SelectionFiltersComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
7942
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.3", type: SelectionFiltersComponent, isStandalone: false, selector: "app-selection-filters-ui", inputs: { actionOptions: "actionOptions", programAttributesFilters: "programAttributesFilters", programStageDataElementFilters: "programStageDataElementFilters", startDate: "startDate", endDate: "endDate", program: "program", organisationUnit: "organisationUnit", selectedOrgUnit: "selectedOrgUnit", orgUnitFieldTitle: "orgUnitFieldTitle" }, outputs: { actionSelected: "actionSelected" }, usesInheritance: true, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, styles: [".react-filters-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;margin-bottom:20px}.input-field,.single-select{width:100%}.clickable-field input{cursor:pointer!important}\n"] }); }
8048
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.3", type: SelectionFiltersComponent, isStandalone: false, selector: "app-selection-filters-ui", inputs: { actionOptions: "actionOptions", programAttributesFilters: "programAttributesFilters", programStageDataElementFilters: "programStageDataElementFilters", startDate: "startDate", endDate: "endDate", program: "program", organisationUnit: "organisationUnit" }, outputs: { actionSelected: "actionSelected" }, usesInheritance: true, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, styles: [".react-filters-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;margin-bottom:20px}.input-field,.single-select{width:100%}\n"] }); }
7943
8049
  }
7944
8050
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: SelectionFiltersComponent, decorators: [{
7945
8051
  type: Component,
7946
- args: [{ selector: 'app-selection-filters-ui', template: '<ng-content></ng-content>', standalone: false, styles: [".react-filters-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;margin-bottom:20px}.input-field,.single-select{width:100%}.clickable-field input{cursor:pointer!important}\n"] }]
8052
+ args: [{ selector: 'app-selection-filters-ui', template: '<ng-content></ng-content>', standalone: false, styles: [".react-filters-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;margin-bottom:20px}.input-field,.single-select{width:100%}\n"] }]
7947
8053
  }], propDecorators: { actionOptions: [{
7948
8054
  type: Input
7949
8055
  }], programAttributesFilters: [{
@@ -7958,10 +8064,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImpor
7958
8064
  type: Input
7959
8065
  }], organisationUnit: [{
7960
8066
  type: Input
7961
- }], selectedOrgUnit: [{
7962
- type: Input
7963
- }], orgUnitFieldTitle: [{
7964
- type: Input
7965
8067
  }], actionSelected: [{
7966
8068
  type: Output
7967
8069
  }] } });
@@ -8230,9 +8332,9 @@ class ProgramEntryFormFieldUtil {
8230
8332
  get fields() {
8231
8333
  const fields = [...this.attributes, ...this.dataElements]
8232
8334
  .map((field) => {
8233
- const options = FieldDropdown.getDropdownOptions(field);
8234
- const hasOptions = options?.length > 0;
8235
8335
  const extension = this.config.formFieldExtensions?.find((fieldExtension) => fieldExtension?.id === field.id);
8336
+ const options = FieldDropdown.getDropdownOptions(field, undefined, extension);
8337
+ const hasOptions = options?.length > 0;
8236
8338
  const autoAssignedValue = this.#getAutoAssignedValue(field);
8237
8339
  if (!isUndefined(autoAssignedValue) &&
8238
8340
  this.config.hideCustomAssignedFields) {
@@ -8438,6 +8540,14 @@ class ProgramEntryFormMetaData {
8438
8540
  const program = await this.#getProgramMetaData();
8439
8541
  if (program) {
8440
8542
  this.program = program;
8543
+ if (this.config?.formFieldExtensions &&
8544
+ this.config.formFieldExtensions.length > 0 &&
8545
+ this.config.formFieldExtensions[0]?.optionsSourceCode) {
8546
+ const orgUnitResponse = await this.d2.httpInstance.get(`organisationUnits.json?fields=id,name,code&paging=false&filter=organisationUnitGroups.code:eq:${this.config.formFieldExtensions[0].optionsSourceCode}`);
8547
+ const orgUnitsResponse = orgUnitResponse?.data?.['organisationUnits'];
8548
+ const organisationUnits = Array.isArray(orgUnitsResponse) ? orgUnitsResponse : [];
8549
+ this.config.formFieldExtensions[0].organisationUnits = organisationUnits;
8550
+ }
8441
8551
  return this.toJson();
8442
8552
  }
8443
8553
  return null;
@@ -8487,9 +8597,6 @@ class ProgramEntryFormModule {
8487
8597
  this.FormLoader = () => {
8488
8598
  return React__default.createElement(CircularLoader$1, { small: true });
8489
8599
  };
8490
- // dataId = computed(() => {
8491
- // return this.instance()?.trackedEntity || this.instance()?.event;
8492
- // });
8493
8600
  this.dataId = computed(() => {
8494
8601
  if (this.config().formType === 'EVENT') {
8495
8602
  return this.instance()?.event;