@iapps/ng-dhis2-ui 1.2.165 → 1.2.166

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.
@@ -4670,6 +4670,141 @@ const getEvents = (response, programStageId, pager, metaData, columnNameSource =
4670
4670
  return { columns: entityColumns, data: dataElementsData };
4671
4671
  };
4672
4672
 
4673
+ const getCellValue = (cell) => {
4674
+ if (cell === null || cell === undefined)
4675
+ return '';
4676
+ if (typeof cell === 'object') {
4677
+ if ('value' in cell)
4678
+ return cell.value;
4679
+ return JSON.stringify(cell);
4680
+ }
4681
+ return cell;
4682
+ };
4683
+ const getExportColumns = (columns) => columns.filter((column) => column.label !== 'Actions');
4684
+ const downloadLineListExcel = (columns, data, fileName = 'records') => {
4685
+ const exportColumns = getExportColumns(columns);
4686
+ const header = exportColumns.map((column) => column.label);
4687
+ const rows = data.map((row) => exportColumns.map((column) => getCellValue(row[column.key])));
4688
+ const worksheet = XLSX.utils.aoa_to_sheet([header, ...rows]);
4689
+ const workbook = XLSX.utils.book_new();
4690
+ XLSX.utils.book_append_sheet(workbook, worksheet, 'LineListData');
4691
+ XLSX.writeFile(workbook, `${fileName}.xlsx`);
4692
+ };
4693
+ const downloadLineListCsv = (columns, data, fileName = 'records') => {
4694
+ const exportColumns = getExportColumns(columns);
4695
+ const escapeCsvValue = (value) => `"${String(getCellValue(value)).replace(/"/g, '""')}"`;
4696
+ const header = exportColumns.map((column) => escapeCsvValue(column.label));
4697
+ const rows = data.map((row) => exportColumns.map((column) => escapeCsvValue(row[column.key])));
4698
+ const csvContent = [header, ...rows].map((row) => row.join(',')).join('\n');
4699
+ const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
4700
+ const url = URL.createObjectURL(blob);
4701
+ const link = document.createElement('a');
4702
+ link.href = url;
4703
+ link.setAttribute('download', `${fileName}.csv`);
4704
+ document.body.appendChild(link);
4705
+ link.click();
4706
+ document.body.removeChild(link);
4707
+ URL.revokeObjectURL(url);
4708
+ };
4709
+
4710
+ const applyDownloadColumnFilter = (columns, filterableColumnIds) => {
4711
+ if (!Array.isArray(filterableColumnIds) || filterableColumnIds.length === 0) {
4712
+ return columns;
4713
+ }
4714
+ const filterableColumnIdsSet = new Set(filterableColumnIds);
4715
+ return columns.filter((column) => filterableColumnIdsSet.has(column.key));
4716
+ };
4717
+ const fetchLineListDownloadRows = async ({ d2, metaData, programId, programStageId, orgUnit, ouMode, startDate, endDate, dataQueryFilters, enrollmentStatus, eventStatus, filterableColumnIds, searcheableDataElements, customDisplayInReportsIds, columnNameSource, fetchOrgUnits, }) => {
4718
+ if (!metaData) {
4719
+ return { columns: [], data: [] };
4720
+ }
4721
+ const isTracker = metaData.programType === 'WITH_REGISTRATION';
4722
+ const isEvent = metaData.programType === 'WITHOUT_REGISTRATION';
4723
+ const downloadPager = new Pager({ paging: false });
4724
+ const resolvedEndDate = endDate ?? format(addDays(new Date(), 1), 'yyyy-MM-dd');
4725
+ if (programStageId || isEvent) {
4726
+ const eventQuery = d2.eventModule.event
4727
+ .setEndDate(resolvedEndDate)
4728
+ .setStartDate(startDate)
4729
+ .setProgram(programId)
4730
+ .setOrgUnit(orgUnit)
4731
+ .setOuMode(ouMode)
4732
+ .setPagination(downloadPager);
4733
+ if (programStageId) {
4734
+ eventQuery.setProgramStage(programStageId);
4735
+ }
4736
+ const response = await eventQuery.get();
4737
+ const events = Array.isArray(response.data)
4738
+ ? response.data
4739
+ : response.data
4740
+ ? [response.data]
4741
+ : [];
4742
+ const eventsResponse = {
4743
+ events,
4744
+ pager: new Pager({
4745
+ page: 1,
4746
+ pageSize: events.length || 1,
4747
+ }),
4748
+ };
4749
+ const downloadRows = getEvents({ data: eventsResponse }, programStageId || '', eventsResponse.pager, metaData, columnNameSource);
4750
+ return {
4751
+ columns: applyDownloadColumnFilter(downloadRows.columns, filterableColumnIds),
4752
+ data: downloadRows.data,
4753
+ };
4754
+ }
4755
+ if (isTracker) {
4756
+ const trackerQuery = d2.trackerModule.trackedEntity
4757
+ .setEndDate(resolvedEndDate)
4758
+ .setStartDate(startDate)
4759
+ .setProgram(programId)
4760
+ .setOrgUnit(orgUnit)
4761
+ .setOuMode(ouMode)
4762
+ .setStatus(enrollmentStatus)
4763
+ .setFilters(dataQueryFilters)
4764
+ .setPagination(downloadPager)
4765
+ .setOrderCriterias([
4766
+ new DataOrderCriteria().setField('createdAt').setOrder('desc'),
4767
+ ]);
4768
+ if (eventStatus?.status && eventStatus?.programStage) {
4769
+ trackerQuery.setEventStatus(eventStatus.status, eventStatus.programStage);
4770
+ }
4771
+ if (programStageId) {
4772
+ trackerQuery.setProgramStage(programStageId);
4773
+ }
4774
+ const response = await trackerQuery.get();
4775
+ const trackedEntityInstances = Array.isArray(response.data)
4776
+ ? response.data
4777
+ : response.data
4778
+ ? [response.data]
4779
+ : [];
4780
+ const uniqueOrgUnitIds = Array.from(new Set(trackedEntityInstances
4781
+ .flatMap((tei) => [
4782
+ tei.latestEnrollment?.orgUnit,
4783
+ ...(tei.attributes
4784
+ ?.filter((attr) => attr.valueType === 'ORGANISATION_UNIT')
4785
+ .map((attr) => attr.value) || []),
4786
+ ])
4787
+ .filter((orgUnitId) => !!orgUnitId)));
4788
+ const fetchedOrgUnits = uniqueOrgUnitIds.length > 0
4789
+ ? await firstValueFrom(fetchOrgUnits(uniqueOrgUnitIds))
4790
+ : new Map();
4791
+ const trackedEntityResponse = {
4792
+ trackedEntityInstances,
4793
+ pager: new Pager({
4794
+ page: 1,
4795
+ pageSize: trackedEntityInstances.length || 1,
4796
+ }),
4797
+ orgUnitsMap: fetchedOrgUnits,
4798
+ };
4799
+ const downloadRows = getTrackedEntityTableData({ data: trackedEntityResponse }, programId, trackedEntityResponse.pager, metaData, searcheableDataElements, customDisplayInReportsIds, columnNameSource);
4800
+ return {
4801
+ columns: applyDownloadColumnFilter(downloadRows.columns, filterableColumnIds),
4802
+ data: downloadRows.data,
4803
+ };
4804
+ }
4805
+ return { columns: [], data: [] };
4806
+ };
4807
+
4673
4808
  class LineListTableComponent extends ReactWrapperModule {
4674
4809
  constructor() {
4675
4810
  super(...arguments);
@@ -4718,6 +4853,7 @@ class LineListTableComponent extends ReactWrapperModule {
4718
4853
  const [endDateState, setEndDateState] = useState(this.endDate);
4719
4854
  const [tempEndDateState, setTempEndDateState] = useState(this.endDate);
4720
4855
  const [loading, setLoading] = useState(false);
4856
+ const [downloadLoading, setDownloadLoading] = useState(false);
4721
4857
  const [filteredColumns, setFilteredColumns] = useState();
4722
4858
  const [inputValues, setInputValues] = useState({});
4723
4859
  const [orgUnitLabel, setOrgUnitLabel] = useState('');
@@ -4941,47 +5077,41 @@ class LineListTableComponent extends ReactWrapperModule {
4941
5077
  triggerRefetch,
4942
5078
  triggerTokenState,
4943
5079
  ]);
4944
- const handleExcelDownload = () => {
4945
- const filteredColumns = columns.filter((col) => col.label !== 'Actions');
4946
- const header = filteredColumns.map((col) => col.label);
4947
- const rows = data.map((row) => filteredColumns.map((col) => {
4948
- const cell = row[col.key];
4949
- if (cell === null || cell === undefined)
4950
- return '';
4951
- return typeof cell === 'object' && 'value' in cell
4952
- ? cell.value
4953
- : cell;
4954
- }));
4955
- const worksheetData = [header, ...rows];
4956
- const worksheet = XLSX.utils.aoa_to_sheet(worksheetData);
4957
- const workbook = XLSX.utils.book_new();
4958
- XLSX.utils.book_append_sheet(workbook, worksheet, 'LineListData');
4959
- XLSX.writeFile(workbook, 'records.xlsx');
4960
- };
4961
- const handleCsvDownload = () => {
4962
- const safeValue = (value) => {
4963
- if (value === null || value === undefined)
4964
- return '';
4965
- if (typeof value === 'object') {
4966
- if ('value' in value) {
4967
- return value.value;
4968
- }
4969
- return JSON.stringify(value);
5080
+ //TODO: ADD DOWNLOADING SUPPORT USING ANALYTICS
5081
+ const handleDownload = async (format) => {
5082
+ try {
5083
+ setDownloadLoading(true);
5084
+ const { columns, data } = await fetchLineListDownloadRows({
5085
+ d2,
5086
+ metaData,
5087
+ programId: programIdState,
5088
+ programStageId: programStageIdState,
5089
+ orgUnit: orgUnitState,
5090
+ ouMode: this.ouMode,
5091
+ startDate: startDateState,
5092
+ endDate: endDateState,
5093
+ dataQueryFilters: dataQueryFiltersState,
5094
+ enrollmentStatus: this.enrollmentStatus,
5095
+ eventStatus: this.eventStatus,
5096
+ filterableColumnIds: this.filterableColumnIds,
5097
+ searcheableDataElements: this.searcheableDataElements,
5098
+ customDisplayInReportsIds: this.customDisplayInReportsIds,
5099
+ columnNameSource: this.columnNameSource,
5100
+ fetchOrgUnits: (orgUnitIds) => this.lineListService.fetchOrgUnits(orgUnitIds),
5101
+ });
5102
+ if (format === 'CSV') {
5103
+ downloadLineListCsv(columns, data);
5104
+ return;
4970
5105
  }
4971
- return value;
4972
- };
4973
- const filteredColumns = columns.filter((col) => col.label !== 'Actions');
4974
- const header = filteredColumns.map((col) => `"${col.label}"`).join(',');
4975
- const csvData = data.map((row) => filteredColumns.map((col) => `"${safeValue(row[col.key])}"`).join(','));
4976
- const csvContent = [header, ...csvData].join('\n');
4977
- const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
4978
- const url = URL.createObjectURL(blob);
4979
- const link = document.createElement('a');
4980
- link.href = url;
4981
- link.setAttribute('download', 'records.csv');
4982
- document.body.appendChild(link);
4983
- link.click();
4984
- document.body.removeChild(link);
5106
+ downloadLineListExcel(columns, data);
5107
+ }
5108
+ catch (error) {
5109
+ console.error('Failed to download line list data:', error);
5110
+ setError('Could not download records. Please try again.');
5111
+ }
5112
+ finally {
5113
+ setDownloadLoading(false);
5114
+ }
4985
5115
  };
4986
5116
  const handleInputChange = (key, value, type) => {
4987
5117
  setInputValues((prevValues) => ({
@@ -5117,7 +5247,7 @@ class LineListTableComponent extends ReactWrapperModule {
5117
5247
  // Returns black for light backgrounds, white for dark backgrounds
5118
5248
  return yiq >= 128 ? colors$1.grey700 : '#FFFFFF';
5119
5249
  }
5120
- return React__default.createElement('div', null, loading
5250
+ return React__default.createElement('div', { className: 'line-list-root' }, loading
5121
5251
  ? React__default.createElement('div', {
5122
5252
  style: {
5123
5253
  display: 'flex',
@@ -5132,22 +5262,26 @@ class LineListTableComponent extends ReactWrapperModule {
5132
5262
  }, 'Loading'))
5133
5263
  : React__default.createElement('div', null, this.showDownloadButton &&
5134
5264
  React__default.createElement('div', {
5265
+ className: 'line-list-download-toolbar',
5135
5266
  style: {
5136
5267
  paddingBottom: '1rem',
5137
- display: 'flex',
5138
- justifyContent: 'flex-end',
5139
5268
  },
5140
5269
  }, React__default.createElement(DropdownButton, {
5141
5270
  name: 'Download',
5271
+ disabled: downloadLoading,
5142
5272
  component: React__default.createElement('span', null, ' ', React__default.createElement(MenuItem$1, {
5143
- label: 'Download Csv',
5144
- onClick: handleCsvDownload,
5273
+ label: 'CSV',
5274
+ disabled: downloadLoading,
5275
+ onClick: () => handleDownload('CSV'),
5145
5276
  }), React__default.createElement(MenuItem$1, {
5146
- label: 'Download Excel',
5147
- onClick: handleExcelDownload,
5277
+ label: 'Excel',
5278
+ disabled: downloadLoading,
5279
+ onClick: () => handleDownload('XLSX'),
5148
5280
  })),
5149
5281
  value: 'Download',
5150
- }, 'Download')), orgUnitModalVisible &&
5282
+ }, downloadLoading
5283
+ ? React__default.createElement(CircularLoader$1, { small: true })
5284
+ : 'Download')), orgUnitModalVisible &&
5151
5285
  React__default.createElement(OrgUnitSelector, {
5152
5286
  hide: hide,
5153
5287
  setHide: setHide,
@@ -5200,7 +5334,7 @@ class LineListTableComponent extends ReactWrapperModule {
5200
5334
  prevSelectedOrgUnit: prevSelectedOrgUnit,
5201
5335
  setPrevSelectedOrgUnit: setPrevSelectedOrgUnit,
5202
5336
  // filteredFilters: filteredFilters
5203
- }), React__default.createElement(LineListTable, {
5337
+ }), React__default.createElement('div', { className: 'line-list-table-scroll' }, React__default.createElement(LineListTable, {
5204
5338
  columns: columns,
5205
5339
  data: data,
5206
5340
  pager: pager,
@@ -5214,7 +5348,7 @@ class LineListTableComponent extends ReactWrapperModule {
5214
5348
  rowsSelected: this.rowsSelected,
5215
5349
  showActionButtons: showActionButtons,
5216
5350
  error: error,
5217
- })));
5351
+ }))));
5218
5352
  };
5219
5353
  }
5220
5354
  ngOnChanges(changes) {
@@ -5263,11 +5397,11 @@ class LineListTableComponent extends ReactWrapperModule {
5263
5397
  this.render();
5264
5398
  }
5265
5399
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: LineListTableComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
5266
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.3", type: LineListTableComponent, isStandalone: false, selector: "ng-dhis2-ui-line-list", inputs: { triggerToken: "triggerToken", programId: "programId", orgUnit: "orgUnit", programStageId: "programStageId", actionOptions: "actionOptions", actionOptionOrientation: "actionOptionOrientation", attributeFilters: "attributeFilters", useOuModeWithOlderDHIS2Instance: "useOuModeWithOlderDHIS2Instance", startDate: "startDate", endDate: "endDate", dataQueryFilters: "dataQueryFilters", lineListRowActionFilterConfig: "lineListRowActionFilterConfig", hydrateForActionFiltering: "hydrateForActionFiltering", ouMode: "ouMode", isButtonLoading: "isButtonLoading", buttonFilter: "buttonFilter", filterRootOrgUnit: "filterRootOrgUnit", showFilters: "showFilters", isOptionSetNameVisible: "isOptionSetNameVisible", allowRowsSelection: "allowRowsSelection", enrollmentStatus: "enrollmentStatus", filterableColumnIds: "filterableColumnIds", triggerRefetch: "triggerRefetch", eventStatus: "eventStatus", showActionButtons: "showActionButtons", showEnrollmentDates: "showEnrollmentDates", showDownloadButton: "showDownloadButton", searcheableDataElements: "searcheableDataElements", columnNameSource: "columnNameSource", customDisplayInReportsIds: "customDisplayInReportsIds" }, outputs: { actionSelected: "actionSelected", rowsSelected: "rowsSelected" }, usesInheritance: true, usesOnChanges: true, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, styles: ["@charset \"UTF-8\";::ng-deep .custom-input{display:flex!important;gap:4px!important;align-items:center!important;border:1px solid #9fadba!important;border-radius:4px!important;margin:0;padding:0 0 0 8px;background-color:#fff!important}::ng-deep .custom-input div{margin:0!important}::ng-deep .custom-input label{font-size:12px;padding-right:0}::ng-deep .custom-input input{border:none!important;font-size:12px!important;display:flex;align-items:center;padding-left:4px;height:40px;outline:none!important;box-shadow:none!important}::ng-deep .custom-input input:focus{border:none!important;box-shadow:none!important}::ng-deep .custom-input [data-test=calendar-clear-button]{background-color:#f2f2f2!important;color:#870808!important;border-radius:6px!important;padding:4px 10px!important;font-size:0!important}::ng-deep .custom-input [data-test=calendar-clear-button]:before{content:\"\\2715\";font-size:14px;color:#870808}::ng-deep .custom-input [data-test=calendar-clear-button]:hover{background-color:#ddd}::ng-deep .calendar-input-wrapper{position:relative}::ng-deep .calendar-input-wrapper .custom-input{position:relative;width:100%;display:flex;align-items:center}::ng-deep .calendar-input-wrapper [data-test=calendar-clear-button]{right:8px;bottom:14px;margin:auto;display:flex;align-items:center;justify-content:center;width:16px;height:16px;font-size:0!important;background-color:#a0adba!important;border-radius:50%;padding:2px;cursor:pointer}::ng-deep .calendar-input-wrapper [data-test=calendar-clear-button]:before{content:\"\\2715\";font-size:10px;color:#f0f0f0}::ng-deep .calendar-input-wrapper input{padding-right:40px!important;height:40px;line-height:40px;box-sizing:border-box}::ng-deep .custom-input label{color:gray!important;opacity:1}::ng-deep .table-top-toolbar{background-color:var(--colors-grey100)!important;color:var(--colors-grey900)!important}::ng-deep .custom-select-input{display:flex!important;gap:4px!important;align-items:center!important;border:1px solid #9fadba!important;border-radius:4px!important;margin:0;padding:0 0 0 8px;background-color:#fff!important}::ng-deep .custom-select-input div{margin:0!important}::ng-deep .custom-select-input [data-test=dhis2-uiwidgets-singleselectfield-label]{font-size:12px;color:gray!important}::ng-deep .custom-select-input [data-test=dhis2-uicore-select-input]{border:none!important;font-size:12px!important;display:flex;align-items:center;padding-left:4px;height:40px;outline:none!important;box-shadow:none!important}::ng-deep .custom-select-input [data-test=dhis2-uicore-select-input]:focus-within{box-shadow:none!important}::ng-deep .custom-select-input input:focus{border:none!important;box-shadow:none!important}\n"] }); }
5400
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.3", type: LineListTableComponent, isStandalone: false, selector: "ng-dhis2-ui-line-list", inputs: { triggerToken: "triggerToken", programId: "programId", orgUnit: "orgUnit", programStageId: "programStageId", actionOptions: "actionOptions", actionOptionOrientation: "actionOptionOrientation", attributeFilters: "attributeFilters", useOuModeWithOlderDHIS2Instance: "useOuModeWithOlderDHIS2Instance", startDate: "startDate", endDate: "endDate", dataQueryFilters: "dataQueryFilters", lineListRowActionFilterConfig: "lineListRowActionFilterConfig", hydrateForActionFiltering: "hydrateForActionFiltering", ouMode: "ouMode", isButtonLoading: "isButtonLoading", buttonFilter: "buttonFilter", filterRootOrgUnit: "filterRootOrgUnit", showFilters: "showFilters", isOptionSetNameVisible: "isOptionSetNameVisible", allowRowsSelection: "allowRowsSelection", enrollmentStatus: "enrollmentStatus", filterableColumnIds: "filterableColumnIds", triggerRefetch: "triggerRefetch", eventStatus: "eventStatus", showActionButtons: "showActionButtons", showEnrollmentDates: "showEnrollmentDates", showDownloadButton: "showDownloadButton", searcheableDataElements: "searcheableDataElements", columnNameSource: "columnNameSource", customDisplayInReportsIds: "customDisplayInReportsIds" }, outputs: { actionSelected: "actionSelected", rowsSelected: "rowsSelected" }, usesInheritance: true, usesOnChanges: true, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, styles: ["@charset \"UTF-8\";::ng-deep .line-list-root{width:100%;max-width:100%;min-width:0}::ng-deep .line-list-download-toolbar{position:sticky;left:0;z-index:2;display:flex;justify-content:flex-end;width:100%;max-width:100%;box-sizing:border-box;background:#fff}::ng-deep .line-list-table-scroll{width:100%;max-width:100%;overflow-x:auto}::ng-deep .custom-input{display:flex!important;gap:4px!important;align-items:center!important;border:1px solid #9fadba!important;border-radius:4px!important;margin:0;padding:0 0 0 8px;background-color:#fff!important}::ng-deep .custom-input div{margin:0!important}::ng-deep .custom-input label{font-size:12px;padding-right:0}::ng-deep .custom-input input{border:none!important;font-size:12px!important;display:flex;align-items:center;padding-left:4px;height:40px;outline:none!important;box-shadow:none!important}::ng-deep .custom-input input:focus{border:none!important;box-shadow:none!important}::ng-deep .custom-input [data-test=calendar-clear-button]{background-color:#f2f2f2!important;color:#870808!important;border-radius:6px!important;padding:4px 10px!important;font-size:0!important}::ng-deep .custom-input [data-test=calendar-clear-button]:before{content:\"\\2715\";font-size:14px;color:#870808}::ng-deep .custom-input [data-test=calendar-clear-button]:hover{background-color:#ddd}::ng-deep .calendar-input-wrapper{position:relative}::ng-deep .calendar-input-wrapper .custom-input{position:relative;width:100%;display:flex;align-items:center}::ng-deep .calendar-input-wrapper [data-test=calendar-clear-button]{right:8px;bottom:14px;margin:auto;display:flex;align-items:center;justify-content:center;width:16px;height:16px;font-size:0!important;background-color:#a0adba!important;border-radius:50%;padding:2px;cursor:pointer}::ng-deep .calendar-input-wrapper [data-test=calendar-clear-button]:before{content:\"\\2715\";font-size:10px;color:#f0f0f0}::ng-deep .calendar-input-wrapper input{padding-right:40px!important;height:40px;line-height:40px;box-sizing:border-box}::ng-deep .custom-input label{color:gray!important;opacity:1}::ng-deep .table-top-toolbar{background-color:var(--colors-grey100)!important;color:var(--colors-grey900)!important}::ng-deep .custom-select-input{display:flex!important;gap:4px!important;align-items:center!important;border:1px solid #9fadba!important;border-radius:4px!important;margin:0;padding:0 0 0 8px;background-color:#fff!important}::ng-deep .custom-select-input div{margin:0!important}::ng-deep .custom-select-input [data-test=dhis2-uiwidgets-singleselectfield-label]{font-size:12px;color:gray!important}::ng-deep .custom-select-input [data-test=dhis2-uicore-select-input]{border:none!important;font-size:12px!important;display:flex;align-items:center;padding-left:4px;height:40px;outline:none!important;box-shadow:none!important}::ng-deep .custom-select-input [data-test=dhis2-uicore-select-input]:focus-within{box-shadow:none!important}::ng-deep .custom-select-input input:focus{border:none!important;box-shadow:none!important}\n"] }); }
5267
5401
  }
