@iapps/ng-dhis2-ui 1.1.68 → 1.1.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,7 +8,7 @@ import { NgxDhis2HttpClientService } from '@iapps/ngx-dhis2-http-client';
8
8
  import * as React from 'react';
9
9
  import React__default, { useState, useCallback, useRef, useEffect, createContext, useContext, useMemo } from 'react';
10
10
  import * as ReactDOM from 'react-dom/client';
11
- import { catchError, of, map, zip, firstValueFrom, filter, Subject, takeUntil } from 'rxjs';
11
+ import { catchError, of, map, zip, firstValueFrom, take, filter, Subject, takeUntil } from 'rxjs';
12
12
  import i18n from '@dhis2/d2-i18n';
13
13
  import { colors, theme, spacers } from '@dhis2/ui-constants';
14
14
  import PropTypes from 'prop-types';
@@ -4096,10 +4096,132 @@ class LineListTableComponent extends ReactWrapperModule {
4096
4096
  useEffect(() => {
4097
4097
  this.setReactStateUpdaters?.(updateRefs.current);
4098
4098
  }, []);
4099
+ // TODO: START: Improve the approach of handling observables
4100
+ // useEffect(() => {
4101
+ // setLoading(true);
4102
+ // this.lineListService
4103
+ // .getLineListData(
4104
+ // this.programId,
4105
+ // orgUnitState,
4106
+ // this.programStageId,
4107
+ // pager.page,
4108
+ // pager.pageSize,
4109
+ // attributeFiltersState,
4110
+ // startDateState,
4111
+ // endDateState,
4112
+ // this.ouMode,
4113
+ // this.filterRootOrgUnit,
4114
+ // this.useOuModeWithOlderDHIS2Instance
4115
+ // )
4116
+ // .subscribe((response: LineListResponse) => {
4117
+ // let entityColumns: ColumnDefinition[] = [];
4118
+ // let responsePager: Pager;
4119
+ // let entityData: TableRow[] = [];
4120
+ // let filteredDataColumns: ColumnDefinition[] = [];
4121
+ // if (this.programStageId) {
4122
+ // responsePager = (response.data as EventsResponse).pager;
4123
+ // const { columns, data } = getProgramStageData(
4124
+ // response,
4125
+ // this.programStageId,
4126
+ // pager,
4127
+ // this.isOptionSetNameVisible
4128
+ // );
4129
+ // entityColumns = columns;
4130
+ // entityData = data;
4131
+ // } else if (
4132
+ // 'trackedEntities' in response.data ||
4133
+ // 'instances' in response.data
4134
+ // ) {
4135
+ // const responseData: any = response.data as TrackedEntityResponse;
4136
+ // responsePager = (response.data as TrackedEntityResponse).pager || {
4137
+ // page: responseData.page,
4138
+ // pageSize: responseData.pageSize,
4139
+ // total: responseData.total,
4140
+ // pageCount: responseData.pageCount,
4141
+ // };
4142
+ // const { columns, data, filteredEntityColumns } =
4143
+ // getTrackedEntityData(
4144
+ // response,
4145
+ // this.programId,
4146
+ // pager,
4147
+ // this.isOptionSetNameVisible,
4148
+ // this.filters
4149
+ // );
4150
+ // entityColumns = columns;
4151
+ // entityData = data;
4152
+ // //TODO: Should be done on the parent
4153
+ // const checkValues = [
4154
+ // ...new Set(
4155
+ // (
4156
+ // response.data as TrackedEntityResponse
4157
+ // )?.trackedEntities?.[0]?.enrollments?.[0]?.events?.flatMap(
4158
+ // (event) =>
4159
+ // event.dataValues
4160
+ // .filter((dataValue) =>
4161
+ // /^[A-Za-z]{3}$/.test(dataValue.value)
4162
+ // )
4163
+ // .map((dataValue) => dataValue.value)
4164
+ // ) ??
4165
+ // (
4166
+ // response.data as TrackedEntityResponse
4167
+ // )?.instances?.[0]?.enrollments?.[0]?.events?.flatMap(
4168
+ // (event) =>
4169
+ // event.dataValues
4170
+ // .filter((dataValue) =>
4171
+ // /^[A-Za-z]{3}$/.test(dataValue.value)
4172
+ // )
4173
+ // .map((dataValue) => dataValue.value)
4174
+ // )
4175
+ // ),
4176
+ // ];
4177
+ // if (checkValues) {
4178
+ // // this.firstValue.emit(firstTei);
4179
+ // setCheckValue(checkValues);
4180
+ // setValuesMatch(!checkValues.includes(this.buttonFilter));
4181
+ // }
4182
+ // // setFilteredColumns(filteredEntityColumns);
4183
+ // // Ensure filter inputs do not disappear when no data is returned
4184
+ // // If filteredEntityColumns is empty, keep the previous columns instead of clearing them
4185
+ // setFilteredColumns((prev) =>
4186
+ // filteredEntityColumns.length > 0 ? filteredEntityColumns : prev
4187
+ // );
4188
+ // filteredDataColumns = filteredEntityColumns;
4189
+ // } else {
4190
+ // responsePager = (response.data as EventsResponse).pager;
4191
+ // const { columns, data } = getEventData(
4192
+ // response,
4193
+ // pager,
4194
+ // this.isOptionSetNameVisible
4195
+ // );
4196
+ // entityColumns = columns;
4197
+ // entityData = data;
4198
+ // }
4199
+ // const finalColumns: ColumnDefinition[] = addActionsColumn(
4200
+ // [{ label: '#', key: 'index' }, ...entityColumns],
4201
+ // this.actionOptions
4202
+ // );
4203
+ // setLoading(false);
4204
+ // setColumns(...[finalColumns]);
4205
+ // setData(...[entityData]);
4206
+ // setPager(...[responsePager]);
4207
+ // });
4208
+ // }, [
4209
+ // programIdState,
4210
+ // orgUnitState,
4211
+ // programStageIdState,
4212
+ // attributeFiltersState,
4213
+ // startDateState,
4214
+ // endDateState,
4215
+ // pager.page,
4216
+ // pager.pageSize,
4217
+ // isOptionSetNameVisibleState,
4218
+ // ]);
4219
+ // TODO: END: Improve the approach of handling observables
4099
4220
  useEffect(() => {
4100
4221
  setLoading(true);
4101
- this.lineListService
4222
+ const subscription = this.lineListService
4102
4223
  .getLineListData(this.programId, orgUnitState, this.programStageId, pager.page, pager.pageSize, attributeFiltersState, startDateState, endDateState, this.ouMode, this.filterRootOrgUnit, this.useOuModeWithOlderDHIS2Instance)
4224
+ .pipe(take(1)) // Automatically unsubscribe after the first emission
4103
4225
  .subscribe((response) => {
4104
4226
  let entityColumns = [];
4105
4227
  let responsePager;
@@ -4111,10 +4233,10 @@ class LineListTableComponent extends ReactWrapperModule {
4111
4233
  entityColumns = columns;
4112
4234
  entityData = data;
4113
4235
  }
4114
- else if ('trackedEntities' in response.data ||
4115
- 'instances' in response.data) {
4236
+ else if ("trackedEntities" in response.data ||
4237
+ "instances" in response.data) {
4116
4238
  const responseData = response.data;
4117
- responsePager = response.data.pager || {
4239
+ responsePager = responseData.pager || {
4118
4240
  page: responseData.page,
4119
4241
  pageSize: responseData.pageSize,
4120
4242
  total: responseData.total,
@@ -4123,23 +4245,18 @@ class LineListTableComponent extends ReactWrapperModule {
4123
4245
  const { columns, data, filteredEntityColumns } = getTrackedEntityData(response, this.programId, pager, this.isOptionSetNameVisible, this.filters);
4124
4246
  entityColumns = columns;
4125
4247
  entityData = data;
4126
- //TODO: Should be done on the parent
4127
4248
  const checkValues = [
4128
- ...new Set(response.data?.trackedEntities?.[0]?.enrollments?.[0]?.events?.flatMap((event) => event.dataValues
4249
+ ...new Set(responseData?.trackedEntities?.[0]?.enrollments?.[0]?.events?.flatMap((event) => event.dataValues
4129
4250
  .filter((dataValue) => /^[A-Za-z]{3}$/.test(dataValue.value))
4130
4251
  .map((dataValue) => dataValue.value)) ??
4131
- response.data?.instances?.[0]?.enrollments?.[0]?.events?.flatMap((event) => event.dataValues
4252
+ responseData?.instances?.[0]?.enrollments?.[0]?.events?.flatMap((event) => event.dataValues
4132
4253
  .filter((dataValue) => /^[A-Za-z]{3}$/.test(dataValue.value))
4133
4254
  .map((dataValue) => dataValue.value))),
4134
4255
  ];
4135
- if (checkValues) {
4136
- // this.firstValue.emit(firstTei);
4256
+ if (checkValues.length > 0) {
4137
4257
  setCheckValue(checkValues);
4138
4258
  setValuesMatch(!checkValues.includes(this.buttonFilter));
4139
4259
  }
4140
- // setFilteredColumns(filteredEntityColumns);
4141
- // Ensure filter inputs do not disappear when no data is returned
4142
- // If filteredEntityColumns is empty, keep the previous columns instead of clearing them
4143
4260
  setFilteredColumns((prev) => filteredEntityColumns.length > 0 ? filteredEntityColumns : prev);
4144
4261
  filteredDataColumns = filteredEntityColumns;
4145
4262
  }
@@ -4149,12 +4266,15 @@ class LineListTableComponent extends ReactWrapperModule {
4149
4266
  entityColumns = columns;
4150
4267
  entityData = data;
4151
4268
  }
4152
- const finalColumns = addActionsColumn([{ label: '#', key: 'index' }, ...entityColumns], this.actionOptions);
4269
+ const finalColumns = addActionsColumn([{ label: "#", key: "index" }, ...entityColumns], this.actionOptions);
4153
4270
  setLoading(false);
4154
- setColumns(...[finalColumns]);
4155
- setData(...[entityData]);
4156
- setPager(...[responsePager]);
4271
+ setColumns(finalColumns);
4272
+ setData(entityData);
4273
+ setPager(responsePager);
4157
4274
  });
4275
+ return () => {
4276
+ subscription.unsubscribe(); // Ensure cleanup on unmount
4277
+ };
4158
4278
  }, [
4159
4279
  programIdState,
4160
4280
  orgUnitState,
@@ -4175,20 +4295,64 @@ class LineListTableComponent extends ReactWrapperModule {
4175
4295
  },
4176
4296
  }));
4177
4297
  };
4298
+ // TODO: START::: Improve the approach on handling observables
4299
+ // const handleApprovalClick = () => {
4300
+ // setIsButtonLoading(true);
4301
+ // this.lineListService
4302
+ // .getLineListData(
4303
+ // this.programId,
4304
+ // this.orgUnit,
4305
+ // this.programStageId,
4306
+ // pager.page,
4307
+ // 1000,
4308
+ // this.attributeFilters,
4309
+ // this.startDate,
4310
+ // this.endDate,
4311
+ // this.ouMode
4312
+ // )
4313
+ // .subscribe((response: LineListResponse) => {
4314
+ // if (
4315
+ // 'trackedEntities' in response.data ||
4316
+ // 'instances' in response.data
4317
+ // ) {
4318
+ // const trackedEntityInstances = (
4319
+ // response.data as TrackedEntityResponse
4320
+ // ).trackedEntities;
4321
+ // // Map TEIs to objects with teiId and enrollmentId
4322
+ // const teiEnrollmentList = trackedEntityInstances
4323
+ // .map((tei) => {
4324
+ // // Find the enrollment where program matches this.programId
4325
+ // const matchingEnrollment = tei.enrollments?.find(
4326
+ // (enrollment) => enrollment.program === this.programId
4327
+ // );
4328
+ // // Return object with teiId and enrollmentId (if found)
4329
+ // return matchingEnrollment
4330
+ // ? {
4331
+ // teiId: tei.trackedEntity,
4332
+ // enrollmentId: matchingEnrollment.enrollment,
4333
+ // }
4334
+ // : null;
4335
+ // })
4336
+ // .filter((item) => item !== null); // Remove null entries (no matching enrollment)
4337
+ // this.approvalSelected.emit(teiEnrollmentList);
4338
+ // } else {
4339
+ // this.approvalSelected.emit([]); // Emit empty array if no TEIs
4340
+ // }
4341
+ // });
4342
+ // };
4343
+ // TODO: END::: Improve the approach on handling observables
4178
4344
  const handleApprovalClick = () => {
4179
4345
  setIsButtonLoading(true);
4180
- this.lineListService
4346
+ const subscription = this.lineListService
4181
4347
  .getLineListData(this.programId, this.orgUnit, this.programStageId, pager.page, 1000, this.attributeFilters, this.startDate, this.endDate, this.ouMode)
4348
+ .pipe(take(1)) // Ensures the observable emits only once
4182
4349
  .subscribe((response) => {
4183
- if ('trackedEntities' in response.data ||
4184
- 'instances' in response.data) {
4350
+ if ("trackedEntities" in response.data || "instances" in response.data) {
4185
4351
  const trackedEntityInstances = response.data.trackedEntities;
4186
4352
  // Map TEIs to objects with teiId and enrollmentId
4187
4353
  const teiEnrollmentList = trackedEntityInstances
4188
4354
  .map((tei) => {
4189
- // Find the enrollment where program matches this.programId
4190
4355
  const matchingEnrollment = tei.enrollments?.find((enrollment) => enrollment.program === this.programId);
4191
- // Return object with teiId and enrollmentId (if found)
4192
4356
  return matchingEnrollment
4193
4357
  ? {
4194
4358
  teiId: tei.trackedEntity,
@@ -4196,13 +4360,17 @@ class LineListTableComponent extends ReactWrapperModule {
4196
4360
  }
4197
4361
  : null;
4198
4362
  })
4199
- .filter((item) => item !== null); // Remove null entries (no matching enrollment)
4363
+ .filter(Boolean); // Removes null entries
4200
4364
  this.approvalSelected.emit(teiEnrollmentList);
4201
4365
  }
4202
4366
  else {
4203
4367
  this.approvalSelected.emit([]); // Emit empty array if no TEIs
4204
4368
  }
4369
+ setIsButtonLoading(false); // Stop loading after processing
4205
4370
  });
4371
+ return () => {
4372
+ subscription.unsubscribe(); // Cleanup to avoid potential memory leaks
4373
+ };
4206
4374
  };
4207
4375
  const handleInputChange = (key, value) => {
4208
4376
  setInputValues((prevValues) => ({
@@ -5975,9 +6143,24 @@ class BaseFormFieldComponent extends ReactWrapperModule {
5975
6143
  checkValueUniqueness();
5976
6144
  }
5977
6145
  }, [this.isValueAssigned()]);
6146
+ // TODO: START: Improve the approach of handling observables
6147
+ // useEffect(() => {
6148
+ // const fieldErrorSubscription = this.fieldError$
6149
+ // .pipe(filter((error) => error !== initialError))
6150
+ // .subscribe({
6151
+ // next: (error: string | undefined) => {
6152
+ // setInitialError(error);
6153
+ // },
6154
+ // });
6155
+ // return () => {
6156
+ // fieldErrorSubscription.unsubscribe();
6157
+ // };
6158
+ // });
6159
+ // TODO: END: Improve the approach of handling observables
5978
6160
  useEffect(() => {
5979
6161
  const fieldErrorSubscription = this.fieldError$
5980
- .pipe(filter((error) => error !== initialError))
6162
+ .pipe(filter((error) => error !== initialError), take(1) // Take only the first emitted value, then unsubscribe
6163
+ )
5981
6164
  .subscribe({
5982
6165
  next: (error) => {
5983
6166
  setInitialError(error);
@@ -5986,7 +6169,7 @@ class BaseFormFieldComponent extends ReactWrapperModule {
5986
6169
  return () => {
5987
6170
  fieldErrorSubscription.unsubscribe();
5988
6171
  };
5989
- });
6172
+ }, [initialError]); // Add initialError as a dependency to avoid stale closures
5990
6173
  const onChange = (payload) => setSelected(payload.selected);
5991
6174
  const arrayValue = useMemo(() => {
5992
6175
  if (value && value.length > 0) {
@@ -6873,14 +7056,30 @@ class SelectionFiltersComponent extends ReactWrapperModule {
6873
7056
  const [selected, setSelected] = useState([]);
6874
7057
  const [rootOrgUnits, setRootOrgUnits] = useState();
6875
7058
  const [config, setConfig] = useState();
7059
+ // TODO: START::: Improve approach on handling observables
7060
+ // useEffect(() => {
7061
+ // zip(this.getAppConfig(), this.getRootOrgUnits()).subscribe({
7062
+ // next: ([appConfig, rootOrgUnits]) => {
7063
+ // setConfig(appConfig);
7064
+ // setRootOrgUnits(rootOrgUnits);
7065
+ // },
7066
+ // error: (error) => console.error(error),
7067
+ // });
7068
+ // }, []);
7069
+ // TODO: END::: Improve approach on handling observables
6876
7070
  useEffect(() => {
6877
- zip(this.getAppConfig(), this.getRootOrgUnits()).subscribe({
7071
+ const subscription = zip(this.getAppConfig(), this.getRootOrgUnits())
7072
+ .pipe(take(1)) // Ensures only one emission
7073
+ .subscribe({
6878
7074
  next: ([appConfig, rootOrgUnits]) => {
6879
7075
  setConfig(appConfig);
6880
7076
  setRootOrgUnits(rootOrgUnits);
6881
7077
  },
6882
7078
  error: (error) => console.error(error),
6883
7079
  });
7080
+ return () => {
7081
+ subscription.unsubscribe(); // Cleanup to avoid memory leaks
7082
+ };
6884
7083
  }, []);
6885
7084
  return config ? (React__default.createElement(Provider, { config: config, plugin: false, parentAlertsAdd: undefined, showAlertsInPlugin: false }, React__default.createElement(Modal$1, { position: "middle", large: true },
6886
7085
  React__default.createElement(ModalTitle$1, null, "Organisation unit"),