@iapps/ng-dhis2-ui 1.2.164 → 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.
@@ -4310,6 +4310,7 @@ const getTrackedEntityTableData = (response, programId, pager, metaData, searche
4310
4310
  ? teisRaw
4311
4311
  : [teisRaw];
4312
4312
  const orgUnitLabel = metaData.orgUnitLabel;
4313
+ const incidentDateLabel = metaData.incidentDateLabel;
4313
4314
  const orgUnitMap = response.data
4314
4315
  .orgUnitsMap;
4315
4316
  const attributeColumns = metaData.displayInListTrackedEntityAttributes
@@ -4369,6 +4370,14 @@ const getTrackedEntityTableData = (response, programId, pager, metaData, searche
4369
4370
  label: orgUnitLabel || 'Registering unit',
4370
4371
  key: 'orgUnit',
4371
4372
  },
4373
+ ...(metaData.displayIncidentDate
4374
+ ? [
4375
+ {
4376
+ label: incidentDateLabel || 'Incident date',
4377
+ key: 'incidentDate',
4378
+ },
4379
+ ]
4380
+ : []),
4372
4381
  ...attributeColumns,
4373
4382
  ...dataElementColumns,
4374
4383
  ];
@@ -4468,6 +4477,11 @@ const getTrackedEntityTableData = (response, programId, pager, metaData, searche
4468
4477
  const orgUnitName = orgUnitMap?.get(orgUnitId) || '-';
4469
4478
  row['orgUnitId'] = { value: orgUnitId || '-' };
4470
4479
  row['orgUnit'] = { value: orgUnitName };
4480
+ row['incidentDate'] = {
4481
+ value: enrollment.incidentDate
4482
+ ? formatDate(enrollment.incidentDate)
4483
+ : '-',
4484
+ };
4471
4485
  const teiObject = typeof tei.toObject === 'function' ? tei.toObject() : null;
4472
4486
  row['responseData'] = {
4473
4487
  value: teiObject || null,
@@ -4656,6 +4670,141 @@ const getEvents = (response, programStageId, pager, metaData, columnNameSource =
4656
4670
  return { columns: entityColumns, data: dataElementsData };
4657
4671
  };
4658
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
+
4659
4808
  class LineListTableComponent extends ReactWrapperModule {
4660
4809
  constructor() {
4661
4810
  super(...arguments);
@@ -4704,6 +4853,7 @@ class LineListTableComponent extends ReactWrapperModule {
4704
4853
  const [endDateState, setEndDateState] = useState(this.endDate);
4705
4854
  const [tempEndDateState, setTempEndDateState] = useState(this.endDate);
4706
4855
  const [loading, setLoading] = useState(false);
4856
+ const [downloadLoading, setDownloadLoading] = useState(false);
4707
4857
  const [filteredColumns, setFilteredColumns] = useState();
4708
4858
  const [inputValues, setInputValues] = useState({});
4709
4859
  const [orgUnitLabel, setOrgUnitLabel] = useState('');
@@ -4927,47 +5077,41 @@ class LineListTableComponent extends ReactWrapperModule {
4927
5077
  triggerRefetch,
4928
5078
  triggerTokenState,
4929
5079
  ]);
4930
- const handleExcelDownload = () => {
4931
- const filteredColumns = columns.filter((col) => col.label !== 'Actions');
4932
- const header = filteredColumns.map((col) => col.label);
4933
- const rows = data.map((row) => filteredColumns.map((col) => {
4934
- const cell = row[col.key];
4935
- if (cell === null || cell === undefined)
4936
- return '';
4937
- return typeof cell === 'object' && 'value' in cell
4938
- ? cell.value
4939
- : cell;
4940
- }));
4941
- const worksheetData = [header, ...rows];
4942
- const worksheet = XLSX.utils.aoa_to_sheet(worksheetData);
4943
- const workbook = XLSX.utils.book_new();
4944
- XLSX.utils.book_append_sheet(workbook, worksheet, 'LineListData');
4945
- XLSX.writeFile(workbook, 'records.xlsx');
4946
- };
4947
- const handleCsvDownload = () => {
4948
- const safeValue = (value) => {
4949
- if (value === null || value === undefined)
4950
- return '';
4951
- if (typeof value === 'object') {
4952
- if ('value' in value) {
4953
- return value.value;
4954
- }
4955
- 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;
4956
5105
  }
4957
- return value;
4958
- };
4959
- const filteredColumns = columns.filter((col) => col.label !== 'Actions');
4960
- const header = filteredColumns.map((col) => `"${col.label}"`).join(',');
4961
- const csvData = data.map((row) => filteredColumns.map((col) => `"${safeValue(row[col.key])}"`).join(','));
4962
- const csvContent = [header, ...csvData].join('\n');
4963
- const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
4964
- const url = URL.createObjectURL(blob);
4965
- const link = document.createElement('a');
4966
- link.href = url;
4967
- link.setAttribute('download', 'records.csv');
4968
- document.body.appendChild(link);
4969
- link.click();
4970
- 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
+ }
4971
5115
  };
4972
5116
  const handleInputChange = (key, value, type) => {
4973
5117
  setInputValues((prevValues) => ({
@@ -5103,7 +5247,7 @@ class LineListTableComponent extends ReactWrapperModule {
5103
5247
  // Returns black for light backgrounds, white for dark backgrounds
5104
5248
  return yiq >= 128 ? colors$1.grey700 : '#FFFFFF';
5105
5249
  }
5106
- return React__default.createElement('div', null, loading
5250
+ return React__default.createElement('div', { className: 'line-list-root' }, loading
5107
5251
  ? React__default.createElement('div', {
5108
5252
  style: {
5109
5253
  display: 'flex',
@@ -5118,22 +5262,26 @@ class LineListTableComponent extends ReactWrapperModule {
5118
5262
  }, 'Loading'))
5119
5263
  : React__default.createElement('div', null, this.showDownloadButton &&
5120
5264
  React__default.createElement('div', {
5265
+ className: 'line-list-download-toolbar',
5121
5266
  style: {
5122
5267
  paddingBottom: '1rem',
5123
- display: 'flex',
5124
- justifyContent: 'flex-end',
5125
5268
  },
5126
5269
  }, React__default.createElement(DropdownButton, {
5127
5270
  name: 'Download',
5271
+ disabled: downloadLoading,
5128
5272
  component: React__default.createElement('span', null, ' ', React__default.createElement(MenuItem$1, {
5129
- label: 'Download Csv',
5130
- onClick: handleCsvDownload,
5273
+ label: 'CSV',
5274
+ disabled: downloadLoading,
5275
+ onClick: () => handleDownload('CSV'),
5131
5276
  }), React__default.createElement(MenuItem$1, {
5132
- label: 'Download Excel',
5133
- onClick: handleExcelDownload,
5277
+ label: 'Excel',
5278
+ disabled: downloadLoading,
5279
+ onClick: () => handleDownload('XLSX'),
5134
5280
  })),
5135
5281
  value: 'Download',
5136
- }, 'Download')), orgUnitModalVisible &&
5282
+ }, downloadLoading
5283
+ ? React__default.createElement(CircularLoader$1, { small: true })
5284
+ : 'Download')), orgUnitModalVisible &&
5137
5285
  React__default.createElement(OrgUnitSelector, {
5138
5286
  hide: hide,
5139
5287
  setHide: setHide,
@@ -5186,7 +5334,7 @@ class LineListTableComponent extends ReactWrapperModule {
5186
5334
  prevSelectedOrgUnit: prevSelectedOrgUnit,
5187
5335
  setPrevSelectedOrgUnit: setPrevSelectedOrgUnit,
5188
5336
  // filteredFilters: filteredFilters
5189
- }), React__default.createElement(LineListTable, {
5337
+ }), React__default.createElement('div', { className: 'line-list-table-scroll' }, React__default.createElement(LineListTable, {
5190
5338
  columns: columns,
5191
5339
  data: data,
5192
5340
  pager: pager,
@@ -5200,7 +5348,7 @@ class LineListTableComponent extends ReactWrapperModule {
5200
5348
  rowsSelected: this.rowsSelected,
5201
5349
  showActionButtons: showActionButtons,
5202
5350
  error: error,
5203
- })));
5351
+ }))));
5204
5352
  };
5205
5353
  }
5206
5354
  ngOnChanges(changes) {
@@ -5249,11 +5397,11 @@ class LineListTableComponent extends ReactWrapperModule {
5249
5397
  this.render();
5250
5398
  }
5251
5399
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: LineListTableComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
5252
- 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"] }); }
5253
5401
  }
5254
5402
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: LineListTableComponent, decorators: [{
5255
5403
  type: Component,
5256
- 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"] }]
5257
5405
  }], propDecorators: { triggerToken: [{
5258
5406
  type: Input
5259
5407
  }], programId: [{
@@ -9053,6 +9201,42 @@ class BaseFormFieldComponent extends ReactWrapperModule {
9053
9201
  // ----------------------------
9054
9202
  useEffect(() => {
9055
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
+ }
9056
9240
  if (!cascade || cascade.kind !== 'OPTIONS_PATH') {
9057
9241
  setFilteredOptions(field.options ?? []);
9058
9242
  setDisabled(baseDisabled);
@@ -9891,7 +10075,8 @@ class FormComponent {
9891
10075
  if (fieldToRemove) {
9892
10076
  const form = this.form();
9893
10077
  const currentValue = form.get(fieldToRemove.key)?.value;
9894
- if (currentValue != null) {
10078
+ const shouldPreserveValue = fieldToRemove.disabled || fieldToRemove.generated;
10079
+ if (currentValue != null && !shouldPreserveValue) {
9895
10080
  form.get(fieldToRemove.key)?.setValue(null);
9896
10081
  this.onFieldUpdate(form, fieldToRemove);
9897
10082
  }
@@ -9999,6 +10184,7 @@ class FormField {
9999
10184
  this.trackedEntityType = options.trackedEntityType;
10000
10185
  this.hasOptions = options.hasOptions;
10001
10186
  this.dependentField = options.dependentField;
10187
+ this.fieldOptionsDependsOn = options.fieldOptionsDependsOn;
10002
10188
  this.displayInList = options.displayInList;
10003
10189
  this.displayInReports = options.displayInReports;
10004
10190
  this.searchable = options.searchable;
@@ -10084,10 +10270,13 @@ var FieldType;
10084
10270
 
10085
10271
  class FieldDropdown {
10086
10272
  constructor(params) {
10087
- this.key = params.key;
10088
- this.value = params.value;
10089
- this.label = params.label;
10090
- 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));
10091
10280
  }
10092
10281
  toJson() {
10093
10282
  return {
@@ -10095,6 +10284,7 @@ class FieldDropdown {
10095
10284
  value: this.value,
10096
10285
  label: this.label,
10097
10286
  order: this.order,
10287
+ options: this.options?.map((option) => option.toJson()),
10098
10288
  };
10099
10289
  }
10100
10290
  static getDropdownOptions(field, locale, extension) {
@@ -10126,10 +10316,11 @@ class FieldDropdown {
10126
10316
  }
10127
10317
  return sortBy((field?.optionSet?.options || []).map((option) => {
10128
10318
  return new FieldDropdown({
10129
- key: option.id,
10130
- value: option.code,
10131
- 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,
10132
10322
  order: option.sortOrder,
10323
+ options: option.options,
10133
10324
  });
10134
10325
  }), 'order');
10135
10326
  }
@@ -12331,9 +12522,10 @@ class ProgramStageEntryFormSectionUtil {
12331
12522
  // }
12332
12523
  class ProgramEntryFormSectionUtil {
12333
12524
  #sortedCanonicalFieldsCache = null;
12334
- constructor(program, config) {
12525
+ constructor(program, config, customFieldConfigurations = {}) {
12335
12526
  this.program = program;
12336
12527
  this.config = config;
12528
+ this.customFieldConfigurations = customFieldConfigurations;
12337
12529
  this.fieldUtil = new ProgramEntryFormFieldUtil(program, config);
12338
12530
  }
12339
12531
  // get programEntryFormSections(): IProgramEntryFormSection[] {
@@ -12556,7 +12748,12 @@ class ProgramEntryFormSectionUtil {
12556
12748
  sortOrder: programTrackedEntityAttribute.sortOrder,
12557
12749
  optionSet: tea.optionSet,
12558
12750
  };
12559
- 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);
12560
12757
  const hasOptions = (options?.length ?? 0) > 0;
12561
12758
  const fieldExtension = this.config.formFieldExtensions?.find((formFieldExtension) => formFieldExtension?.id === iTrackedEntityFormFieldBase.id);
12562
12759
  const autoAssignedValue = this.#getAutoAssignedValue(iTrackedEntityFormFieldBase);
@@ -12565,22 +12762,24 @@ class ProgramEntryFormSectionUtil {
12565
12762
  return null;
12566
12763
  }
12567
12764
  return new FormField({
12568
- id: iTrackedEntityFormFieldBase.id,
12569
- code: iTrackedEntityFormFieldBase.code,
12570
- label: iTrackedEntityFormFieldBase.formName ||
12571
- iTrackedEntityFormFieldBase.name,
12572
- key: iTrackedEntityFormFieldBase.code
12573
- ? camelCase(iTrackedEntityFormFieldBase.code)
12574
- : iTrackedEntityFormFieldBase.id,
12575
- required: iTrackedEntityFormFieldBase.mandatory,
12576
- 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),
12577
12774
  options,
12578
12775
  hasOptions,
12579
- disabled: this.#getDisabledStatus(iTrackedEntityFormFieldBase) || !!tea.generated,
12580
- order: iTrackedEntityFormFieldBase.sortOrder,
12581
- 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),
12582
12780
  allowFutureDate: programTrackedEntityAttribute.allowFutureDate === true,
12583
12781
  extension: fieldExtension,
12782
+ fieldOptionsDependsOn: fieldWithCustomConfiguration.fieldOptionsDependsOn,
12584
12783
  generated: !!tea.generated,
12585
12784
  unique: !!tea.unique,
12586
12785
  pattern: tea.pattern,
@@ -12604,6 +12803,18 @@ class ProgramEntryFormSectionUtil {
12604
12803
  continue;
12605
12804
  fieldsByAttributeId.set(canonicalField.id, canonicalField);
12606
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
+ }
12607
12818
  return fieldsByAttributeId;
12608
12819
  }
12609
12820
  }
@@ -12614,6 +12825,7 @@ class ProgramEntryFormSectionUtil {
12614
12825
  class ProgramEntryFormMetaData {
12615
12826
  constructor() {
12616
12827
  this.d2 = window?.d2Web;
12828
+ this.customFieldConfigurations = {};
12617
12829
  }
12618
12830
  setConfig(config) {
12619
12831
  this.config = config;
@@ -12812,7 +13024,7 @@ class ProgramEntryFormMetaData {
12812
13024
  if (displayType !== 'FLAT') {
12813
13025
  return [];
12814
13026
  }
12815
- const baseSections = new ProgramEntryFormSectionUtil(this.program, this.config).programEntryFormSections;
13027
+ const baseSections = new ProgramEntryFormSectionUtil(this.program, this.config, this.customFieldConfigurations).programEntryFormSections;
12816
13028
  const autoAssigned = this.config?.autoAssignedValues;
12817
13029
  if (!autoAssigned?.length) {
12818
13030
  return baseSections;
@@ -12894,13 +13106,37 @@ class ProgramEntryFormMetaData {
12894
13106
  sections,
12895
13107
  programStageSections,
12896
13108
  rules: this.rules,
13109
+ customFieldConfigurations: this.customFieldConfigurations,
12897
13110
  };
12898
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
+ }
12899
13133
  async get() {
12900
13134
  try {
12901
13135
  const program = await this.#getProgramMetaData();
12902
13136
  if (program) {
12903
13137
  this.program = program;
13138
+ this.customFieldConfigurations =
13139
+ await this.#getCustomFieldConfigurations();
12904
13140
  if (this.config?.formFieldExtensions &&
12905
13141
  this.config.formFieldExtensions.length > 0 &&
12906
13142
  this.config.formFieldExtensions[0]?.optionsSourceCode) {