@iapps/ng-dhis2-ui 1.1.56 → 1.1.59
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.
- package/fesm2022/iapps-ng-dhis2-ui.mjs +652 -268
- package/fesm2022/iapps-ng-dhis2-ui.mjs.map +1 -1
- package/lib/modules/form/components/base-form-field.component.d.ts +3 -1
- package/lib/modules/form/containers/form-field/form-field.component.d.ts +1 -0
- package/lib/modules/form/hooks/field-validation.hook.d.ts +3 -1
- package/lib/modules/form/interfaces/form-field.interface.d.ts +1 -0
- package/lib/modules/form/models/form-field.model.d.ts +1 -0
- package/lib/modules/form/models/form-metadata-section.model.d.ts +1 -0
- package/lib/modules/form/utils/field-type.util.d.ts +1 -1
- package/lib/modules/form/utils/field.util.d.ts +1 -1
- package/lib/modules/line-list/components/org-unit-form-field.component.d.ts +14 -0
- package/lib/modules/line-list/containers/line-list.component.d.ts +2 -2
- package/lib/modules/line-list/line-list.module.d.ts +2 -1
- package/lib/modules/line-list/models/line-list.models.d.ts +62 -9
- package/lib/modules/line-list/services/line-list.service.d.ts +4 -0
- package/lib/modules/line-list/utils/filter-builder.d.ts +2 -1
- package/lib/modules/program-entry-form/utils/program-entry-form-field.util.d.ts +1 -0
- package/package.json +1 -1
|
@@ -8,7 +8,7 @@ import { NgxDhis2HttpClientService } from '@iapps/ngx-dhis2-http-client';
|
|
|
8
8
|
import * as React from 'react';
|
|
9
9
|
import React__default, { useState, useCallback, useRef, useEffect, createContext, useContext, useMemo } from 'react';
|
|
10
10
|
import * as ReactDOM from 'react-dom/client';
|
|
11
|
-
import { catchError, of, map, zip, firstValueFrom, Subject, takeUntil } from 'rxjs';
|
|
11
|
+
import { catchError, of, map, zip, firstValueFrom, filter, Subject, takeUntil } from 'rxjs';
|
|
12
12
|
import i18n from '@dhis2/d2-i18n';
|
|
13
13
|
import { colors, theme, spacers } from '@dhis2/ui-constants';
|
|
14
14
|
import PropTypes from 'prop-types';
|
|
@@ -25,17 +25,17 @@ import { Layer } from '@dhis2-ui/layer';
|
|
|
25
25
|
import { CircularLoader } from '@dhis2-ui/loader';
|
|
26
26
|
import { MenuItem, MenuDivider } from '@dhis2-ui/menu';
|
|
27
27
|
import parse from 'html-react-parser';
|
|
28
|
-
import { Button as Button$1, IconDimensionData16, IconClock16, IconDimensionOrgUnit16, spacers as spacers$1, colors as colors$1, IconEdit16, theme as theme$1, Tooltip, IconChevronRight16, SingleSelectField, SingleSelectOption, InputField as InputField$1, TabBar, Tab, Transfer, Modal as Modal$1, ModalTitle as ModalTitle$1, ModalContent as ModalContent$1, ModalActions as ModalActions$1, ButtonStrip as ButtonStrip$1, Checkbox, OrganisationUnitTree, MultiSelect, MultiSelectOption, IconWarningFilled16, Layer as Layer$1, Popper, FlyoutMenu, MenuItem as MenuItem$1, IconMore24, CircularLoader as CircularLoader$1,
|
|
29
|
-
import { isObject, isString, sortBy, isArray, find, camelCase, flatten, isUndefined,
|
|
28
|
+
import { Button as Button$1, IconDimensionData16, IconClock16, IconDimensionOrgUnit16, spacers as spacers$1, colors as colors$1, IconEdit16, theme as theme$1, Tooltip, IconChevronRight16, SingleSelectField, SingleSelectOption, InputField as InputField$1, TabBar, Tab, Transfer, Modal as Modal$1, ModalTitle as ModalTitle$1, ModalContent as ModalContent$1, ModalActions as ModalActions$1, ButtonStrip as ButtonStrip$1, Checkbox, OrganisationUnitTree, MultiSelect, MultiSelectOption, IconWarningFilled16, Layer as Layer$1, Popper, FlyoutMenu, MenuItem as MenuItem$1, IconMore24, CircularLoader as CircularLoader$1, Chip, DataTableToolbar, CalendarInput, DataTable, TableHead, DataTableRow, DataTableColumnHeader, TableBody, DataTableCell, TableFoot, Pagination, FileInputField, FileListItem, MultiSelectField, TextAreaField } from '@dhis2/ui';
|
|
29
|
+
import { isObject, isString, debounce, sortBy, isArray, find, camelCase, flatten, isUndefined, head, isEmpty } from 'lodash';
|
|
30
30
|
import cx from 'classnames';
|
|
31
31
|
import { getNowInCalendar, generateFixedPeriods } from '@dhis2/multi-calendar-dates';
|
|
32
|
-
import { map as map$1
|
|
32
|
+
import { switchMap, map as map$1 } from 'rxjs/operators';
|
|
33
33
|
import * as i2 from '@angular/forms';
|
|
34
34
|
import { Validators, UntypedFormControl, UntypedFormGroup, FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
|
35
35
|
import { toObservable } from '@angular/core/rxjs-interop';
|
|
36
36
|
import { format } from 'date-fns';
|
|
37
|
+
import { DataQueryFilter, DataFilterCondition, ProgramRuleEngine } from '@iapps/d2-web-sdk';
|
|
37
38
|
import moment from 'moment';
|
|
38
|
-
import { ProgramRuleEngine } from '@iapps/d2-web-sdk';
|
|
39
39
|
|
|
40
40
|
class SharedDhis2UiModule {
|
|
41
41
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.8", ngImport: i0, type: SharedDhis2UiModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
@@ -3287,32 +3287,100 @@ const getFilteredTrackedEntityInstances = (trackedEntityInstances, filters) => {
|
|
|
3287
3287
|
event.dataValues?.some((dataValue) => dataValue.dataElement === filter.dataElement &&
|
|
3288
3288
|
matchesCondition(dataValue.value, filter.operator, filter.value))))));
|
|
3289
3289
|
};
|
|
3290
|
+
const getFilteredTrackedEntites = (trackedEntities, filters) => {
|
|
3291
|
+
if (!filters.length)
|
|
3292
|
+
return trackedEntities;
|
|
3293
|
+
return trackedEntities.filter((tei) => filters.every((filter) => tei.enrollments?.some((enrollment) => enrollment.events?.some((event) => event.programStage === filter.programStage &&
|
|
3294
|
+
event.dataValues?.some((dataValue) => dataValue.dataElement === filter.dataElement &&
|
|
3295
|
+
matchesCondition(dataValue.value, filter.operator, filter.value))))));
|
|
3296
|
+
};
|
|
3290
3297
|
|
|
3291
3298
|
class LineListService {
|
|
3292
3299
|
constructor(httpClient) {
|
|
3293
3300
|
this.httpClient = httpClient;
|
|
3294
3301
|
}
|
|
3295
3302
|
getProgramMetadata(programId) {
|
|
3296
|
-
return this.httpClient.get(`programs/${programId}.json?fields=programType,programStages[id,name,programStageDataElements[dataElement[id,name]]],
|
|
3297
|
-
}
|
|
3303
|
+
return this.httpClient.get(`programs/${programId}.json?fields=programType,programStages[id,name,programStageDataElements[dataElement[id,name]]],programTrackedEntityAttributes[*]`);
|
|
3304
|
+
}
|
|
3305
|
+
// private getTrackedEntityInstances(
|
|
3306
|
+
// programId: string,
|
|
3307
|
+
// orgUnit: string,
|
|
3308
|
+
// page: number,
|
|
3309
|
+
// pageSize: number,
|
|
3310
|
+
// filters: AttributeFilter[] = [],
|
|
3311
|
+
// startDate?: string,
|
|
3312
|
+
// endDate?: string,
|
|
3313
|
+
// ouMode?: string,
|
|
3314
|
+
// filterRootOrgUnit?: boolean
|
|
3315
|
+
// ): Observable<TrackedEntityResponse> {
|
|
3316
|
+
// const filterParams = buildFilters(filters);
|
|
3317
|
+
// // const dateFilter = startDate || endDate ? `enrollmentEnrolledAfter=${startDate}&enrollmentEnrolledBefore=${endDate}` : '';
|
|
3318
|
+
// const dateFilter = [
|
|
3319
|
+
// startDate ? `enrollmentEnrolledAfter=${startDate}` : '',
|
|
3320
|
+
// endDate ? `enrollmentEnrolledBefore=${endDate}` : '',
|
|
3321
|
+
// ].filter(Boolean).join('&');
|
|
3322
|
+
// const ouModeIdentifier = ouMode ? `&orgUnitMode=${ouMode}`: ``;
|
|
3323
|
+
// return this.httpClient.get(
|
|
3324
|
+
// `tracker/trackedEntities.json?program=${programId}&orgUnit=${orgUnit}${ouModeIdentifier}&page=${page}&pageSize=${pageSize}&fields=trackedEntity,orgUnit,attributes[*],enrollments[*]&totalPages=true&${filterParams}${dateFilter}&order=createdAt:desc`
|
|
3325
|
+
// ).pipe(
|
|
3326
|
+
// map((response: any) => {
|
|
3327
|
+
// if (filterRootOrgUnit) {
|
|
3328
|
+
// response.trackedEntities = response.trackedEntities.filter(
|
|
3329
|
+
// (tei: any) => tei.orgUnit !== orgUnit // Exclude TEIs with the root orgUnit
|
|
3330
|
+
// );
|
|
3331
|
+
// }
|
|
3332
|
+
// return response;
|
|
3333
|
+
// })
|
|
3334
|
+
// );
|
|
3335
|
+
// }
|
|
3298
3336
|
getTrackedEntityInstances(programId, orgUnit, page, pageSize, filters = [], startDate, endDate, ouMode, filterRootOrgUnit) {
|
|
3299
3337
|
const filterParams = buildFilters(filters);
|
|
3300
|
-
const dateFilter =
|
|
3301
|
-
|
|
3302
|
-
|
|
3338
|
+
const dateFilter = [
|
|
3339
|
+
startDate ? `enrollmentEnrolledAfter=${startDate}` : '',
|
|
3340
|
+
endDate ? `enrollmentEnrolledBefore=${endDate}` : '',
|
|
3341
|
+
].filter(Boolean).join('&');
|
|
3342
|
+
const ouModeIdentifier = ouMode ? `&orgUnitMode=${ouMode}` : '';
|
|
3343
|
+
return this.httpClient
|
|
3344
|
+
.get(`tracker/trackedEntities.json?program=${programId}&orgUnit=${orgUnit}${ouModeIdentifier}&page=${page}&pageSize=${pageSize}&fields=trackedEntity,orgUnit,attributes[*],enrollments[*]&totalPages=true&${filterParams}${dateFilter}&order=createdAt:desc`)
|
|
3345
|
+
.pipe(switchMap((response) => {
|
|
3303
3346
|
if (filterRootOrgUnit) {
|
|
3304
|
-
response.
|
|
3305
|
-
);
|
|
3347
|
+
response.trackedEntities = response.trackedEntities.filter((tei) => tei.orgUnit !== orgUnit);
|
|
3306
3348
|
}
|
|
3307
|
-
|
|
3349
|
+
// **Step 1:** Extract unique orgUnits from enrollments
|
|
3350
|
+
const uniqueOrgUnitIds = new Set();
|
|
3351
|
+
response.trackedEntities.forEach((tei) => {
|
|
3352
|
+
tei.enrollments?.forEach((enrollment) => {
|
|
3353
|
+
if (enrollment.program === programId) {
|
|
3354
|
+
uniqueOrgUnitIds.add(enrollment.orgUnit);
|
|
3355
|
+
}
|
|
3356
|
+
});
|
|
3357
|
+
});
|
|
3358
|
+
// If no unique orgUnits found, return immediately
|
|
3359
|
+
if (uniqueOrgUnitIds.size === 0) {
|
|
3360
|
+
return of({ ...response, orgUnitsMap: new Map() });
|
|
3361
|
+
}
|
|
3362
|
+
// **Step 2:** Fetch orgUnit names in a single request
|
|
3363
|
+
return this.fetchOrgUnits(Array.from(uniqueOrgUnitIds)).pipe(map$1((orgUnitsMap) => ({
|
|
3364
|
+
...response,
|
|
3365
|
+
orgUnitsMap, // Attach orgUnitId → orgUnitName map
|
|
3366
|
+
})));
|
|
3367
|
+
}));
|
|
3368
|
+
}
|
|
3369
|
+
//filter=id:in:[id1,id2,id3]
|
|
3370
|
+
/**
|
|
3371
|
+
* Fetch orgUnit names for given orgUnit IDs
|
|
3372
|
+
*/
|
|
3373
|
+
fetchOrgUnits(orgUnitIds) {
|
|
3374
|
+
const idsQuery = `filter=id:in:[${orgUnitIds}]`;
|
|
3375
|
+
return this.httpClient
|
|
3376
|
+
.get(`organisationUnits.json?fields=id,name&paging=false&${idsQuery}`)
|
|
3377
|
+
.pipe(map$1((response) => {
|
|
3378
|
+
const orgUnitsMap = new Map();
|
|
3379
|
+
response.organisationUnits.forEach((orgUnit) => {
|
|
3380
|
+
orgUnitsMap.set(orgUnit.id, orgUnit.name);
|
|
3381
|
+
});
|
|
3382
|
+
return orgUnitsMap;
|
|
3308
3383
|
}));
|
|
3309
|
-
// pipe(
|
|
3310
|
-
// map((response: any) => {
|
|
3311
|
-
// //TODO: use the api to to order by desc
|
|
3312
|
-
// // response.trackedEntityInstances.reverse();
|
|
3313
|
-
// return response;
|
|
3314
|
-
// })
|
|
3315
|
-
// );
|
|
3316
3384
|
}
|
|
3317
3385
|
getEvents(programId, orgUnit, page, pageSize, filters = [], startDate, endDate, ouMode) {
|
|
3318
3386
|
const filterParams = buildFilters(filters);
|
|
@@ -3338,6 +3406,7 @@ class LineListService {
|
|
|
3338
3406
|
return this.getTrackedEntityInstances(programId, orgUnit, page, pageSize, filters, startDate, endDate, ouMode, filterRootOrgUnit).pipe(map$1((teis) => ({
|
|
3339
3407
|
metadata: programMetadata,
|
|
3340
3408
|
data: teis,
|
|
3409
|
+
orgUnits: teis.orgUnitsMap
|
|
3341
3410
|
})));
|
|
3342
3411
|
}
|
|
3343
3412
|
else {
|
|
@@ -3382,15 +3451,18 @@ const getProgramStageData = (response, programStageId, pager) => {
|
|
|
3382
3451
|
};
|
|
3383
3452
|
const getTrackedEntityData = (response, programId, pager, filters) => {
|
|
3384
3453
|
let teis = response.data
|
|
3385
|
-
.
|
|
3454
|
+
.trackedEntities;
|
|
3386
3455
|
let programMetaData = response.metadata.programTrackedEntityAttributes;
|
|
3387
|
-
//organisation units from metadata
|
|
3388
|
-
let orgUnitsFromMetaData = response.metadata.organisationUnits;
|
|
3389
|
-
//a Map to efficiently look up orgUnit names by ID
|
|
3390
|
-
const orgUnitMap = new Map(
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3456
|
+
// //organisation units from metadata
|
|
3457
|
+
// let orgUnitsFromMetaData = response.metadata.organisationUnits;
|
|
3458
|
+
// //a Map to efficiently look up orgUnit names by ID
|
|
3459
|
+
// const orgUnitMap = new Map<string, string>(
|
|
3460
|
+
// orgUnitsFromMetaData.map((ou: { id: string; name: string }) => [
|
|
3461
|
+
// ou.id,
|
|
3462
|
+
// ou.name,
|
|
3463
|
+
// ])
|
|
3464
|
+
// );
|
|
3465
|
+
const orgUnitMap = response.data.orgUnitsMap;
|
|
3394
3466
|
const mappedProgramMetadataAttributes = programMetaData.map((attr) => ({
|
|
3395
3467
|
displayInList: attr.displayInList,
|
|
3396
3468
|
searchable: attr.searchable,
|
|
@@ -3403,7 +3475,7 @@ const getTrackedEntityData = (response, programId, pager, filters) => {
|
|
|
3403
3475
|
return acc;
|
|
3404
3476
|
}, {});
|
|
3405
3477
|
if (filters) {
|
|
3406
|
-
teis =
|
|
3478
|
+
teis = getFilteredTrackedEntites(teis, filters);
|
|
3407
3479
|
}
|
|
3408
3480
|
const allAttributes = new Set();
|
|
3409
3481
|
teis.forEach((tei) => {
|
|
@@ -3431,10 +3503,6 @@ const getTrackedEntityData = (response, programId, pager, filters) => {
|
|
|
3431
3503
|
// Check if the column label is part of any value in programAttributesMap
|
|
3432
3504
|
return Object.values(programAttributesMap).some((value) => value.includes(column.label));
|
|
3433
3505
|
});
|
|
3434
|
-
// entityColumns.push({
|
|
3435
|
-
// label: 'Location',
|
|
3436
|
-
// key: 'orgUnit',
|
|
3437
|
-
// });
|
|
3438
3506
|
// Add orgUnit as the first column
|
|
3439
3507
|
entityColumns.unshift({
|
|
3440
3508
|
label: 'Location',
|
|
@@ -3462,9 +3530,14 @@ const getTrackedEntityData = (response, programId, pager, filters) => {
|
|
|
3462
3530
|
: tei.attributes;
|
|
3463
3531
|
attributesToUse.forEach((attr) => (row[attr.attribute] = attr.value));
|
|
3464
3532
|
//include orgUnit in the row for id access
|
|
3465
|
-
row['orgUnitId'] =
|
|
3533
|
+
row['orgUnitId'] = matchingEnrollment.orgUnit;
|
|
3466
3534
|
// Access the name of the orgUnit using the Map
|
|
3467
|
-
row['orgUnit'] =
|
|
3535
|
+
// row['orgUnit'] = matchingEnrollment.orgUnitName;
|
|
3536
|
+
row['orgUnit'] = orgUnitMap?.get(matchingEnrollment.orgUnit) || 'N/A';
|
|
3537
|
+
// row['orgUnit'] =
|
|
3538
|
+
// orgUnitMap && orgUnitMap[matchingEnrollment.orgUnit]
|
|
3539
|
+
// ? orgUnitMap[matchingEnrollment.orgUnit]
|
|
3540
|
+
// : 'N/A';
|
|
3468
3541
|
return row;
|
|
3469
3542
|
});
|
|
3470
3543
|
return {
|
|
@@ -3503,12 +3576,195 @@ const addActionsColumn = (columns, actionOptions) => {
|
|
|
3503
3576
|
];
|
|
3504
3577
|
};
|
|
3505
3578
|
|
|
3579
|
+
// Copyright 2025 UDSM DHIS2 Lab. All rights reserved.
|
|
3580
|
+
// Use of this source code is governed by a BSD-style
|
|
3581
|
+
// license that can be found in the LICENSE file.
|
|
3582
|
+
// @flow
|
|
3583
|
+
const orgUnitFieldStyles$1 = {
|
|
3584
|
+
container: {
|
|
3585
|
+
border: '1px solid',
|
|
3586
|
+
borderColor: colors$1.grey400,
|
|
3587
|
+
borderRadius: '3px',
|
|
3588
|
+
height: '100%',
|
|
3589
|
+
width: '100%',
|
|
3590
|
+
zIndex: 0,
|
|
3591
|
+
backgroundColor: 'white',
|
|
3592
|
+
},
|
|
3593
|
+
searchField: {
|
|
3594
|
+
height: '40px',
|
|
3595
|
+
overflow: 'hidden',
|
|
3596
|
+
borderBottomLeftRadius: 0,
|
|
3597
|
+
borderBottomRightRadius: 0,
|
|
3598
|
+
boxShadow: 'none',
|
|
3599
|
+
border: 'none',
|
|
3600
|
+
},
|
|
3601
|
+
debounceFieldContainer: {
|
|
3602
|
+
padding: '8px',
|
|
3603
|
+
background: colors$1.grey100,
|
|
3604
|
+
borderTopLeftRadius: '3px',
|
|
3605
|
+
borderTopRightRadius: '3px',
|
|
3606
|
+
},
|
|
3607
|
+
orgUnitTreeContainer: {
|
|
3608
|
+
borderBottomLeftRadius: '3px',
|
|
3609
|
+
borderBottomRightRadius: '3px',
|
|
3610
|
+
overflow: 'auto',
|
|
3611
|
+
},
|
|
3612
|
+
orgUnitTreeLoader: {
|
|
3613
|
+
padding: '8px',
|
|
3614
|
+
},
|
|
3615
|
+
orgUnitFieldLabel: {
|
|
3616
|
+
marginBottom: '8px',
|
|
3617
|
+
display: 'block',
|
|
3618
|
+
boxSizing: 'border-box',
|
|
3619
|
+
fontSize: '14px',
|
|
3620
|
+
lineHeight: '19px',
|
|
3621
|
+
color: 'rgb(33, 41, 52)',
|
|
3622
|
+
padding: '0',
|
|
3623
|
+
},
|
|
3624
|
+
orgUnitFieldRequiredLabel: {
|
|
3625
|
+
paddingInlineStart: '4px',
|
|
3626
|
+
},
|
|
3627
|
+
};
|
|
3628
|
+
const OrgUnitFormField$1 = (props) => {
|
|
3629
|
+
const { onSelectOrgUnit, label, required, selected, disabled, key } = props;
|
|
3630
|
+
const d2 = window?.d2Web;
|
|
3631
|
+
const classes = useDynamicStyles$2(orgUnitFieldStyles$1);
|
|
3632
|
+
const config = useMemo(() => {
|
|
3633
|
+
return d2.systemInfo?.toInitObject();
|
|
3634
|
+
}, []);
|
|
3635
|
+
const rootOrgUnits = useMemo(() => {
|
|
3636
|
+
return d2.currentUser?.organisationUnits || [];
|
|
3637
|
+
}, []);
|
|
3638
|
+
const getExpandedItems = () => {
|
|
3639
|
+
if (rootOrgUnits && rootOrgUnits.length === 1) {
|
|
3640
|
+
return [`/${rootOrgUnits[0].id}`];
|
|
3641
|
+
}
|
|
3642
|
+
else if (rootOrgUnits?.length > 1) {
|
|
3643
|
+
return rootOrgUnits.map((root) => root.path);
|
|
3644
|
+
}
|
|
3645
|
+
return undefined;
|
|
3646
|
+
};
|
|
3647
|
+
const initiallyExpanded = getExpandedItems();
|
|
3648
|
+
const [searchText, setSearchText] = useState(undefined);
|
|
3649
|
+
const [loading, setLoading] = useState(true);
|
|
3650
|
+
const [searchLoading, setSearchLoading] = useState();
|
|
3651
|
+
const [searchData, setSearchData] = useState();
|
|
3652
|
+
const [selectedOrgUnit, setSelectedOrgUnit] = useState();
|
|
3653
|
+
const [expanded, setExpanded] = useState(initiallyExpanded);
|
|
3654
|
+
const [showOrgUnitTree, setShowOrgUnitTree] = useState(!selected);
|
|
3655
|
+
useEffect(() => {
|
|
3656
|
+
setLoading(true);
|
|
3657
|
+
if (selected) {
|
|
3658
|
+
d2.httpInstance
|
|
3659
|
+
.get(`organisationUnits/${selected}.json?fields=id,displayName,path,code,ancestors[id,displayName]`)
|
|
3660
|
+
.then((response) => {
|
|
3661
|
+
if (response.data) {
|
|
3662
|
+
setSelectedOrgUnit(response.data);
|
|
3663
|
+
handleExpand({ path: response.data['path'] });
|
|
3664
|
+
setLoading(false);
|
|
3665
|
+
}
|
|
3666
|
+
});
|
|
3667
|
+
}
|
|
3668
|
+
else {
|
|
3669
|
+
setShowOrgUnitTree(true);
|
|
3670
|
+
setLoading(false);
|
|
3671
|
+
}
|
|
3672
|
+
}, []);
|
|
3673
|
+
const highlighted = useMemo(() => {
|
|
3674
|
+
if (selectedOrgUnit) {
|
|
3675
|
+
return [selectedOrgUnit['path']];
|
|
3676
|
+
}
|
|
3677
|
+
return undefined;
|
|
3678
|
+
}, [selectedOrgUnit]);
|
|
3679
|
+
const selectedOrgUnitLabel = useMemo(() => {
|
|
3680
|
+
if (selectedOrgUnit) {
|
|
3681
|
+
return [
|
|
3682
|
+
...(selectedOrgUnit['ancestors'] || []),
|
|
3683
|
+
selectedOrgUnit,
|
|
3684
|
+
]
|
|
3685
|
+
.map((orgUnit) => orgUnit['displayName'])
|
|
3686
|
+
.join(' / ');
|
|
3687
|
+
}
|
|
3688
|
+
return '';
|
|
3689
|
+
}, [selectedOrgUnit]);
|
|
3690
|
+
const handleExpand = ({ path }) => {
|
|
3691
|
+
if (expanded && !expanded.includes(path)) {
|
|
3692
|
+
setExpanded([...expanded, path]);
|
|
3693
|
+
}
|
|
3694
|
+
};
|
|
3695
|
+
const handleCollapse = ({ path }) => {
|
|
3696
|
+
const pathIndex = expanded?.indexOf(path);
|
|
3697
|
+
if (pathIndex && pathIndex !== -1 && expanded) {
|
|
3698
|
+
const updatedExpanded = pathIndex === 0
|
|
3699
|
+
? expanded.slice(1)
|
|
3700
|
+
: [...expanded.slice(0, pathIndex), ...expanded.slice(pathIndex + 1)];
|
|
3701
|
+
setExpanded(updatedExpanded);
|
|
3702
|
+
}
|
|
3703
|
+
};
|
|
3704
|
+
const handleOrgUnitSearch = async (query) => {
|
|
3705
|
+
setSearchLoading(true);
|
|
3706
|
+
try {
|
|
3707
|
+
const searchResponse = await d2.httpInstance.get(`organisationUnits.json?fields=id,displayName,path,publicAccess,access,lastUpdated,children[id,displayName,publicAccess,access,path,children::isNotEmpty]&paging=true&query=${query}&withinUserSearchHierarchy=true&pageSize=15`);
|
|
3708
|
+
setSearchData((searchResponse.data || {})['organisationUnits']);
|
|
3709
|
+
setSearchLoading(false);
|
|
3710
|
+
}
|
|
3711
|
+
catch (e) {
|
|
3712
|
+
setSearchLoading(false);
|
|
3713
|
+
}
|
|
3714
|
+
};
|
|
3715
|
+
const debouncedSearch = useCallback(debounce((value) => {
|
|
3716
|
+
handleOrgUnitSearch(value);
|
|
3717
|
+
}, 500), []);
|
|
3718
|
+
const handleFilterChange = (event) => {
|
|
3719
|
+
setSearchText(event.value);
|
|
3720
|
+
debouncedSearch(event.value);
|
|
3721
|
+
};
|
|
3722
|
+
const ready = useMemo(() => {
|
|
3723
|
+
return searchText?.length ? !searchLoading : !loading;
|
|
3724
|
+
}, [searchText, searchLoading, loading]);
|
|
3725
|
+
const renderOrgUnitTree = () => {
|
|
3726
|
+
if (searchText && searchText?.length > 0) {
|
|
3727
|
+
if (searchLoading) {
|
|
3728
|
+
return (React__default.createElement("div", { className: classes.orgUnitTreeLoader },
|
|
3729
|
+
React__default.createElement(CircularLoader$1, { small: true })));
|
|
3730
|
+
}
|
|
3731
|
+
return (ready && (React__default.createElement(OrganisationUnitTree, { key: `${key}-search`, roots: searchData?.map((orgUnit) => orgUnit.id) || [], singleSelection: true, expanded: expanded, handleExpand: handleExpand, handleCollapse: handleCollapse, selected: highlighted, onChange: (event) => {
|
|
3732
|
+
setSelectedOrgUnit(event);
|
|
3733
|
+
setShowOrgUnitTree(false);
|
|
3734
|
+
onSelectOrgUnit(event);
|
|
3735
|
+
} })));
|
|
3736
|
+
}
|
|
3737
|
+
return (React__default.createElement(OrganisationUnitTree, { key: key, roots: rootOrgUnits?.map((orgUnit) => orgUnit.id) || [], singleSelection: true, expanded: expanded, handleExpand: handleExpand, handleCollapse: handleCollapse, selected: highlighted, onChange: (event) => {
|
|
3738
|
+
setSelectedOrgUnit(event);
|
|
3739
|
+
setShowOrgUnitTree(false);
|
|
3740
|
+
onSelectOrgUnit(event);
|
|
3741
|
+
} }));
|
|
3742
|
+
};
|
|
3743
|
+
const handleBlur = () => {
|
|
3744
|
+
// onBlur && onBlur(null);
|
|
3745
|
+
};
|
|
3746
|
+
return (config && (React__default.createElement(Provider, { config: config, plugin: false, parentAlertsAdd: undefined, showAlertsInPlugin: false },
|
|
3747
|
+
React__default.createElement("div", null,
|
|
3748
|
+
label && (React__default.createElement("div", { className: classes.orgUnitFieldLabel },
|
|
3749
|
+
React__default.createElement("span", null, label),
|
|
3750
|
+
required ? (React__default.createElement("span", { className: classes.orgUnitFieldRequiredLabel }, "*")) : (React__default.createElement(React__default.Fragment, null)))),
|
|
3751
|
+
!showOrgUnitTree ? (selectedOrgUnit ? (React__default.createElement(React__default.Fragment, null,
|
|
3752
|
+
React__default.createElement(Tooltip, { content: selectedOrgUnitLabel },
|
|
3753
|
+
React__default.createElement(Chip, { onRemove: () => {
|
|
3754
|
+
setShowOrgUnitTree(true);
|
|
3755
|
+
} }, selectedOrgUnit.displayName)))) : (React__default.createElement("div", { className: classes.orgUnitTreeLoader },
|
|
3756
|
+
React__default.createElement(CircularLoader$1, { small: true })))) : (React__default.createElement("div", { className: classes.container },
|
|
3757
|
+
React__default.createElement("div", { className: classes.debounceFieldContainer },
|
|
3758
|
+
React__default.createElement(InputField$1, { placeholder: "Search", value: searchText, disabled: disabled, onChange: handleFilterChange })),
|
|
3759
|
+
loading ? (React__default.createElement("div", { className: classes.orgUnitTreeLoader },
|
|
3760
|
+
React__default.createElement(CircularLoader$1, { small: true }))) : (React__default.createElement("div", { className: classes.orgUnitTreeContainer, style: { maxHeight: '200px', overflowY: 'auto' } }, renderOrgUnitTree()))))))));
|
|
3761
|
+
};
|
|
3762
|
+
|
|
3506
3763
|
// src/app/line-list-table.component.ts
|
|
3507
3764
|
class LineListTableComponent extends ReactWrapperModule {
|
|
3508
3765
|
constructor() {
|
|
3509
3766
|
super(...arguments);
|
|
3510
3767
|
this.dispatchTeis = false;
|
|
3511
|
-
this.showFilterButton = false;
|
|
3512
3768
|
this.actionSelected = new EventEmitter();
|
|
3513
3769
|
this.reactStateUpdaters = null;
|
|
3514
3770
|
this.approvalSelected = new EventEmitter();
|
|
@@ -3516,6 +3772,7 @@ class LineListTableComponent extends ReactWrapperModule {
|
|
|
3516
3772
|
this.buttonLabel = '';
|
|
3517
3773
|
this.firstValue = new EventEmitter();
|
|
3518
3774
|
this.filterRootOrgUnit = false;
|
|
3775
|
+
this.showFilters = false;
|
|
3519
3776
|
this.setReactStateUpdaters = (updaters) => {
|
|
3520
3777
|
this.reactStateUpdaters = updaters;
|
|
3521
3778
|
};
|
|
@@ -3544,6 +3801,12 @@ class LineListTableComponent extends ReactWrapperModule {
|
|
|
3544
3801
|
//TODO: this will be migrated on the parent component
|
|
3545
3802
|
const [checkValue, setCheckValue] = useState([]);
|
|
3546
3803
|
const [valueMatch, setValuesMatch] = useState(false);
|
|
3804
|
+
const [selectedOrgUnit, setSelectedOrgUnit] = useState(''); // for testing purposes to be removed
|
|
3805
|
+
const [hide, setHide] = useState(true);
|
|
3806
|
+
const [showAllFilters, setShowAllFilters] = useState(false);
|
|
3807
|
+
const visibleFilters = showAllFilters
|
|
3808
|
+
? filteredColumns ?? []
|
|
3809
|
+
: (filteredColumns ?? []).slice(0, 2);
|
|
3547
3810
|
// Store updaters in refs for Angular to access
|
|
3548
3811
|
const updateRefs = useRef({
|
|
3549
3812
|
setProgramIdState,
|
|
@@ -3557,25 +3820,10 @@ class LineListTableComponent extends ReactWrapperModule {
|
|
|
3557
3820
|
useEffect(() => {
|
|
3558
3821
|
this.setReactStateUpdaters?.(updateRefs.current);
|
|
3559
3822
|
}, []);
|
|
3560
|
-
useEffect(() => {
|
|
3561
|
-
if (!loading) {
|
|
3562
|
-
setTimeout(() => {
|
|
3563
|
-
const paginationContainer = document.querySelector('[data-test="dhis2-uiwidgets-pagination"]');
|
|
3564
|
-
if (paginationContainer) {
|
|
3565
|
-
paginationContainer.style.display = 'flex';
|
|
3566
|
-
paginationContainer.style.flexDirection = 'row';
|
|
3567
|
-
paginationContainer.style.justifyContent = 'space-between';
|
|
3568
|
-
paginationContainer.style.alignItems = 'center';
|
|
3569
|
-
}
|
|
3570
|
-
}, 500); // Short delay to ensure styles apply after data renders
|
|
3571
|
-
}
|
|
3572
|
-
}, [loading]);
|
|
3573
3823
|
useEffect(() => {
|
|
3574
3824
|
setLoading(true);
|
|
3575
3825
|
this.lineListService
|
|
3576
|
-
.getLineListData(this.programId,
|
|
3577
|
-
// this.attributeFilters,
|
|
3578
|
-
this.startDate, this.endDate, this.ouMode, this.filterRootOrgUnit)
|
|
3826
|
+
.getLineListData(this.programId, orgUnitState, this.programStageId, pager.page, pager.pageSize, attributeFiltersState, startDateState, endDateState, this.ouMode, this.filterRootOrgUnit)
|
|
3579
3827
|
.subscribe((response) => {
|
|
3580
3828
|
let entityColumns = [];
|
|
3581
3829
|
let responsePager;
|
|
@@ -3587,15 +3835,14 @@ class LineListTableComponent extends ReactWrapperModule {
|
|
|
3587
3835
|
entityColumns = columns;
|
|
3588
3836
|
entityData = data;
|
|
3589
3837
|
}
|
|
3590
|
-
else if ('
|
|
3591
|
-
responsePager = response.data
|
|
3592
|
-
.pager;
|
|
3838
|
+
else if ('trackedEntities' in response.data) {
|
|
3839
|
+
responsePager = response.data.pager;
|
|
3593
3840
|
const { columns, data, filteredEntityColumns } = getTrackedEntityData(response, this.programId, pager, this.filters);
|
|
3594
3841
|
entityColumns = columns;
|
|
3595
3842
|
entityData = data;
|
|
3596
3843
|
//TODO: Should be done on the parent
|
|
3597
3844
|
let checkValues = [
|
|
3598
|
-
...new Set(response.data?.
|
|
3845
|
+
...new Set(response.data?.trackedEntities?.[0]?.enrollments?.[0]?.events?.flatMap((event) => event.dataValues
|
|
3599
3846
|
.filter((dataValue) => /^[A-Za-z]{3}$/.test(dataValue.value))
|
|
3600
3847
|
.map((dataValue) => dataValue.value))),
|
|
3601
3848
|
];
|
|
@@ -3646,8 +3893,8 @@ class LineListTableComponent extends ReactWrapperModule {
|
|
|
3646
3893
|
this.lineListService
|
|
3647
3894
|
.getLineListData(this.programId, this.orgUnit, this.programStageId, pager.page, 1000, this.attributeFilters, this.startDate, this.endDate, this.ouMode)
|
|
3648
3895
|
.subscribe((response) => {
|
|
3649
|
-
if ('
|
|
3650
|
-
const trackedEntityInstances = response.data.
|
|
3896
|
+
if ('trackedEntities' in response.data) {
|
|
3897
|
+
const trackedEntityInstances = response.data.trackedEntities;
|
|
3651
3898
|
// Map TEIs to objects with teiId and enrollmentId
|
|
3652
3899
|
const teiEnrollmentList = trackedEntityInstances
|
|
3653
3900
|
.map((tei) => {
|
|
@@ -3656,7 +3903,7 @@ class LineListTableComponent extends ReactWrapperModule {
|
|
|
3656
3903
|
// Return object with teiId and enrollmentId (if found)
|
|
3657
3904
|
return matchingEnrollment
|
|
3658
3905
|
? {
|
|
3659
|
-
teiId: tei.
|
|
3906
|
+
teiId: tei.trackedEntity,
|
|
3660
3907
|
enrollmentId: matchingEnrollment.enrollment,
|
|
3661
3908
|
}
|
|
3662
3909
|
: null;
|
|
@@ -3685,55 +3932,17 @@ class LineListTableComponent extends ReactWrapperModule {
|
|
|
3685
3932
|
});
|
|
3686
3933
|
};
|
|
3687
3934
|
return (React__default.createElement("div", null,
|
|
3688
|
-
React__default.createElement("div", null,
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
}, onClick: handleApprovalClick, primary: true }, this.buttonLabel))),
|
|
3700
|
-
this.showFilterButton && (React__default.createElement(Button$1
|
|
3701
|
-
// style={{
|
|
3702
|
-
// backgroundColor: '#6b7280',
|
|
3703
|
-
// color: 'white',
|
|
3704
|
-
// padding: '8px 16px',
|
|
3705
|
-
// borderRadius: '4px',
|
|
3706
|
-
// cursor: 'pointer',
|
|
3707
|
-
// display: 'flex',
|
|
3708
|
-
// alignItems: 'center',
|
|
3709
|
-
// gap: 8,
|
|
3710
|
-
// }}
|
|
3711
|
-
, {
|
|
3712
|
-
// style={{
|
|
3713
|
-
// backgroundColor: '#6b7280',
|
|
3714
|
-
// color: 'white',
|
|
3715
|
-
// padding: '8px 16px',
|
|
3716
|
-
// borderRadius: '4px',
|
|
3717
|
-
// cursor: 'pointer',
|
|
3718
|
-
// display: 'flex',
|
|
3719
|
-
// alignItems: 'center',
|
|
3720
|
-
// gap: 8,
|
|
3721
|
-
// }}
|
|
3722
|
-
onClick: () => {
|
|
3723
|
-
setFilters(!filters);
|
|
3724
|
-
}, secondary: true },
|
|
3725
|
-
React__default.createElement(IconFilter16, null),
|
|
3726
|
-
filters ? 'Hide Filters' : 'Show Filters'))),
|
|
3727
|
-
filters && (React__default.createElement("div", null, (filteredColumns ?? []).map(({ label, key }) => (React__default.createElement(InputField$1, { key: key, label: label, value: inputValues[key] || '', onChange: (e) => {
|
|
3728
|
-
// Check if it's a standard event with target.value
|
|
3729
|
-
if ('target' in e && e.target) {
|
|
3730
|
-
handleInputChange(key, e.target.value);
|
|
3731
|
-
}
|
|
3732
|
-
// Check if it's an event with direct value
|
|
3733
|
-
else if ('value' in e) {
|
|
3734
|
-
handleInputChange(key, e.value ?? '');
|
|
3735
|
-
}
|
|
3736
|
-
} }))))),
|
|
3935
|
+
React__default.createElement("div", null, valueMatch &&
|
|
3936
|
+
this.dispatchTeis &&
|
|
3937
|
+
(isButtonLoading ? (React__default.createElement(CircularLoader$1, { small: true })) : (
|
|
3938
|
+
//TODO: this implementation should be done where the library will be consumed
|
|
3939
|
+
React__default.createElement(Button$1, { style: {
|
|
3940
|
+
backgroundColor: '#3b82f6',
|
|
3941
|
+
color: 'white',
|
|
3942
|
+
padding: '8px 16px',
|
|
3943
|
+
borderRadius: '4px',
|
|
3944
|
+
cursor: 'pointer',
|
|
3945
|
+
}, onClick: handleApprovalClick, primary: true }, this.buttonLabel)))),
|
|
3737
3946
|
loading ? (React__default.createElement("div", { style: {
|
|
3738
3947
|
display: 'flex',
|
|
3739
3948
|
alignItems: 'center',
|
|
@@ -3743,29 +3952,90 @@ class LineListTableComponent extends ReactWrapperModule {
|
|
|
3743
3952
|
React__default.createElement(CircularLoader$1, { small: true }),
|
|
3744
3953
|
React__default.createElement("div", { style: {
|
|
3745
3954
|
fontSize: 14,
|
|
3746
|
-
} }, "Loading"))) : (
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
React__default.createElement(
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3955
|
+
} }, "Loading"))) : (
|
|
3956
|
+
// <div>
|
|
3957
|
+
// <div/>
|
|
3958
|
+
React__default.createElement("div", null,
|
|
3959
|
+
React__default.createElement(Modal$1, { hide: hide },
|
|
3960
|
+
React__default.createElement(ModalTitle$1, null, "Select organization unit"),
|
|
3961
|
+
React__default.createElement(ModalContent$1, null,
|
|
3962
|
+
React__default.createElement(OrgUnitFormField$1
|
|
3963
|
+
// label="Select organization unit"
|
|
3964
|
+
, {
|
|
3965
|
+
// label="Select organization unit"
|
|
3966
|
+
key: selectedOrgUnit, onSelectOrgUnit: (selectedOrgUnit) => {
|
|
3967
|
+
// console.log(
|
|
3968
|
+
// 'this is the selected org unit key',
|
|
3969
|
+
// selectedOrgUnit
|
|
3970
|
+
// );
|
|
3971
|
+
setSelectedOrgUnit(selectedOrgUnit.displayName);
|
|
3972
|
+
setOrgUnitState(selectedOrgUnit.id);
|
|
3973
|
+
setHide(true);
|
|
3974
|
+
} })),
|
|
3975
|
+
React__default.createElement(ModalActions$1, null,
|
|
3976
|
+
React__default.createElement(ButtonStrip$1, { end: true },
|
|
3977
|
+
React__default.createElement(Button$1, { onClick: () => setHide(true), secondary: true }, "Close")))),
|
|
3978
|
+
this.showFilters && (React__default.createElement(DataTableToolbar, { className: "table-top-toolbar" },
|
|
3979
|
+
React__default.createElement("div", { style: {
|
|
3980
|
+
display: 'flex',
|
|
3981
|
+
gap: '10px',
|
|
3982
|
+
flexWrap: 'wrap',
|
|
3983
|
+
} },
|
|
3984
|
+
React__default.createElement(InputField$1, { key: "location", label: "Location:", value: selectedOrgUnit, onFocus: () => setHide(false), className: "custom-input" }),
|
|
3985
|
+
React__default.createElement(CalendarInput, { label: "Start Date:", calendar: "gregory", locale: "en-GB", timeZone: "Africa/Dar_es_Salaam", className: "custom-input", date: startDateState, onDateSelect: (selectedDate) => {
|
|
3986
|
+
setStartDateState(selectedDate.calendarDateString);
|
|
3987
|
+
} }),
|
|
3988
|
+
React__default.createElement(CalendarInput, { label: "End Date:", calendar: "gregory", locale: "en-GB", timeZone: "Africa/Dar_es_Salaam", className: "custom-input", date: endDateState, onDateSelect: (selectedDate) => {
|
|
3989
|
+
setEndDateState(selectedDate.calendarDateString);
|
|
3990
|
+
} }),
|
|
3991
|
+
(visibleFilters ?? []).map(({ label, key }) => (React__default.createElement(InputField$1, { key: key, label: `${label}:`, className: "custom-input", value: inputValues[key] || '', onChange: (e) => {
|
|
3992
|
+
if ('target' in e && e.target) {
|
|
3993
|
+
setInputValues((prevValues) => ({
|
|
3994
|
+
...prevValues,
|
|
3995
|
+
[key]: e
|
|
3996
|
+
.target.value,
|
|
3997
|
+
}));
|
|
3998
|
+
}
|
|
3999
|
+
else if ('value' in e) {
|
|
4000
|
+
setInputValues((prevValues) => ({
|
|
4001
|
+
...prevValues,
|
|
4002
|
+
[key]: e.value ?? '',
|
|
4003
|
+
}));
|
|
4004
|
+
}
|
|
4005
|
+
}, onBlur: (e) => {
|
|
4006
|
+
let currentValue = '';
|
|
4007
|
+
if ('target' in e && e.target) {
|
|
4008
|
+
currentValue = e.target.value;
|
|
4009
|
+
}
|
|
4010
|
+
else if ('value' in e) {
|
|
4011
|
+
currentValue = e.value ?? '';
|
|
4012
|
+
}
|
|
4013
|
+
handleInputChange(key, currentValue);
|
|
4014
|
+
} }))),
|
|
4015
|
+
React__default.createElement(Button$1, { onClick: () => setShowAllFilters(!showAllFilters), className: "custom-button", secondary: true, large: true }, showAllFilters ? 'Less Filters' : 'More Filters')))),
|
|
4016
|
+
React__default.createElement(DataTable, null,
|
|
4017
|
+
React__default.createElement(TableHead, null,
|
|
4018
|
+
React__default.createElement(DataTableRow, null, columns.map((col) => (React__default.createElement(DataTableColumnHeader, { key: col.key }, col.label))))),
|
|
4019
|
+
React__default.createElement(TableBody, null, data.length > 0 ? (data.map((row) => (React__default.createElement(DataTableRow, { key: row.index }, columns.map((col) => (React__default.createElement(DataTableCell, { key: col.key }, col.key === 'actions' ? (React__default.createElement(DropdownMenu$1, { dropdownOptions: getDropdownOptions(row), onClick: (option) => option.onClick?.() })) : (row[col.key] || '--' // Display '--' if value is undefined or null
|
|
4020
|
+
)))))))) : (React__default.createElement(DataTableRow, null,
|
|
4021
|
+
React__default.createElement(DataTableCell, { colSpan: columns.length?.toString(), style: {
|
|
4022
|
+
textAlign: 'center',
|
|
4023
|
+
fontWeight: 'bold',
|
|
4024
|
+
color: 'grey',
|
|
4025
|
+
padding: '20px',
|
|
4026
|
+
} }, "No data found")))),
|
|
4027
|
+
React__default.createElement(TableFoot, null,
|
|
4028
|
+
React__default.createElement(DataTableRow, null,
|
|
4029
|
+
React__default.createElement(DataTableCell, { colSpan: columns.length.toString() },
|
|
4030
|
+
React__default.createElement("div", null,
|
|
4031
|
+
React__default.createElement(Pagination, { page: pager.page, pageCount: pager.pageCount, pageSize: pager.pageSize, total: pager.total, onPageChange: (page) => setPager((prev) => ({ ...prev, page })), onPageSizeChange: (pageSize) => {
|
|
4032
|
+
const newPageSize = Number(pageSize);
|
|
4033
|
+
setPager((prev) => ({
|
|
4034
|
+
...prev,
|
|
4035
|
+
page: 1,
|
|
4036
|
+
pageSize: newPageSize,
|
|
4037
|
+
}));
|
|
4038
|
+
}, pageSizes: ['10', '20', '50', '100', '500'] }))))))))));
|
|
3769
4039
|
};
|
|
3770
4040
|
}
|
|
3771
4041
|
ngOnChanges(changes) {
|
|
@@ -3801,11 +4071,11 @@ class LineListTableComponent extends ReactWrapperModule {
|
|
|
3801
4071
|
this.render();
|
|
3802
4072
|
}
|
|
3803
4073
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.8", ngImport: i0, type: LineListTableComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
3804
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.8", type: LineListTableComponent, isStandalone: false, selector: "
|
|
4074
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.8", type: LineListTableComponent, isStandalone: false, selector: "ng-dhis2-ui-line-list", inputs: { programId: "programId", orgUnit: "orgUnit", programStageId: "programStageId", actionOptions: "actionOptions", attributeFilters: "attributeFilters", startDate: "startDate", endDate: "endDate", filters: "filters", ouMode: "ouMode", dispatchTeis: "dispatchTeis", isButtonLoading: "isButtonLoading", buttonLabel: "buttonLabel", buttonFilter: "buttonFilter", filterRootOrgUnit: "filterRootOrgUnit", showFilters: "showFilters" }, outputs: { actionSelected: "actionSelected", approvalSelected: "approvalSelected", firstValue: "firstValue" }, usesInheritance: true, usesOnChanges: true, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, styles: ["::ng-deep .custom-input{display:flex!important;gap:4px!important;align-items:center!important;border:1px solid #9FADBA!important;border-radius:4px!important;padding-left:16px}::ng-deep .custom-input input{border:none!important;outline:none!important;box-shadow:none!important}::ng-deep .custom-input input:focus{border:none!important;box-shadow:none!important}::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}\n"] }); }
|
|
3805
4075
|
}
|
|
3806
4076
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.8", ngImport: i0, type: LineListTableComponent, decorators: [{
|
|
3807
4077
|
type: Component,
|
|
3808
|
-
args: [{ selector: '
|
|
4078
|
+
args: [{ selector: 'ng-dhis2-ui-line-list', template: '<ng-content></ng-content>', standalone: false, styles: ["::ng-deep .custom-input{display:flex!important;gap:4px!important;align-items:center!important;border:1px solid #9FADBA!important;border-radius:4px!important;padding-left:16px}::ng-deep .custom-input input{border:none!important;outline:none!important;box-shadow:none!important}::ng-deep .custom-input input:focus{border:none!important;box-shadow:none!important}::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}\n"] }]
|
|
3809
4079
|
}], propDecorators: { programId: [{
|
|
3810
4080
|
type: Input
|
|
3811
4081
|
}], orgUnit: [{
|
|
@@ -3826,8 +4096,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.8", ngImpor
|
|
|
3826
4096
|
type: Input
|
|
3827
4097
|
}], dispatchTeis: [{
|
|
3828
4098
|
type: Input
|
|
3829
|
-
}], showFilterButton: [{
|
|
3830
|
-
type: Input
|
|
3831
4099
|
}], actionSelected: [{
|
|
3832
4100
|
type: Output
|
|
3833
4101
|
}], approvalSelected: [{
|
|
@@ -3842,11 +4110,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.8", ngImpor
|
|
|
3842
4110
|
type: Input
|
|
3843
4111
|
}], filterRootOrgUnit: [{
|
|
3844
4112
|
type: Input
|
|
4113
|
+
}], showFilters: [{
|
|
4114
|
+
type: Input
|
|
3845
4115
|
}] } });
|
|
3846
4116
|
|
|
3847
4117
|
class LineListModule {
|
|
3848
4118
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.8", ngImport: i0, type: LineListModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
3849
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.1.8", ngImport: i0, type: LineListModule, declarations: [LineListTableComponent], imports: [CommonModule, SharedDhis2UiModule], exports: [LineListTableComponent] }); }
|
|
4119
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.1.8", ngImport: i0, type: LineListModule, declarations: [LineListTableComponent], imports: [CommonModule, SharedDhis2UiModule, ReactWrapperModule], exports: [LineListTableComponent] }); }
|
|
3850
4120
|
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.1.8", ngImport: i0, type: LineListModule, providers: [LineListService], imports: [CommonModule, SharedDhis2UiModule] }); }
|
|
3851
4121
|
}
|
|
3852
4122
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.8", ngImport: i0, type: LineListModule, decorators: [{
|
|
@@ -3854,7 +4124,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.8", ngImpor
|
|
|
3854
4124
|
args: [{
|
|
3855
4125
|
declarations: [LineListTableComponent],
|
|
3856
4126
|
exports: [LineListTableComponent],
|
|
3857
|
-
imports: [CommonModule, SharedDhis2UiModule],
|
|
4127
|
+
imports: [CommonModule, SharedDhis2UiModule, ReactWrapperModule],
|
|
3858
4128
|
providers: [LineListService],
|
|
3859
4129
|
}]
|
|
3860
4130
|
}] });
|
|
@@ -4065,11 +4335,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.8", ngImpor
|
|
|
4065
4335
|
// Use of this source code is governed by a BSD-style
|
|
4066
4336
|
// license that can be found in the LICENSE file.
|
|
4067
4337
|
const useFieldValidation = (props) => {
|
|
4068
|
-
const { form, touched, field, value } = props;
|
|
4338
|
+
const { form, touched, field, value, initialError, recordExistError } = props;
|
|
4069
4339
|
const hasError = useMemo(() => {
|
|
4340
|
+
if (recordExistError) {
|
|
4341
|
+
return true;
|
|
4342
|
+
}
|
|
4343
|
+
if (initialError && value) {
|
|
4344
|
+
return true;
|
|
4345
|
+
}
|
|
4070
4346
|
return touched && (form.get(field.id) || form.get(field.key))?.invalid;
|
|
4071
|
-
}, [value, touched]);
|
|
4347
|
+
}, [value, touched, initialError, recordExistError]);
|
|
4072
4348
|
const validationError = useMemo(() => {
|
|
4349
|
+
if (recordExistError) {
|
|
4350
|
+
return recordExistError;
|
|
4351
|
+
}
|
|
4352
|
+
if (initialError && value) {
|
|
4353
|
+
return initialError;
|
|
4354
|
+
}
|
|
4073
4355
|
if (!touched) {
|
|
4074
4356
|
return undefined;
|
|
4075
4357
|
}
|
|
@@ -4080,25 +4362,26 @@ const useFieldValidation = (props) => {
|
|
|
4080
4362
|
if (error['min']) {
|
|
4081
4363
|
return `${field.label || 'Value'} should not be less than ${error['min'].min}!`;
|
|
4082
4364
|
}
|
|
4083
|
-
|
|
4365
|
+
if (error['max']) {
|
|
4084
4366
|
return `${field.label || 'Value'} should not be greater than ${error['max'].max}!`;
|
|
4085
4367
|
}
|
|
4086
|
-
|
|
4368
|
+
if (error['pattern']) {
|
|
4087
4369
|
if (field.type === 'email') {
|
|
4088
4370
|
return `The email address provided is not valid`;
|
|
4089
4371
|
}
|
|
4090
|
-
|
|
4372
|
+
if (field.type === 'tel') {
|
|
4091
4373
|
return 'Phone number provided is not valid';
|
|
4092
4374
|
}
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4375
|
+
return `${field.label || 'Value'} should have appropriate format`;
|
|
4376
|
+
}
|
|
4377
|
+
if (error['customError']) {
|
|
4378
|
+
return error['customError'];
|
|
4096
4379
|
}
|
|
4097
4380
|
return error['required']
|
|
4098
4381
|
? `${field.label || 'Value'} is required`
|
|
4099
4382
|
: undefined;
|
|
4100
|
-
}, [
|
|
4101
|
-
return {
|
|
4383
|
+
}, [touched, value, initialError, recordExistError]);
|
|
4384
|
+
return { validationError, hasError };
|
|
4102
4385
|
};
|
|
4103
4386
|
|
|
4104
4387
|
// Copyright 2025 UDSM DHIS2 Lab. All rights reserved.
|
|
@@ -4134,6 +4417,7 @@ class FormField {
|
|
|
4134
4417
|
this.programStage = options.programStage;
|
|
4135
4418
|
this.program = options.program;
|
|
4136
4419
|
this.trackedEntityInstance = options.trackedEntityInstance;
|
|
4420
|
+
this.trackedEntityType = options.trackedEntityType;
|
|
4137
4421
|
this.hasOptions = options.hasOptions;
|
|
4138
4422
|
this.dependentField = options.dependentField;
|
|
4139
4423
|
this.displayInList = options.displayInList;
|
|
@@ -4319,6 +4603,8 @@ class FieldUtil {
|
|
|
4319
4603
|
return 'number';
|
|
4320
4604
|
case FieldType.PHONE_NUMBER:
|
|
4321
4605
|
return 'tel';
|
|
4606
|
+
case FieldType.EMAIL:
|
|
4607
|
+
return 'email';
|
|
4322
4608
|
case FieldType.ORG_UNIT:
|
|
4323
4609
|
return 'org-unit';
|
|
4324
4610
|
case FieldType.DATE:
|
|
@@ -4435,8 +4721,11 @@ class FormUtil {
|
|
|
4435
4721
|
const maxValidators = field.max
|
|
4436
4722
|
? [Validators.max(field.max)]
|
|
4437
4723
|
: [];
|
|
4438
|
-
const phoneNumberValidator = field.type === 'tel'
|
|
4439
|
-
|
|
4724
|
+
const phoneNumberValidator = field.type === 'tel' ? [] : [];
|
|
4725
|
+
const emailValidator = field.type === 'email'
|
|
4726
|
+
? [
|
|
4727
|
+
Validators.pattern('^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$'),
|
|
4728
|
+
]
|
|
4440
4729
|
: [];
|
|
4441
4730
|
group[field.key] = field.required
|
|
4442
4731
|
? new UntypedFormControl({ value, disabled: field.disabled }, {
|
|
@@ -4445,6 +4734,7 @@ class FormUtil {
|
|
|
4445
4734
|
...minValidators,
|
|
4446
4735
|
...maxValidators,
|
|
4447
4736
|
...phoneNumberValidator,
|
|
4737
|
+
...emailValidator,
|
|
4448
4738
|
],
|
|
4449
4739
|
updateOn: 'change',
|
|
4450
4740
|
})
|
|
@@ -4453,6 +4743,7 @@ class FormUtil {
|
|
|
4453
4743
|
...minValidators,
|
|
4454
4744
|
...maxValidators,
|
|
4455
4745
|
...phoneNumberValidator,
|
|
4746
|
+
...emailValidator,
|
|
4456
4747
|
],
|
|
4457
4748
|
});
|
|
4458
4749
|
if (field.referenceKey) {
|
|
@@ -4466,6 +4757,7 @@ class FormUtil {
|
|
|
4466
4757
|
...minValidators,
|
|
4467
4758
|
...maxValidators,
|
|
4468
4759
|
...phoneNumberValidator,
|
|
4760
|
+
...emailValidator,
|
|
4469
4761
|
],
|
|
4470
4762
|
updateOn: 'change',
|
|
4471
4763
|
})
|
|
@@ -4474,6 +4766,7 @@ class FormUtil {
|
|
|
4474
4766
|
...minValidators,
|
|
4475
4767
|
...maxValidators,
|
|
4476
4768
|
...phoneNumberValidator,
|
|
4769
|
+
...emailValidator,
|
|
4477
4770
|
],
|
|
4478
4771
|
});
|
|
4479
4772
|
}
|
|
@@ -4692,6 +4985,9 @@ class MetadataRule {
|
|
|
4692
4985
|
const regex = /(A|#|V)\{([^}]+)\}/g;
|
|
4693
4986
|
return this.rule?.condition?.replace(regex, (match, _, ruleVariable) => {
|
|
4694
4987
|
const ruleVariableObject = this.ruleVariables?.find((variable) => variable.name === ruleVariable);
|
|
4988
|
+
if (!ruleVariableObject) {
|
|
4989
|
+
return `{${ruleVariable}}`;
|
|
4990
|
+
}
|
|
4695
4991
|
const code = camelCase(ruleVariableObject?.dataElement?.code) ||
|
|
4696
4992
|
ruleVariableObject?.dataElement?.id ||
|
|
4697
4993
|
camelCase(ruleVariableObject?.trackedEntityAttribute?.code) ||
|
|
@@ -4719,6 +5015,7 @@ class FormMetadataSection {
|
|
|
4719
5015
|
sortOrder: programTrackedEntityAttribute['sortOrder'],
|
|
4720
5016
|
mandatory: programTrackedEntityAttribute['mandatory'],
|
|
4721
5017
|
allowFutureDate: programTrackedEntityAttribute['allowFutureDate'],
|
|
5018
|
+
trackedEntityType: (this.params.program['trackedEntityType'] || {})['id'],
|
|
4722
5019
|
metaType: 'ATTRIBUTE',
|
|
4723
5020
|
};
|
|
4724
5021
|
});
|
|
@@ -5331,6 +5628,7 @@ class BaseFormFieldComponent extends ReactWrapperModule {
|
|
|
5331
5628
|
this.httpClient = inject(NgxDhis2HttpClientService);
|
|
5332
5629
|
this.fieldType = 'textbox';
|
|
5333
5630
|
this.field = input.required();
|
|
5631
|
+
this.fieldError = input();
|
|
5334
5632
|
this.fieldConfig = input(new FieldConfig());
|
|
5335
5633
|
this.form = model.required();
|
|
5336
5634
|
this.isValid = input();
|
|
@@ -5338,6 +5636,7 @@ class BaseFormFieldComponent extends ReactWrapperModule {
|
|
|
5338
5636
|
this.value = model();
|
|
5339
5637
|
this.value$ = toObservable(this.value);
|
|
5340
5638
|
this.isValueAssigned$ = toObservable(this.isValueAssigned);
|
|
5639
|
+
this.fieldError$ = toObservable(this.fieldError);
|
|
5341
5640
|
this.label = computed(() => {
|
|
5342
5641
|
return !this.fieldConfig()?.hideLabel ? this.field().label : undefined;
|
|
5343
5642
|
});
|
|
@@ -5369,13 +5668,30 @@ class BaseFormFieldComponent extends ReactWrapperModule {
|
|
|
5369
5668
|
const [selected, setSelected] = useState();
|
|
5370
5669
|
const [touched, setTouched] = useState(false);
|
|
5371
5670
|
const [disabled, setDisabled] = useState(this.field()?.disabled ?? this.field()?.generated ?? false);
|
|
5671
|
+
const [initialError, setInitialError] = useState();
|
|
5672
|
+
const [recordExistError, setRecordExistError] = useState();
|
|
5673
|
+
const [checkingUniqueness, setCheckingUniqueness] = useState();
|
|
5372
5674
|
useEffect(() => {
|
|
5373
5675
|
if (this.isValueAssigned()) {
|
|
5676
|
+
const value = this.form().get(this.field().id)?.value ||
|
|
5677
|
+
this.form().get(this.field().key)?.value;
|
|
5374
5678
|
setDisabled(true);
|
|
5375
|
-
setValue(
|
|
5376
|
-
|
|
5679
|
+
setValue(value);
|
|
5680
|
+
checkValueUniqueness();
|
|
5377
5681
|
}
|
|
5378
5682
|
}, [this.isValueAssigned()]);
|
|
5683
|
+
useEffect(() => {
|
|
5684
|
+
const fieldErrorSubscription = this.fieldError$
|
|
5685
|
+
.pipe(filter((error) => error !== initialError))
|
|
5686
|
+
.subscribe({
|
|
5687
|
+
next: (error) => {
|
|
5688
|
+
setInitialError(error);
|
|
5689
|
+
},
|
|
5690
|
+
});
|
|
5691
|
+
return () => {
|
|
5692
|
+
fieldErrorSubscription.unsubscribe();
|
|
5693
|
+
};
|
|
5694
|
+
});
|
|
5379
5695
|
const onChange = (payload) => setSelected(payload.selected);
|
|
5380
5696
|
const arrayValue = useMemo(() => {
|
|
5381
5697
|
if (value && value.length > 0) {
|
|
@@ -5383,131 +5699,186 @@ class BaseFormFieldComponent extends ReactWrapperModule {
|
|
|
5383
5699
|
}
|
|
5384
5700
|
return [];
|
|
5385
5701
|
}, [value]);
|
|
5386
|
-
const {
|
|
5702
|
+
const { validationError, hasError } = useFieldValidation({
|
|
5387
5703
|
field: this.field(),
|
|
5388
5704
|
form: this.form(),
|
|
5705
|
+
initialError,
|
|
5706
|
+
recordExistError,
|
|
5389
5707
|
value,
|
|
5390
5708
|
touched,
|
|
5391
5709
|
});
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5710
|
+
const checkValueUniqueness = async () => {
|
|
5711
|
+
if (this.field().unique && this.field().trackedEntityType) {
|
|
5712
|
+
setCheckingUniqueness(true);
|
|
5713
|
+
setRecordExistError(undefined);
|
|
5714
|
+
const d2 = window.d2Web;
|
|
5715
|
+
try {
|
|
5716
|
+
const response = (await d2.trackerModule.trackedEntity
|
|
5717
|
+
.setTrackedEntityType(this.field().trackedEntityType)
|
|
5718
|
+
.setOuMode('ACCESSIBLE')
|
|
5719
|
+
.setFilters([
|
|
5720
|
+
new DataQueryFilter()
|
|
5721
|
+
.setAttribute(this.field().id)
|
|
5722
|
+
.setCondition(DataFilterCondition.Equal)
|
|
5723
|
+
.setValue(value),
|
|
5724
|
+
])
|
|
5725
|
+
.get())?.data;
|
|
5726
|
+
setCheckingUniqueness(false);
|
|
5727
|
+
if (response?.length > 0) {
|
|
5728
|
+
const customError = `Record with this ${this.label()} is already registered`;
|
|
5729
|
+
(this.form().controls[this.field().id] ||
|
|
5730
|
+
this.form().controls[this.field().key])?.setErrors({
|
|
5731
|
+
customError,
|
|
5408
5732
|
});
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5733
|
+
setRecordExistError(customError);
|
|
5734
|
+
}
|
|
5735
|
+
}
|
|
5736
|
+
catch (e) {
|
|
5737
|
+
setCheckingUniqueness(false);
|
|
5738
|
+
setRecordExistError(undefined);
|
|
5739
|
+
}
|
|
5740
|
+
}
|
|
5741
|
+
};
|
|
5742
|
+
const formFieldContent = () => {
|
|
5743
|
+
switch (this.field().controlType) {
|
|
5744
|
+
case 'textarea':
|
|
5745
|
+
return (React__default.createElement(TextAreaField, { error: hasError, validationText: validationError, inputWidth: this.fieldConfig()?.inputWidth, required: this.field().required, name: this.field().id, label: this.label(), rows: 5, placeholder: this.placeholder(), value: value, onChange: (event) => {
|
|
5746
|
+
this.ngZone.run(() => {
|
|
5747
|
+
(this.form().get(this.field().id) ||
|
|
5748
|
+
this.form().get(this.field().key))?.setValue(event.value);
|
|
5749
|
+
this.immediateUpdate.emit({
|
|
5750
|
+
form: this.form(),
|
|
5751
|
+
value: event.value,
|
|
5752
|
+
});
|
|
5418
5753
|
});
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5754
|
+
setValue(event.value);
|
|
5755
|
+
setTouched(true);
|
|
5756
|
+
}, onBlur: () => {
|
|
5757
|
+
checkValueUniqueness();
|
|
5758
|
+
this.ngZone.run(() => {
|
|
5759
|
+
this.update.emit({ form: this.form(), value });
|
|
5760
|
+
});
|
|
5761
|
+
} }));
|
|
5762
|
+
case 'org-unit':
|
|
5763
|
+
return (React__default.createElement(OrgUnitFormField, { label: this.label(), key: this.field().id, required: this.field().required, onSelectOrgUnit: (selectedOrgUnit) => {
|
|
5764
|
+
this.ngZone.run(() => {
|
|
5765
|
+
(this.form().get(this.field().id) ||
|
|
5766
|
+
this.form().get(this.field().key))?.setValue(selectedOrgUnit);
|
|
5767
|
+
this.update.emit({
|
|
5768
|
+
form: this.form(),
|
|
5769
|
+
value: selectedOrgUnit,
|
|
5770
|
+
});
|
|
5771
|
+
});
|
|
5772
|
+
setValue(selectedOrgUnit);
|
|
5773
|
+
setTouched(true);
|
|
5774
|
+
}, selected: value }));
|
|
5775
|
+
case 'transfer':
|
|
5776
|
+
return (React__default.createElement(Transfer, { filterable: true, filterPlaceholder: "Search", selected: selected, leftHeader: React__default.createElement("div", { style: {
|
|
5777
|
+
fontSize: 14,
|
|
5778
|
+
padding: '8px 4px',
|
|
5779
|
+
} }, this.field().availableOptionsLabel), rightHeader: React__default.createElement("div", { style: {
|
|
5780
|
+
fontSize: 14,
|
|
5781
|
+
padding: '8px 4px',
|
|
5782
|
+
} }, this.field().selectedOptionsLabel), options: this.field().options, onChange: (event) => {
|
|
5783
|
+
onChange({ selected: event.selected });
|
|
5784
|
+
this.ngZone.run(() => {
|
|
5785
|
+
(this.form().get(this.field().id) ||
|
|
5786
|
+
this.form().get(this.field().key))?.setValue(event.selected);
|
|
5787
|
+
});
|
|
5788
|
+
setValue(event.selected);
|
|
5789
|
+
setTouched(true);
|
|
5450
5790
|
this.update.emit({ form: this.form(), value });
|
|
5451
|
-
});
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
this.form().get(this.field().key))?.setValue(event.selected);
|
|
5458
|
-
this.update.emit({
|
|
5459
|
-
form: this.form(),
|
|
5460
|
-
value: event.selected,
|
|
5791
|
+
} }));
|
|
5792
|
+
case 'checkbox':
|
|
5793
|
+
return (React__default.createElement(Checkbox, { checked: Boolean(value), error: hasError, label: this.label(), name: this.field().id, disabled: disabled, onChange: (event) => {
|
|
5794
|
+
this.ngZone.run(() => {
|
|
5795
|
+
(this.form().get(this.field().id) ||
|
|
5796
|
+
this.form().get(this.field().key))?.setValue(event.checked);
|
|
5461
5797
|
});
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
const selectedValue = (event.selected || []).join(',');
|
|
5469
|
-
this.ngZone.run(() => {
|
|
5470
|
-
(this.form().get(this.field().id) ||
|
|
5471
|
-
this.form().get(this.field().key))?.setValue(selectedValue);
|
|
5472
|
-
this.update.emit({
|
|
5473
|
-
form: this.form(),
|
|
5474
|
-
value: selectedValue,
|
|
5798
|
+
setValue(event.checked);
|
|
5799
|
+
setTouched(true);
|
|
5800
|
+
}, onBlur: () => {
|
|
5801
|
+
checkValueUniqueness();
|
|
5802
|
+
this.ngZone.run(() => {
|
|
5803
|
+
this.update.emit({ form: this.form(), value });
|
|
5475
5804
|
});
|
|
5476
|
-
});
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
}, selected: arrayValue }, (this.field().options || []).map((option) => (React__default.createElement(MultiSelectOption, { key: option.key, label: option.label, value: option.value })))));
|
|
5480
|
-
case 'file':
|
|
5481
|
-
return (React__default.createElement(React__default.Fragment, null,
|
|
5482
|
-
React__default.createElement(FileUploadField, { label: this.label(), id: this.field().id, hasError: hasError, required: this.field().required, validationText: validationError, performUpload: true, uploadUrl: 'fileResources', onUploadSuccess: (fileId) => {
|
|
5805
|
+
} }));
|
|
5806
|
+
case 'dropdown':
|
|
5807
|
+
return (React__default.createElement(SingleSelectField, { filterable: (this.field().options || []).length > 5, clearable: true, error: hasError, validationText: validationError, inputWidth: this.fieldConfig()?.inputWidth, disabled: disabled, required: this.field().required, className: "select", label: this.label(), selected: value, onChange: (event) => {
|
|
5483
5808
|
this.ngZone.run(() => {
|
|
5484
|
-
this.form().get(this.field().id) ||
|
|
5485
|
-
this.form().get(this.field().key)?.setValue(
|
|
5809
|
+
(this.form().get(this.field().id) ||
|
|
5810
|
+
this.form().get(this.field().key))?.setValue(event.selected);
|
|
5486
5811
|
this.update.emit({
|
|
5487
5812
|
form: this.form(),
|
|
5488
|
-
value:
|
|
5813
|
+
value: event.selected,
|
|
5489
5814
|
});
|
|
5490
5815
|
});
|
|
5491
|
-
setValue(
|
|
5816
|
+
setValue(event.selected);
|
|
5492
5817
|
setTouched(true);
|
|
5493
|
-
}
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
this.
|
|
5500
|
-
|
|
5501
|
-
|
|
5818
|
+
}, onBlur: () => {
|
|
5819
|
+
checkValueUniqueness();
|
|
5820
|
+
} }, (this.field().options || []).map((option) => (React__default.createElement(SingleSelectOption, { key: crypto.randomUUID() || option.key, label: option.label, value: option.value })))));
|
|
5821
|
+
case 'multi-dropdown':
|
|
5822
|
+
return (React__default.createElement(MultiSelectField, { clearText: "Clear", clearable: true, empty: "No data found", filterable: (this.field().options || []).length > 5, filterPlaceholder: "Type to filter options", error: hasError, validationText: validationError, inputWidth: this.fieldConfig()?.inputWidth, disabled: disabled, label: this.label(), required: this.field().required, loadingText: "Loading options", noMatchText: "No options found", onChange: (event) => {
|
|
5823
|
+
const selectedValue = (event.selected || []).join(',');
|
|
5824
|
+
this.ngZone.run(() => {
|
|
5825
|
+
(this.form().get(this.field().id) ||
|
|
5826
|
+
this.form().get(this.field().key))?.setValue(selectedValue);
|
|
5827
|
+
this.update.emit({
|
|
5828
|
+
form: this.form(),
|
|
5829
|
+
value: selectedValue,
|
|
5830
|
+
});
|
|
5502
5831
|
});
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
this.
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5832
|
+
setValue(selectedValue);
|
|
5833
|
+
setTouched(true);
|
|
5834
|
+
}, onBlur: () => {
|
|
5835
|
+
checkValueUniqueness();
|
|
5836
|
+
}, selected: arrayValue }, (this.field().options || []).map((option) => (React__default.createElement(MultiSelectOption, { key: option.key, label: option.label, value: option.value })))));
|
|
5837
|
+
case 'file':
|
|
5838
|
+
return (React__default.createElement(React__default.Fragment, null,
|
|
5839
|
+
React__default.createElement(FileUploadField, { label: this.label(), id: this.field().id, hasError: hasError, required: this.field().required, validationText: validationError, performUpload: true, uploadUrl: 'fileResources', onUploadSuccess: (fileId) => {
|
|
5840
|
+
this.ngZone.run(() => {
|
|
5841
|
+
this.form().get(this.field().id) ||
|
|
5842
|
+
this.form().get(this.field().key)?.setValue(fileId);
|
|
5843
|
+
this.update.emit({
|
|
5844
|
+
form: this.form(),
|
|
5845
|
+
value: fileId,
|
|
5846
|
+
});
|
|
5847
|
+
});
|
|
5848
|
+
setValue(fileId);
|
|
5849
|
+
setTouched(true);
|
|
5850
|
+
} })));
|
|
5851
|
+
default:
|
|
5852
|
+
return (React__default.createElement(InputField$1, { error: hasError, validationText: validationError, type: this.field().type, inputWidth: this.fieldConfig()?.inputWidth, required: this.field().required, name: this.field().id, label: this.label(), min: this.field().min?.toString(), max: this.field().max?.toString(), placeholder: this.placeholder(), value: value, readOnly: disabled, onChange: (event) => {
|
|
5853
|
+
this.ngZone.run(() => {
|
|
5854
|
+
(this.form().get(this.field().id) ||
|
|
5855
|
+
this.form().get(this.field().key))?.setValue(event.value);
|
|
5856
|
+
this.immediateUpdate.emit({
|
|
5857
|
+
form: this.form(),
|
|
5858
|
+
value: event.value,
|
|
5859
|
+
});
|
|
5860
|
+
});
|
|
5861
|
+
setValue(event.value);
|
|
5862
|
+
setTouched(true);
|
|
5863
|
+
}, onBlur: () => {
|
|
5864
|
+
checkValueUniqueness();
|
|
5865
|
+
this.ngZone.run(() => {
|
|
5866
|
+
this.update.emit({ form: this.form(), value });
|
|
5867
|
+
});
|
|
5868
|
+
} }));
|
|
5869
|
+
}
|
|
5870
|
+
};
|
|
5871
|
+
{
|
|
5872
|
+
return (React__default.createElement(React__default.Fragment, null,
|
|
5873
|
+
formFieldContent(),
|
|
5874
|
+
checkingUniqueness && (React__default.createElement("div", { style: {
|
|
5875
|
+
display: 'flex',
|
|
5876
|
+
alignItems: 'center',
|
|
5877
|
+
gap: 8,
|
|
5878
|
+
paddingTop: 8,
|
|
5879
|
+
} },
|
|
5880
|
+
React__default.createElement(CircularLoader$1, { small: true }),
|
|
5881
|
+
React__default.createElement("div", { style: { color: colors$1.grey800, fontSize: 14 } }, "Checking...")))));
|
|
5511
5882
|
}
|
|
5512
5883
|
};
|
|
5513
5884
|
}
|
|
@@ -5515,7 +5886,7 @@ class BaseFormFieldComponent extends ReactWrapperModule {
|
|
|
5515
5886
|
this.value.set(event);
|
|
5516
5887
|
}
|
|
5517
5888
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.8", ngImport: i0, type: BaseFormFieldComponent, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
|
|
5518
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.1.8", type: BaseFormFieldComponent, isStandalone: true, inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, fieldConfig: { classPropertyName: "fieldConfig", publicName: "fieldConfig", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: true, transformFunction: null }, isValid: { classPropertyName: "isValid", publicName: "isValid", isSignal: true, isRequired: false, transformFunction: null }, isValueAssigned: { classPropertyName: "isValueAssigned", publicName: "isValueAssigned", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { form: "formChange", value: "valueChange", update: "update", immediateUpdate: "immediateUpdate" }, usesInheritance: true, usesOnChanges: true, ngImport: i0 }); }
|
|
5889
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.1.8", type: BaseFormFieldComponent, isStandalone: true, inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, fieldError: { classPropertyName: "fieldError", publicName: "fieldError", isSignal: true, isRequired: false, transformFunction: null }, fieldConfig: { classPropertyName: "fieldConfig", publicName: "fieldConfig", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: true, transformFunction: null }, isValid: { classPropertyName: "isValid", publicName: "isValid", isSignal: true, isRequired: false, transformFunction: null }, isValueAssigned: { classPropertyName: "isValueAssigned", publicName: "isValueAssigned", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { form: "formChange", value: "valueChange", update: "update", immediateUpdate: "immediateUpdate" }, usesInheritance: true, usesOnChanges: true, ngImport: i0 }); }
|
|
5519
5890
|
}
|
|
5520
5891
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.8", ngImport: i0, type: BaseFormFieldComponent, decorators: [{
|
|
5521
5892
|
type: Directive
|
|
@@ -5548,6 +5919,7 @@ class FormFieldComponent {
|
|
|
5548
5919
|
this.immediateFieldUpdate = new EventEmitter();
|
|
5549
5920
|
this.runtimeOptionChange = new EventEmitter();
|
|
5550
5921
|
this.value = signal('');
|
|
5922
|
+
this.runtimeError = signal(undefined);
|
|
5551
5923
|
this._runtimeOptions = [];
|
|
5552
5924
|
this.runtimeOptions = [];
|
|
5553
5925
|
this.fieldFilterControl = new FormControl();
|
|
@@ -5581,9 +5953,10 @@ class FormFieldComponent {
|
|
|
5581
5953
|
});
|
|
5582
5954
|
}
|
|
5583
5955
|
#processRules(ruleActions) {
|
|
5584
|
-
ruleActions
|
|
5585
|
-
|
|
5586
|
-
|
|
5956
|
+
const fieldRuleActions = ruleActions.filter((ruleAction) => ruleAction.field === this.field().id);
|
|
5957
|
+
let runtimeError;
|
|
5958
|
+
let isValueAssigned = false;
|
|
5959
|
+
fieldRuleActions.forEach((ruleAction) => {
|
|
5587
5960
|
switch (ruleAction.actionType) {
|
|
5588
5961
|
case 'ASSIGN': {
|
|
5589
5962
|
if (ruleAction.assignedData) {
|
|
@@ -5592,16 +5965,26 @@ class FormFieldComponent {
|
|
|
5592
5965
|
this.form
|
|
5593
5966
|
?.get(this.field().key)
|
|
5594
5967
|
?.setValue(ruleAction.assignedData);
|
|
5595
|
-
|
|
5968
|
+
isValueAssigned = true;
|
|
5596
5969
|
this.onValueChange();
|
|
5597
5970
|
}
|
|
5598
5971
|
}
|
|
5599
5972
|
break;
|
|
5600
5973
|
}
|
|
5974
|
+
case 'SHOWERROR': {
|
|
5975
|
+
runtimeError = ruleAction.displayedContent;
|
|
5976
|
+
(this.form.controls[this.field().id] ||
|
|
5977
|
+
this.form.controls[this.field().key])?.setErrors({
|
|
5978
|
+
customError: runtimeError,
|
|
5979
|
+
});
|
|
5980
|
+
break;
|
|
5981
|
+
}
|
|
5601
5982
|
default:
|
|
5602
5983
|
break;
|
|
5603
5984
|
}
|
|
5604
5985
|
});
|
|
5986
|
+
this.runtimeError.set(runtimeError);
|
|
5987
|
+
this.isValueAssigned.set(isValueAssigned);
|
|
5605
5988
|
}
|
|
5606
5989
|
ngOnDestroy() {
|
|
5607
5990
|
this._onDestroy.next();
|
|
@@ -5709,11 +6092,11 @@ class FormFieldComponent {
|
|
|
5709
6092
|
this._runtimeOptions = [...options];
|
|
5710
6093
|
}
|
|
5711
6094
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.8", ngImport: i0, type: FormFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
5712
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.1.8", type: FormFieldComponent, isStandalone: false, selector: "ng-dhis2-ui-form-field", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, fieldConfig: { classPropertyName: "fieldConfig", publicName: "fieldConfig", isSignal: false, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: false, isRequired: false, transformFunction: null }, isCheckBoxButton: { classPropertyName: "isCheckBoxButton", publicName: "isCheckBoxButton", isSignal: false, isRequired: false, transformFunction: null }, fieldClass: { classPropertyName: "fieldClass", publicName: "fieldClass", isSignal: false, isRequired: false, transformFunction: null }, programRuleActions: { classPropertyName: "programRuleActions", publicName: "programRuleActions", isSignal: true, isRequired: false, transformFunction: null }, dataEntities: { classPropertyName: "dataEntities", publicName: "dataEntities", isSignal: false, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: false, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { fieldUpdate: "fieldUpdate", immediateFieldUpdate: "immediateFieldUpdate", runtimeOptionChange: "runtimeOptionChange" }, usesOnChanges: true, ngImport: i0, template: "<div *ngIf=\"field()\" class=\"form-field-item\">\n <ng-dhis2-ui-form-field-item\n [field]=\"field()\"\n [isValueAssigned]=\"isValueAssigned()\"\n [fieldConfig]=\"fieldConfig\"\n [(form)]=\"form\"\n [value]=\"value()\"\n (update)=\"onValueChange()\"\n (immediateUpdate)=\"onImmediateChange($event)\"\n />\n</div>\n", styles: [".form-field-item{margin-bottom:16px}\n"], dependencies: [{ kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: FormFieldItemComponent, selector: "ng-dhis2-ui-form-field-item" }] }); }
|
|
6095
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.1.8", type: FormFieldComponent, isStandalone: false, selector: "ng-dhis2-ui-form-field", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, fieldConfig: { classPropertyName: "fieldConfig", publicName: "fieldConfig", isSignal: false, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: false, isRequired: false, transformFunction: null }, isCheckBoxButton: { classPropertyName: "isCheckBoxButton", publicName: "isCheckBoxButton", isSignal: false, isRequired: false, transformFunction: null }, fieldClass: { classPropertyName: "fieldClass", publicName: "fieldClass", isSignal: false, isRequired: false, transformFunction: null }, programRuleActions: { classPropertyName: "programRuleActions", publicName: "programRuleActions", isSignal: true, isRequired: false, transformFunction: null }, dataEntities: { classPropertyName: "dataEntities", publicName: "dataEntities", isSignal: false, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: false, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { fieldUpdate: "fieldUpdate", immediateFieldUpdate: "immediateFieldUpdate", runtimeOptionChange: "runtimeOptionChange" }, usesOnChanges: true, ngImport: i0, template: "<div *ngIf=\"field()\" class=\"form-field-item\">\n <ng-dhis2-ui-form-field-item\n [field]=\"field()\"\n [fieldError]=\"runtimeError()\"\n [isValueAssigned]=\"isValueAssigned()\"\n [fieldConfig]=\"fieldConfig\"\n [(form)]=\"form\"\n [value]=\"value()\"\n (update)=\"onValueChange()\"\n (immediateUpdate)=\"onImmediateChange($event)\"\n />\n</div>\n", styles: [".form-field-item{margin-bottom:16px}\n"], dependencies: [{ kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: FormFieldItemComponent, selector: "ng-dhis2-ui-form-field-item" }] }); }
|
|
5713
6096
|
}
|
|
5714
6097
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.8", ngImport: i0, type: FormFieldComponent, decorators: [{
|
|
5715
6098
|
type: Component,
|
|
5716
|
-
args: [{ selector: 'ng-dhis2-ui-form-field', standalone: false, template: "<div *ngIf=\"field()\" class=\"form-field-item\">\n <ng-dhis2-ui-form-field-item\n [field]=\"field()\"\n [isValueAssigned]=\"isValueAssigned()\"\n [fieldConfig]=\"fieldConfig\"\n [(form)]=\"form\"\n [value]=\"value()\"\n (update)=\"onValueChange()\"\n (immediateUpdate)=\"onImmediateChange($event)\"\n />\n</div>\n", styles: [".form-field-item{margin-bottom:16px}\n"] }]
|
|
6099
|
+
args: [{ selector: 'ng-dhis2-ui-form-field', standalone: false, template: "<div *ngIf=\"field()\" class=\"form-field-item\">\n <ng-dhis2-ui-form-field-item\n [field]=\"field()\"\n [fieldError]=\"runtimeError()\"\n [isValueAssigned]=\"isValueAssigned()\"\n [fieldConfig]=\"fieldConfig\"\n [(form)]=\"form\"\n [value]=\"value()\"\n (update)=\"onValueChange()\"\n (immediateUpdate)=\"onImmediateChange($event)\"\n />\n</div>\n", styles: [".form-field-item{margin-bottom:16px}\n"] }]
|
|
5717
6100
|
}], ctorParameters: () => [], propDecorators: { fieldConfig: [{
|
|
5718
6101
|
type: Input
|
|
5719
6102
|
}], form: [{
|
|
@@ -6647,6 +7030,7 @@ class ProgramEntryFormFieldUtil {
|
|
|
6647
7030
|
sortOrder: programTrackedEntityAttribute.sortOrder,
|
|
6648
7031
|
mandatory: programTrackedEntityAttribute.mandatory,
|
|
6649
7032
|
allowFutureDate: programTrackedEntityAttribute.allowFutureDate,
|
|
7033
|
+
trackedEntityType: this.program.trackedEntityType?.id,
|
|
6650
7034
|
metaType: 'ATTRIBUTE',
|
|
6651
7035
|
stepId: null,
|
|
6652
7036
|
};
|
|
@@ -6729,7 +7113,7 @@ class ProgramEntryFormFieldUtil {
|
|
|
6729
7113
|
label: field.formName || field.name,
|
|
6730
7114
|
key: field.code ? camelCase(field.code) : field.id,
|
|
6731
7115
|
required: field.mandatory,
|
|
6732
|
-
type: field.valueType,
|
|
7116
|
+
type: FieldUtil.getFieldType(field.valueType),
|
|
6733
7117
|
options,
|
|
6734
7118
|
disabled: this.#getDisabledStatus(field),
|
|
6735
7119
|
order: field.sortOrder,
|
|
@@ -7000,7 +7384,7 @@ class ProgramEntryFormModule {
|
|
|
7000
7384
|
const instance = await this.#getInstance();
|
|
7001
7385
|
this.instance.set(instance);
|
|
7002
7386
|
if (this.config().autoAssignedValues) {
|
|
7003
|
-
this.#updateInstanceWithAutoAssignedValues(this.config().autoAssignedValues);
|
|
7387
|
+
this.#updateInstanceWithAutoAssignedValues(this.config().autoAssignedValues || []);
|
|
7004
7388
|
}
|
|
7005
7389
|
this.loading.set(false);
|
|
7006
7390
|
}
|