5268
5402
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: LineListTableComponent, decorators: [{
5269
5403
  type: Component,
5270
- args: [{ selector: 'ng-dhis2-ui-line-list', template: '<ng-content></ng-content>', standalone: false, styles: ["@charset \"UTF-8\";::ng-deep .custom-input{display:flex!important;gap:4px!important;align-items:center!important;border:1px solid #9fadba!important;border-radius:4px!important;margin:0;padding:0 0 0 8px;background-color:#fff!important}::ng-deep .custom-input div{margin:0!important}::ng-deep .custom-input label{font-size:12px;padding-right:0}::ng-deep .custom-input input{border:none!important;font-size:12px!important;display:flex;align-items:center;padding-left:4px;height:40px;outline:none!important;box-shadow:none!important}::ng-deep .custom-input input:focus{border:none!important;box-shadow:none!important}::ng-deep .custom-input [data-test=calendar-clear-button]{background-color:#f2f2f2!important;color:#870808!important;border-radius:6px!important;padding:4px 10px!important;font-size:0!important}::ng-deep .custom-input [data-test=calendar-clear-button]:before{content:\"\\2715\";font-size:14px;color:#870808}::ng-deep .custom-input [data-test=calendar-clear-button]:hover{background-color:#ddd}::ng-deep .calendar-input-wrapper{position:relative}::ng-deep .calendar-input-wrapper .custom-input{position:relative;width:100%;display:flex;align-items:center}::ng-deep .calendar-input-wrapper [data-test=calendar-clear-button]{right:8px;bottom:14px;margin:auto;display:flex;align-items:center;justify-content:center;width:16px;height:16px;font-size:0!important;background-color:#a0adba!important;border-radius:50%;padding:2px;cursor:pointer}::ng-deep .calendar-input-wrapper [data-test=calendar-clear-button]:before{content:\"\\2715\";font-size:10px;color:#f0f0f0}::ng-deep .calendar-input-wrapper input{padding-right:40px!important;height:40px;line-height:40px;box-sizing:border-box}::ng-deep .custom-input label{color:gray!important;opacity:1}::ng-deep .table-top-toolbar{background-color:var(--colors-grey100)!important;color:var(--colors-grey900)!important}::ng-deep .custom-select-input{display:flex!important;gap:4px!important;align-items:center!important;border:1px solid #9fadba!important;border-radius:4px!important;margin:0;padding:0 0 0 8px;background-color:#fff!important}::ng-deep .custom-select-input div{margin:0!important}::ng-deep .custom-select-input [data-test=dhis2-uiwidgets-singleselectfield-label]{font-size:12px;color:gray!important}::ng-deep .custom-select-input [data-test=dhis2-uicore-select-input]{border:none!important;font-size:12px!important;display:flex;align-items:center;padding-left:4px;height:40px;outline:none!important;box-shadow:none!important}::ng-deep .custom-select-input [data-test=dhis2-uicore-select-input]:focus-within{box-shadow:none!important}::ng-deep .custom-select-input input:focus{border:none!important;box-shadow:none!important}\n"] }]
5404
+ args: [{ selector: 'ng-dhis2-ui-line-list', template: '<ng-content></ng-content>', standalone: false, styles: ["@charset \"UTF-8\";::ng-deep .line-list-root{width:100%;max-width:100%;min-width:0}::ng-deep .line-list-download-toolbar{position:sticky;left:0;z-index:2;display:flex;justify-content:flex-end;width:100%;max-width:100%;box-sizing:border-box;background:#fff}::ng-deep .line-list-table-scroll{width:100%;max-width:100%;overflow-x:auto}::ng-deep .custom-input{display:flex!important;gap:4px!important;align-items:center!important;border:1px solid #9fadba!important;border-radius:4px!important;margin:0;padding:0 0 0 8px;background-color:#fff!important}::ng-deep .custom-input div{margin:0!important}::ng-deep .custom-input label{font-size:12px;padding-right:0}::ng-deep .custom-input input{border:none!important;font-size:12px!important;display:flex;align-items:center;padding-left:4px;height:40px;outline:none!important;box-shadow:none!important}::ng-deep .custom-input input:focus{border:none!important;box-shadow:none!important}::ng-deep .custom-input [data-test=calendar-clear-button]{background-color:#f2f2f2!important;color:#870808!important;border-radius:6px!important;padding:4px 10px!important;font-size:0!important}::ng-deep .custom-input [data-test=calendar-clear-button]:before{content:\"\\2715\";font-size:14px;color:#870808}::ng-deep .custom-input [data-test=calendar-clear-button]:hover{background-color:#ddd}::ng-deep .calendar-input-wrapper{position:relative}::ng-deep .calendar-input-wrapper .custom-input{position:relative;width:100%;display:flex;align-items:center}::ng-deep .calendar-input-wrapper [data-test=calendar-clear-button]{right:8px;bottom:14px;margin:auto;display:flex;align-items:center;justify-content:center;width:16px;height:16px;font-size:0!important;background-color:#a0adba!important;border-radius:50%;padding:2px;cursor:pointer}::ng-deep .calendar-input-wrapper [data-test=calendar-clear-button]:before{content:\"\\2715\";font-size:10px;color:#f0f0f0}::ng-deep .calendar-input-wrapper input{padding-right:40px!important;height:40px;line-height:40px;box-sizing:border-box}::ng-deep .custom-input label{color:gray!important;opacity:1}::ng-deep .table-top-toolbar{background-color:var(--colors-grey100)!important;color:var(--colors-grey900)!important}::ng-deep .custom-select-input{display:flex!important;gap:4px!important;align-items:center!important;border:1px solid #9fadba!important;border-radius:4px!important;margin:0;padding:0 0 0 8px;background-color:#fff!important}::ng-deep .custom-select-input div{margin:0!important}::ng-deep .custom-select-input [data-test=dhis2-uiwidgets-singleselectfield-label]{font-size:12px;color:gray!important}::ng-deep .custom-select-input [data-test=dhis2-uicore-select-input]{border:none!important;font-size:12px!important;display:flex;align-items:center;padding-left:4px;height:40px;outline:none!important;box-shadow:none!important}::ng-deep .custom-select-input [data-test=dhis2-uicore-select-input]:focus-within{box-shadow:none!important}::ng-deep .custom-select-input input:focus{border:none!important;box-shadow:none!important}\n"] }]
5271
5405
  }], propDecorators: { triggerToken: [{
5272
5406
  type: Input
5273
5407
  }], programId: [{
@@ -9067,6 +9201,42 @@ class BaseFormFieldComponent extends ReactWrapperModule {
9067
9201
  // ----------------------------
9068
9202
  useEffect(() => {
9069
9203
  const cascade = this.#resolveCascadeForCurrentField();
9204
+ const dependentField = field.dependentField;
9205
+ if (dependentField) {
9206
+ const parentCtrl = this.#getControl(fg, dependentField.key || dependentField.id);
9207
+ if (!parentCtrl) {
9208
+ setFilteredOptions([]);
9209
+ setDisabled(baseDisabled);
9210
+ return undefined;
9211
+ }
9212
+ const parentOptions = dependentField.options?.length > 0
9213
+ ? dependentField.options
9214
+ : dependentField.optionSet?.options ?? [];
9215
+ const applyDependentOptions = (parentValue) => {
9216
+ const parentOption = parentOptions.find((option) => {
9217
+ const optionValue = option?.value ?? option?.code ?? option?.key;
9218
+ return optionValue === parentValue;
9219
+ });
9220
+ const nextOptions = parentOption?.options ?? [];
9221
+ const childCtrl = this.#getCurrentFieldControl(fg);
9222
+ const currentChildValue = String(childCtrl?.value ?? '').trim();
9223
+ if (currentChildValue) {
9224
+ const stillValid = nextOptions.some((option) => {
9225
+ const optionValue = String(option?.value ?? option?.code ?? option?.key ?? '');
9226
+ return optionValue === currentChildValue;
9227
+ });
9228
+ if (!stillValid) {
9229
+ childCtrl?.setValue(null, { emitEvent: true });
9230
+ setValue('');
9231
+ }
9232
+ }
9233
+ setFilteredOptions(nextOptions);
9234
+ setDisabled(baseDisabled || !parentValue);
9235
+ };
9236
+ applyDependentOptions(parentCtrl.value);
9237
+ const sub = parentCtrl.valueChanges.subscribe((value) => applyDependentOptions(value));
9238
+ return () => sub.unsubscribe();
9239
+ }
9070
9240
  if (!cascade || cascade.kind !== 'OPTIONS_PATH') {
9071
9241
  setFilteredOptions(field.options ?? []);
9072
9242
  setDisabled(baseDisabled);
@@ -9905,7 +10075,8 @@ class FormComponent {
9905
10075
  if (fieldToRemove) {
9906
10076
  const form = this.form();
9907
10077
  const currentValue = form.get(fieldToRemove.key)?.value;
9908
- if (currentValue != null) {
10078
+ const shouldPreserveValue = fieldToRemove.disabled || fieldToRemove.generated;
10079
+ if (currentValue != null && !shouldPreserveValue) {
9909
10080
  form.get(fieldToRemove.key)?.setValue(null);
9910
10081
  this.onFieldUpdate(form, fieldToRemove);
9911
10082
  }
@@ -10013,6 +10184,7 @@ class FormField {
10013
10184
  this.trackedEntityType = options.trackedEntityType;
10014
10185
  this.hasOptions = options.hasOptions;
10015
10186
  this.dependentField = options.dependentField;
10187
+ this.fieldOptionsDependsOn = options.fieldOptionsDependsOn;
10016
10188
  this.displayInList = options.displayInList;
10017
10189
  this.displayInReports = options.displayInReports;
10018
10190
  this.searchable = options.searchable;
@@ -10098,10 +10270,13 @@ var FieldType;
10098
10270
 
10099
10271
  class FieldDropdown {
10100
10272
  constructor(params) {
10101
- this.key = params.key;
10102
- this.value = params.value;
10103
- this.label = params.label;
10104
- this.order = params.order;
10273
+ const rawParams = params;
10274
+ this.key = params.key ?? rawParams.id ?? rawParams.code ?? rawParams.name;
10275
+ this.value = params.value ?? rawParams.code ?? rawParams.name;
10276
+ this.label =
10277
+ params.label ?? rawParams.name ?? rawParams.displayName ?? rawParams.code;
10278
+ this.order = params.order ?? rawParams.sortOrder;
10279
+ this.options = params.options?.map((option) => new FieldDropdown(option));
10105
10280
  }
10106
10281
  toJson() {
10107
10282
  return {
@@ -10109,6 +10284,7 @@ class FieldDropdown {
10109
10284
  value: this.value,
10110
10285
  label: this.label,
10111
10286
  order: this.order,
10287
+ options: this.options?.map((option) => option.toJson()),
10112
10288
  };
10113
10289
  }
10114
10290
  static getDropdownOptions(field, locale, extension) {
@@ -10140,10 +10316,11 @@ class FieldDropdown {
10140
10316
  }
10141
10317
  return sortBy((field?.optionSet?.options || []).map((option) => {
10142
10318
  return new FieldDropdown({
10143
- key: option.id,
10144
- value: option.code,
10145
- label: option.name,
10319
+ key: option.id ?? option.code ?? option.name,
10320
+ value: option.code ?? option.value ?? option.name,
10321
+ label: option.name ?? option.label ?? option.code,
10146
10322
  order: option.sortOrder,
10323
+ options: option.options,
10147
10324
  });
10148
10325
  }), 'order');
10149
10326
  }
@@ -12345,9 +12522,10 @@ class ProgramStageEntryFormSectionUtil {
12345
12522
  // }
12346
12523
  class ProgramEntryFormSectionUtil {
12347
12524
  #sortedCanonicalFieldsCache = null;
12348
- constructor(program, config) {
12525
+ constructor(program, config, customFieldConfigurations = {}) {
12349
12526
  this.program = program;
12350
12527
  this.config = config;
12528
+ this.customFieldConfigurations = customFieldConfigurations;
12351
12529
  this.fieldUtil = new ProgramEntryFormFieldUtil(program, config);
12352
12530
  }
12353
12531
  // get programEntryFormSections(): IProgramEntryFormSection[] {
@@ -12570,7 +12748,12 @@ class ProgramEntryFormSectionUtil {
12570
12748
  sortOrder: programTrackedEntityAttribute.sortOrder,
12571
12749
  optionSet: tea.optionSet,
12572
12750
  };
12573
- const options = FieldDropdown.getDropdownOptions(iTrackedEntityFormFieldBase);
12751
+ const customConfiguration = this.customFieldConfigurations?.[iTrackedEntityFormFieldBase.id];
12752
+ const fieldWithCustomConfiguration = {
12753
+ ...iTrackedEntityFormFieldBase,
12754
+ ...(customConfiguration || {}),
12755
+ };
12756
+ const options = FieldDropdown.getDropdownOptions(fieldWithCustomConfiguration);
12574
12757
  const hasOptions = (options?.length ?? 0) > 0;
12575
12758
  const fieldExtension = this.config.formFieldExtensions?.find((formFieldExtension) => formFieldExtension?.id === iTrackedEntityFormFieldBase.id);
12576
12759
  const autoAssignedValue = this.#getAutoAssignedValue(iTrackedEntityFormFieldBase);
@@ -12579,22 +12762,24 @@ class ProgramEntryFormSectionUtil {
12579
12762
  return null;
12580
12763
  }
12581
12764
  return new FormField({
12582
- id: iTrackedEntityFormFieldBase.id,
12583
- code: iTrackedEntityFormFieldBase.code,
12584
- label: iTrackedEntityFormFieldBase.formName ||
12585
- iTrackedEntityFormFieldBase.name,
12586
- key: iTrackedEntityFormFieldBase.code
12587
- ? camelCase(iTrackedEntityFormFieldBase.code)
12588
- : iTrackedEntityFormFieldBase.id,
12589
- required: iTrackedEntityFormFieldBase.mandatory,
12590
- type: FieldUtil.getFieldType(iTrackedEntityFormFieldBase.valueType),
12765
+ id: fieldWithCustomConfiguration.id,
12766
+ code: fieldWithCustomConfiguration.code,
12767
+ label: fieldWithCustomConfiguration.formName ||
12768
+ fieldWithCustomConfiguration.name,
12769
+ key: fieldWithCustomConfiguration.code
12770
+ ? camelCase(fieldWithCustomConfiguration.code)
12771
+ : fieldWithCustomConfiguration.id,
12772
+ required: fieldWithCustomConfiguration.mandatory,
12773
+ type: FieldUtil.getFieldType(fieldWithCustomConfiguration.valueType),
12591
12774
  options,
12592
12775
  hasOptions,
12593
- disabled: this.#getDisabledStatus(iTrackedEntityFormFieldBase) || !!tea.generated,
12594
- order: iTrackedEntityFormFieldBase.sortOrder,
12595
- controlType: FieldUtil.getFieldControlType(iTrackedEntityFormFieldBase.valueType, hasOptions),
12776
+ disabled: this.#getDisabledStatus(iTrackedEntityFormFieldBase) ||
12777
+ !!tea.generated,
12778
+ order: fieldWithCustomConfiguration.sortOrder,
12779
+ controlType: FieldUtil.getFieldControlType(fieldWithCustomConfiguration.valueType, hasOptions),
12596
12780
  allowFutureDate: programTrackedEntityAttribute.allowFutureDate === true,
12597
12781
  extension: fieldExtension,
12782
+ fieldOptionsDependsOn: fieldWithCustomConfiguration.fieldOptionsDependsOn,
12598
12783
  generated: !!tea.generated,
12599
12784
  unique: !!tea.unique,
12600
12785
  pattern: tea.pattern,
@@ -12618,6 +12803,18 @@ class ProgramEntryFormSectionUtil {
12618
12803
  continue;
12619
12804
  fieldsByAttributeId.set(canonicalField.id, canonicalField);
12620
12805
  }
12806
+ for (const [fieldId, field] of fieldsByAttributeId.entries()) {
12807
+ if (!field.fieldOptionsDependsOn)
12808
+ continue;
12809
+ const dependentField = fieldsByAttributeId.get(field.fieldOptionsDependsOn);
12810
+ if (!dependentField)
12811
+ continue;
12812
+ fieldsByAttributeId.set(fieldId, new FormField({
12813
+ ...field,
12814
+ dependentField,
12815
+ controlType: 'dropdown',
12816
+ }));
12817
+ }
12621
12818
  return fieldsByAttributeId;
12622
12819
  }
12623
12820
  }
@@ -12628,6 +12825,7 @@ class ProgramEntryFormSectionUtil {
12628
12825
  class ProgramEntryFormMetaData {
12629
12826
  constructor() {
12630
12827
  this.d2 = window?.d2Web;
12828
+ this.customFieldConfigurations = {};
12631
12829
  }
12632
12830
  setConfig(config) {
12633
12831
  this.config = config;
@@ -12826,7 +13024,7 @@ class ProgramEntryFormMetaData {
12826
13024
  if (displayType !== 'FLAT') {
12827
13025
  return [];
12828
13026
  }
12829
- const baseSections = new ProgramEntryFormSectionUtil(this.program, this.config).programEntryFormSections;
13027
+ const baseSections = new ProgramEntryFormSectionUtil(this.program, this.config, this.customFieldConfigurations).programEntryFormSections;
12830
13028
  const autoAssigned = this.config?.autoAssignedValues;
12831
13029
  if (!autoAssigned?.length) {
12832
13030
  return baseSections;
@@ -12908,13 +13106,37 @@ class ProgramEntryFormMetaData {
12908
13106
  sections,
12909
13107
  programStageSections,
12910
13108
  rules: this.rules,
13109
+ customFieldConfigurations: this.customFieldConfigurations,
12911
13110
  };
12912
13111
  }
13112
+ async #getCustomFieldConfigurations() {
13113
+ try {
13114
+ const response = await this.d2.httpInstance.get('dataStore/field-extensions?fields=id,fieldOptionsDependsOn,optionSet&paging=false');
13115
+ const entries = response?.data?.['entries'];
13116
+ if (!Array.isArray(entries)) {
13117
+ return {};
13118
+ }
13119
+ return entries.reduce((fieldConfigurations, fieldConfiguration) => {
13120
+ if (!fieldConfiguration?.id) {
13121
+ return fieldConfigurations;
13122
+ }
13123
+ return {
13124
+ ...fieldConfigurations,
13125
+ [fieldConfiguration.id]: fieldConfiguration,
13126
+ };
13127
+ }, {});
13128
+ }
13129
+ catch {
13130
+ return {};
13131
+ }
13132
+ }
12913
13133
  async get() {
12914
13134
  try {
12915
13135
  const program = await this.#getProgramMetaData();
12916
13136
  if (program) {
12917
13137
  this.program = program;
13138
+ this.customFieldConfigurations =
13139
+ await this.#getCustomFieldConfigurations();
12918
13140
  if (this.config?.formFieldExtensions &&
12919
13141
  this.config.formFieldExtensions.length > 0 &&
12920
13142
  this.config.formFieldExtensions[0]?.optionsSourceCode) {