@iapps/ng-dhis2-ui 1.0.8 → 1.1.0
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 +512 -8
- package/fesm2022/iapps-ng-dhis2-ui.mjs.map +1 -1
- package/index.d.ts +1 -0
- package/lib/modules/line-list/components/dropdown-menu/dropdown-menu.d.ts +19 -0
- package/lib/modules/line-list/components/dropdown-menu/dropdown-menu.styles.d.ts +5 -0
- package/lib/modules/line-list/components/dropdown-menu/index.d.ts +1 -0
- package/lib/modules/line-list/containers/line-list.component.d.ts +31 -0
- package/lib/modules/line-list/hooks/index.d.ts +1 -0
- package/lib/modules/line-list/hooks/use-dynamic-styles.hook.d.ts +1 -0
- package/lib/modules/line-list/index.d.ts +2 -0
- package/lib/modules/line-list/line-list.module.d.ts +9 -0
- package/lib/modules/line-list/models/attribute-filter.model.d.ts +10 -0
- package/lib/modules/line-list/models/filter-config.model.d.ts +6 -0
- package/lib/modules/line-list/models/line-list.models.d.ts +129 -0
- package/lib/modules/line-list/services/line-list.service.d.ts +16 -0
- package/lib/modules/line-list/utils/filter-builder.d.ts +5 -0
- package/lib/modules/line-list/utils/line-list-utils.d.ts +15 -0
- package/lib/modules/react-wrapper/react-wrapper.component.d.ts +2 -3
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CommonModule } from '@angular/common';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { NgModule, Directive, Input, ContentChild, Component, EventEmitter, Output } from '@angular/core';
|
|
4
|
-
import { IconEdit16, spacers, colors, theme, Tooltip, Button as Button$1, IconDimensionData16, IconClock16, IconDimensionOrgUnit16, 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 } from '@dhis2/ui';
|
|
3
|
+
import { NgModule, Directive, Input, ContentChild, Component, EventEmitter, Output, Injectable, inject } from '@angular/core';
|
|
4
|
+
import { IconEdit16, spacers, colors, theme, Tooltip, Button as Button$1, IconDimensionData16, IconClock16, IconDimensionOrgUnit16, 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, DataTable, TableHead, DataTableRow, DataTableColumnHeader, TableBody, DataTableCell, TableFoot, Pagination } from '@dhis2/ui';
|
|
5
5
|
import cx from 'classnames';
|
|
6
6
|
import PropTypes from 'prop-types';
|
|
7
7
|
import * as React from 'react';
|
|
@@ -27,6 +27,7 @@ import { MenuItem, MenuDivider } from '@dhis2-ui/menu';
|
|
|
27
27
|
import parse from 'html-react-parser';
|
|
28
28
|
import { getNowInCalendar, generateFixedPeriods } from '@dhis2/multi-calendar-dates';
|
|
29
29
|
import { isObject, isString } from 'lodash';
|
|
30
|
+
import { map as map$1, switchMap } from 'rxjs/operators';
|
|
30
31
|
|
|
31
32
|
const TransferOption = ({ disabled, label, highlighted, selected, onClick, onDoubleClick, value, icon, active, tooltipText, dataTest, onEditClick, }) => {
|
|
32
33
|
const renderContent = () => (React__default.createElement("div", { className: cx('chip', {
|
|
@@ -1150,9 +1151,6 @@ class ReactWrapperComponent {
|
|
|
1150
1151
|
this.reactDomRoot = ReactDOM.createRoot(this.elementRef.nativeElement);
|
|
1151
1152
|
this.render();
|
|
1152
1153
|
}
|
|
1153
|
-
ngOnChanges() {
|
|
1154
|
-
// if (this.elementRef) this.render();
|
|
1155
|
-
}
|
|
1156
1154
|
ngOnDestroy() {
|
|
1157
1155
|
this.reactDomRoot?.unmount();
|
|
1158
1156
|
}
|
|
@@ -1173,13 +1171,13 @@ class ReactWrapperComponent {
|
|
|
1173
1171
|
}
|
|
1174
1172
|
}
|
|
1175
1173
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: ReactWrapperComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1176
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.0.7", type: ReactWrapperComponent, isStandalone: false, selector: "ng-dhis2-ui-wrapper", inputs: { props: "props", component: "component" }, queries: [{ propertyName: "content", first: true, predicate: ["ngDhis2UiContent"], descendants: true }],
|
|
1174
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.0.7", type: ReactWrapperComponent, isStandalone: false, selector: "ng-dhis2-ui-wrapper", inputs: { props: "props", component: "component" }, queries: [{ propertyName: "content", first: true, predicate: ["ngDhis2UiContent"], descendants: true }], ngImport: i0 }); }
|
|
1177
1175
|
}
|
|
1178
1176
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: ReactWrapperComponent, decorators: [{
|
|
1179
1177
|
type: Directive,
|
|
1180
1178
|
args: [{
|
|
1181
1179
|
selector: 'ng-dhis2-ui-wrapper',
|
|
1182
|
-
standalone: false
|
|
1180
|
+
standalone: false,
|
|
1183
1181
|
}]
|
|
1184
1182
|
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { props: [{
|
|
1185
1183
|
type: Input
|
|
@@ -2963,9 +2961,515 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.7", ngImpor
|
|
|
2963
2961
|
// Use of this source code is governed by a BSD-style
|
|
2964
2962
|
// license that can be found in the LICENSE file.
|
|
2965
2963
|
|
|
2964
|
+
// export function buildFilters(filters: AttributeFilter[]): string {
|
|
2965
|
+
// return filters
|
|
2966
|
+
// .map(({ attribute, operator, value }) => `filter=${attribute}:${operator}:${value}`)
|
|
2967
|
+
// .join('&');
|
|
2968
|
+
// }
|
|
2969
|
+
function buildFilters(filters) {
|
|
2970
|
+
return filters
|
|
2971
|
+
.map(({ attribute, dataElement, operator, value }) => {
|
|
2972
|
+
// Check if attribute is defined, otherwise use dataElement
|
|
2973
|
+
const field = attribute || dataElement;
|
|
2974
|
+
return field ? `filter=${field}:${operator}:${value}` : '';
|
|
2975
|
+
})
|
|
2976
|
+
.filter(Boolean) // Remove any empty strings
|
|
2977
|
+
.join('&');
|
|
2978
|
+
}
|
|
2979
|
+
const matchesCondition = (eventValue, operator, filterValue) => {
|
|
2980
|
+
if (typeof eventValue === 'number' && typeof filterValue === 'number') {
|
|
2981
|
+
switch (operator) {
|
|
2982
|
+
case '=':
|
|
2983
|
+
return eventValue === filterValue;
|
|
2984
|
+
case '!=':
|
|
2985
|
+
return eventValue !== filterValue;
|
|
2986
|
+
case '>':
|
|
2987
|
+
return eventValue > filterValue;
|
|
2988
|
+
case '<':
|
|
2989
|
+
return eventValue < filterValue;
|
|
2990
|
+
case '>=':
|
|
2991
|
+
return eventValue >= filterValue;
|
|
2992
|
+
case '<=':
|
|
2993
|
+
return eventValue <= filterValue;
|
|
2994
|
+
default:
|
|
2995
|
+
return false;
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
const strEventValue = String(eventValue);
|
|
2999
|
+
const strFilterValue = String(filterValue);
|
|
3000
|
+
switch (operator) {
|
|
3001
|
+
case '=':
|
|
3002
|
+
return strEventValue === strFilterValue;
|
|
3003
|
+
case '!=':
|
|
3004
|
+
return strEventValue !== strFilterValue;
|
|
3005
|
+
case 'contains':
|
|
3006
|
+
return strEventValue.includes(strFilterValue);
|
|
3007
|
+
case 'startsWith':
|
|
3008
|
+
return strEventValue.startsWith(strFilterValue);
|
|
3009
|
+
case 'endsWith':
|
|
3010
|
+
return strEventValue.endsWith(strFilterValue);
|
|
3011
|
+
case 'regex':
|
|
3012
|
+
return new RegExp(strFilterValue).test(strEventValue);
|
|
3013
|
+
default:
|
|
3014
|
+
return false;
|
|
3015
|
+
}
|
|
3016
|
+
};
|
|
3017
|
+
const getFilteredTrackedEntityInstances = (trackedEntityInstance, filters) => {
|
|
3018
|
+
return trackedEntityInstance.filter((trackedEntityInstance) => trackedEntityInstance.enrollments.some((enrollment) => enrollment.events.some((event) => filters.every((filter) => event.programStage === filter.programStage &&
|
|
3019
|
+
event.dataValues.some((dataValue) => dataValue.dataElement === filter.dataElement &&
|
|
3020
|
+
matchesCondition(dataValue.value, filter.operator, filter.value))))));
|
|
3021
|
+
};
|
|
3022
|
+
|
|
3023
|
+
class LineListService {
|
|
3024
|
+
constructor(httpClient) {
|
|
3025
|
+
this.httpClient = httpClient;
|
|
3026
|
+
}
|
|
3027
|
+
getProgramMetadata(programId) {
|
|
3028
|
+
return this.httpClient.get(`programs/${programId}.json?fields=programType,programStages[id,name,programStageDataElements[dataElement[id,name]]],organisationUnits[id,name]`);
|
|
3029
|
+
}
|
|
3030
|
+
getTrackedEntityInstances(programId, orgUnit, page, pageSize, filters = [], startDate, endDate) {
|
|
3031
|
+
const filterParams = buildFilters(filters);
|
|
3032
|
+
const dateFilter = startDate && endDate ? `&programStartDate=${startDate}&programEndDate=${endDate}` : '';
|
|
3033
|
+
return this.httpClient.get(`trackedEntityInstances.json?program=${programId}&ou=${orgUnit}&page=${page}&pageSize=${pageSize}&fields=trackedEntityInstance,attributes[*],enrollments[*]&totalPages=true&${filterParams}${dateFilter}`).pipe(map$1((response) => {
|
|
3034
|
+
response.trackedEntityInstances.forEach((tei) => {
|
|
3035
|
+
// Filter attributes to only include those with displayInList=true
|
|
3036
|
+
tei.attributes = tei.attributes.filter((attr) => attr.displayInList);
|
|
3037
|
+
});
|
|
3038
|
+
return response;
|
|
3039
|
+
}));
|
|
3040
|
+
}
|
|
3041
|
+
getEvents(programId, orgUnit, page, pageSize, filters = [], startDate, endDate) {
|
|
3042
|
+
const filterParams = buildFilters(filters);
|
|
3043
|
+
const dateFilter = startDate && endDate ? `&startDate=${startDate}&endDate=${endDate}` : '';
|
|
3044
|
+
return this.httpClient.get(`events.json?program=${programId}&orgUnit=${orgUnit}&page=${page}&pageSize=${pageSize}&fields=*&totalPages=true&${filterParams}${dateFilter}`);
|
|
3045
|
+
}
|
|
3046
|
+
getEventsByProgramStage(programStageId, orgUnit, page, pageSize, filters = [], startDate, endDate) {
|
|
3047
|
+
const filterParams = buildFilters(filters);
|
|
3048
|
+
const dateFilter = startDate && endDate ? `&startDate=${startDate}&endDate=${endDate}` : '';
|
|
3049
|
+
return this.httpClient.get(`events.json?programStage=${programStageId}&orgUnit=${orgUnit}&page=${page}&pageSize=${pageSize}&fields=*&totalPages=true&${filterParams}${dateFilter}`);
|
|
3050
|
+
}
|
|
3051
|
+
getLineListData(programId, orgUnit = '', programStageId, page = 1, pageSize = 10, filters = [], startDate, endDate) {
|
|
3052
|
+
return this.getProgramMetadata(programId).pipe(switchMap((programMetadata) => {
|
|
3053
|
+
if (programStageId) {
|
|
3054
|
+
return this.getEventsByProgramStage(programStageId, orgUnit, page, pageSize, filters, startDate, endDate).pipe(map$1((events) => ({
|
|
3055
|
+
metadata: programMetadata,
|
|
3056
|
+
data: events,
|
|
3057
|
+
})));
|
|
3058
|
+
}
|
|
3059
|
+
if (programMetadata.programType === 'WITH_REGISTRATION') {
|
|
3060
|
+
return this.getTrackedEntityInstances(programId, orgUnit, page, pageSize, filters, startDate, endDate).pipe(map$1((teis) => ({
|
|
3061
|
+
metadata: programMetadata,
|
|
3062
|
+
data: teis,
|
|
3063
|
+
})));
|
|
3064
|
+
}
|
|
3065
|
+
else {
|
|
3066
|
+
return this.getEvents(programId, orgUnit, page, pageSize, filters, startDate, endDate).pipe(map$1((events) => ({
|
|
3067
|
+
metadata: programMetadata,
|
|
3068
|
+
data: events,
|
|
3069
|
+
})));
|
|
3070
|
+
}
|
|
3071
|
+
}));
|
|
3072
|
+
}
|
|
3073
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: LineListService, deps: [{ token: i1.NgxDhis2HttpClientService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3074
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: LineListService }); }
|
|
3075
|
+
}
|
|
3076
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: LineListService, decorators: [{
|
|
3077
|
+
type: Injectable
|
|
3078
|
+
}], ctorParameters: () => [{ type: i1.NgxDhis2HttpClientService }] });
|
|
3079
|
+
|
|
3080
|
+
const dropdownStyles = {
|
|
3081
|
+
layer: {
|
|
3082
|
+
zIndex: '1 !important',
|
|
3083
|
+
},
|
|
3084
|
+
};
|
|
3085
|
+
|
|
3086
|
+
// Helper function to generate a unique class name
|
|
3087
|
+
const generateClassName = (base) => `${base}-${Math.random().toString(36).substr(2, 9)}`;
|
|
3088
|
+
// Helper function to convert camelCased properties to hyphenated CSS properties
|
|
3089
|
+
const convertStylesToCSS = (className, styles) => {
|
|
3090
|
+
let cssString = `.${className} {`;
|
|
3091
|
+
for (const key in styles) {
|
|
3092
|
+
if (typeof styles[key] === "object" && key.startsWith("@media")) {
|
|
3093
|
+
const mediaStyles = convertStylesToCSS(className, styles[key]);
|
|
3094
|
+
cssString += `${key} { ${mediaStyles} }`;
|
|
3095
|
+
}
|
|
3096
|
+
else if (key.startsWith("&:")) {
|
|
3097
|
+
const pseudoClass = key.replace("&", "");
|
|
3098
|
+
cssString += `${pseudoClass} { ${convertStylesToCSS("", styles[key])} }`;
|
|
3099
|
+
}
|
|
3100
|
+
else {
|
|
3101
|
+
const cssKey = key.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
|
|
3102
|
+
cssString += `${cssKey}: ${styles[key]}; `;
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
cssString += "}";
|
|
3106
|
+
return cssString;
|
|
3107
|
+
};
|
|
3108
|
+
// Custom hook for dynamic styles with generics
|
|
3109
|
+
const useDynamicStyles = (stylesObj) => {
|
|
3110
|
+
const [classNames, setClassNames] = useState({});
|
|
3111
|
+
useEffect(() => {
|
|
3112
|
+
const convertedStyles = Object.keys(stylesObj).map((key) => {
|
|
3113
|
+
const className = generateClassName(key);
|
|
3114
|
+
const styleElement = document.createElement("style");
|
|
3115
|
+
styleElement.innerHTML = convertStylesToCSS(className, stylesObj[key]);
|
|
3116
|
+
document.head.appendChild(styleElement);
|
|
3117
|
+
return {
|
|
3118
|
+
key,
|
|
3119
|
+
className,
|
|
3120
|
+
styleElement,
|
|
3121
|
+
};
|
|
3122
|
+
});
|
|
3123
|
+
setClassNames(convertedStyles.reduce((classNameEntity, convertedStyle) => {
|
|
3124
|
+
return {
|
|
3125
|
+
...classNameEntity,
|
|
3126
|
+
[convertedStyle.key]: convertedStyle.className,
|
|
3127
|
+
};
|
|
3128
|
+
}, {}));
|
|
3129
|
+
return () => {
|
|
3130
|
+
convertedStyles.forEach((convertedStyle) => {
|
|
3131
|
+
document.head.removeChild(convertedStyle.styleElement);
|
|
3132
|
+
});
|
|
3133
|
+
};
|
|
3134
|
+
}, []);
|
|
3135
|
+
return classNames;
|
|
3136
|
+
};
|
|
3137
|
+
|
|
3138
|
+
const useCurrentModal = () => {
|
|
3139
|
+
const [CurrentModal, setCurrentModal] = useState();
|
|
3140
|
+
return [
|
|
3141
|
+
CurrentModal,
|
|
3142
|
+
(Modal) => {
|
|
3143
|
+
// As setState supports functional updates, we can't pass functional
|
|
3144
|
+
// components directly
|
|
3145
|
+
setCurrentModal(() => Modal);
|
|
3146
|
+
},
|
|
3147
|
+
];
|
|
3148
|
+
};
|
|
3149
|
+
const ContextMenu = (params) => {
|
|
3150
|
+
const { onClose, onMenuClick, anchorRef, dropDownOptions } = params;
|
|
3151
|
+
const [CurrentModal, setCurrentModal] = useCurrentModal();
|
|
3152
|
+
const styles = useDynamicStyles(dropdownStyles);
|
|
3153
|
+
return (React__default.createElement(React__default.Fragment, null,
|
|
3154
|
+
React__default.createElement(Layer$1, { onBackdropClick: (event, data) => onClose(event, data), className: styles.layer },
|
|
3155
|
+
React__default.createElement(Popper, { reference: anchorRef, placement: "bottom-start" },
|
|
3156
|
+
React__default.createElement(FlyoutMenu, null, dropDownOptions.map((dropDownOption, index) => (React__default.createElement(MenuItem$1, { key: index, destructive: dropDownOption.destructive, label: dropDownOption.label, icon: dropDownOption.icon, onClick: () => {
|
|
3157
|
+
onMenuClick(dropDownOption);
|
|
3158
|
+
onClose();
|
|
3159
|
+
}, dense: true })))))),
|
|
3160
|
+
CurrentModal && (React__default.createElement(CurrentModal, { onClose: () => {
|
|
3161
|
+
onClose();
|
|
3162
|
+
setCurrentModal(null);
|
|
3163
|
+
} }))));
|
|
3164
|
+
};
|
|
3165
|
+
const DropdownMenu = (props) => {
|
|
3166
|
+
const ref = useRef();
|
|
3167
|
+
const [visible, setVisible] = useState(false);
|
|
3168
|
+
const { dropdownOptions, onClick } = props;
|
|
3169
|
+
return (React__default.createElement("div", { ref: ref, style: { display: "inline-block" } },
|
|
3170
|
+
React__default.createElement(Button$1, { small: true, secondary: true, icon: React__default.createElement(IconMore24, { color: colors.grey600 }), onClick: () => setVisible(true) }),
|
|
3171
|
+
visible && (React__default.createElement(ContextMenu, { dropDownOptions: dropdownOptions, anchorRef: ref, onClose: () => setVisible(false), onMenuClick: (dropDownOption) => {
|
|
3172
|
+
onClick(dropDownOption);
|
|
3173
|
+
} }))));
|
|
3174
|
+
};
|
|
3175
|
+
|
|
3176
|
+
const getProgramStageData = (response, programStageId, pager) => {
|
|
3177
|
+
const events = response.data.events;
|
|
3178
|
+
const allDataElements = new Set();
|
|
3179
|
+
events.forEach((event) => {
|
|
3180
|
+
event.dataValues.forEach((dv) => allDataElements.add(dv.dataElement));
|
|
3181
|
+
});
|
|
3182
|
+
const stageFromMetaData = response.metadata.programStages.find((stage) => stage.id === programStageId);
|
|
3183
|
+
if (!stageFromMetaData) {
|
|
3184
|
+
throw new Error(`Program stage with ID ${programStageId} not found`);
|
|
3185
|
+
}
|
|
3186
|
+
const entityColumns = Array.from(allDataElements).map((dataElementId) => ({
|
|
3187
|
+
label: stageFromMetaData.programStageDataElements.find((psde) => psde.dataElement.id === dataElementId)?.dataElement.name || dataElementId,
|
|
3188
|
+
key: dataElementId,
|
|
3189
|
+
}));
|
|
3190
|
+
const dataElementsData = events.map((event, idx) => {
|
|
3191
|
+
let row = {
|
|
3192
|
+
event: event.event,
|
|
3193
|
+
index: (pager.page - 1) * pager.pageSize + idx + 1,
|
|
3194
|
+
};
|
|
3195
|
+
allDataElements.forEach((dataElementId) => (row[dataElementId] = ''));
|
|
3196
|
+
event.dataValues.forEach((dv) => (row[dv.dataElement] = dv.value));
|
|
3197
|
+
return row;
|
|
3198
|
+
});
|
|
3199
|
+
return { columns: entityColumns, data: dataElementsData };
|
|
3200
|
+
};
|
|
3201
|
+
const getTrackedEntityData = (response, programId, pager, filters) => {
|
|
3202
|
+
let teis = response.data
|
|
3203
|
+
.trackedEntityInstances;
|
|
3204
|
+
if (filters) {
|
|
3205
|
+
teis = getFilteredTrackedEntityInstances(teis, filters);
|
|
3206
|
+
}
|
|
3207
|
+
const allAttributes = new Set();
|
|
3208
|
+
teis.forEach((tei) => {
|
|
3209
|
+
const matchingEnrollment = tei.enrollments.find((enrollment) => enrollment.program === programId);
|
|
3210
|
+
const attributes = matchingEnrollment
|
|
3211
|
+
? matchingEnrollment.attributes
|
|
3212
|
+
: tei.attributes;
|
|
3213
|
+
attributes.forEach((attr) => allAttributes.add(attr.attribute));
|
|
3214
|
+
});
|
|
3215
|
+
const entityColumns = Array.from(allAttributes).map((attrId) => {
|
|
3216
|
+
const foundAttribute = teis
|
|
3217
|
+
.flatMap((tei) => {
|
|
3218
|
+
const matchingEnrollment = tei.enrollments.find((enrollment) => enrollment.program === programId);
|
|
3219
|
+
return matchingEnrollment
|
|
3220
|
+
? matchingEnrollment.attributes
|
|
3221
|
+
: tei.attributes;
|
|
3222
|
+
})
|
|
3223
|
+
.find((attr) => attr.attribute === attrId);
|
|
3224
|
+
return {
|
|
3225
|
+
label: foundAttribute?.displayName || attrId,
|
|
3226
|
+
key: attrId,
|
|
3227
|
+
};
|
|
3228
|
+
});
|
|
3229
|
+
const attributesData = teis.map((tei, idx) => {
|
|
3230
|
+
let row = {
|
|
3231
|
+
trackedEntityInstance: tei.trackedEntityInstance,
|
|
3232
|
+
index: (pager.page - 1) * pager.pageSize + idx + 1,
|
|
3233
|
+
};
|
|
3234
|
+
allAttributes.forEach((attrId) => (row[attrId] = ''));
|
|
3235
|
+
const matchingEnrollment = tei.enrollments.find((enrollment) => enrollment.program === programId);
|
|
3236
|
+
const attributesToUse = matchingEnrollment
|
|
3237
|
+
? matchingEnrollment.attributes
|
|
3238
|
+
: tei.attributes;
|
|
3239
|
+
attributesToUse.forEach((attr) => (row[attr.attribute] = attr.value));
|
|
3240
|
+
return row;
|
|
3241
|
+
});
|
|
3242
|
+
return { columns: entityColumns, data: attributesData };
|
|
3243
|
+
};
|
|
3244
|
+
const getEventData = (response, pager) => {
|
|
3245
|
+
const events = response.data.events;
|
|
3246
|
+
const uniqueDataElements = new Set();
|
|
3247
|
+
events.forEach((event) => {
|
|
3248
|
+
event.dataValues.forEach((dv) => uniqueDataElements.add(dv.dataElement));
|
|
3249
|
+
});
|
|
3250
|
+
const entityColumns = Array.from(uniqueDataElements).map((dataElement) => ({
|
|
3251
|
+
label: response.metadata.programStages[0].programStageDataElements.find((psde) => psde.dataElement.id === dataElement)?.dataElement.name || dataElement,
|
|
3252
|
+
key: dataElement,
|
|
3253
|
+
}));
|
|
3254
|
+
const dataElementsData = events.map((event, idx) => {
|
|
3255
|
+
let row = {
|
|
3256
|
+
event: event.event,
|
|
3257
|
+
index: (pager.page - 1) * pager.pageSize + idx + 1,
|
|
3258
|
+
};
|
|
3259
|
+
uniqueDataElements.forEach((dataElement) => (row[dataElement] = ''));
|
|
3260
|
+
event.dataValues.forEach((dv) => (row[dv.dataElement] = dv.value));
|
|
3261
|
+
return row;
|
|
3262
|
+
});
|
|
3263
|
+
return { columns: entityColumns, data: dataElementsData };
|
|
3264
|
+
};
|
|
3265
|
+
const addActionsColumn = (columns, actionOptions) => {
|
|
3266
|
+
return [
|
|
3267
|
+
...columns,
|
|
3268
|
+
...(actionOptions && actionOptions.length > 0
|
|
3269
|
+
? [{ label: 'Actions', key: 'actions' }]
|
|
3270
|
+
: []),
|
|
3271
|
+
];
|
|
3272
|
+
};
|
|
3273
|
+
|
|
3274
|
+
// src/app/line-list-table.component.ts
|
|
3275
|
+
class LineListTableComponent extends ReactWrapperComponent {
|
|
3276
|
+
constructor() {
|
|
3277
|
+
super(...arguments);
|
|
3278
|
+
this.actionSelected = new EventEmitter();
|
|
3279
|
+
this.reactStateUpdaters = null;
|
|
3280
|
+
this.setReactStateUpdaters = (updaters) => {
|
|
3281
|
+
this.reactStateUpdaters = updaters;
|
|
3282
|
+
};
|
|
3283
|
+
this.lineListService = inject(LineListService);
|
|
3284
|
+
this.LineList = () => {
|
|
3285
|
+
const [columns, setColumns] = useState([]);
|
|
3286
|
+
const [data, setData] = useState([]);
|
|
3287
|
+
const [pager, setPager] = useState({
|
|
3288
|
+
page: 1,
|
|
3289
|
+
pageSize: 10,
|
|
3290
|
+
total: 0,
|
|
3291
|
+
pageCount: 1,
|
|
3292
|
+
});
|
|
3293
|
+
const paginationRef = useRef(null);
|
|
3294
|
+
const [programIdState, setProgramIdState] = useState(this.programId);
|
|
3295
|
+
const [orgUnitState, setOrgUnitState] = useState(this.orgUnit);
|
|
3296
|
+
const [programStageIdState, setProgramStageIdState] = useState(this.programStageId);
|
|
3297
|
+
const [attributeFiltersState, setAttributeFiltersState] = useState(this.attributeFilters);
|
|
3298
|
+
const [startDateState, setStartDateState] = useState(this.startDate);
|
|
3299
|
+
const [endDateState, setEndDateState] = useState(this.endDate);
|
|
3300
|
+
const [loading, setLoading] = useState(false);
|
|
3301
|
+
// Store updaters in refs for Angular to access
|
|
3302
|
+
const updateRefs = useRef({
|
|
3303
|
+
setProgramIdState,
|
|
3304
|
+
setOrgUnitState,
|
|
3305
|
+
setProgramStageIdState,
|
|
3306
|
+
setAttributeFiltersState,
|
|
3307
|
+
setStartDateState,
|
|
3308
|
+
setEndDateState,
|
|
3309
|
+
});
|
|
3310
|
+
useEffect(() => {
|
|
3311
|
+
this.setReactStateUpdaters?.(updateRefs.current);
|
|
3312
|
+
}, []);
|
|
3313
|
+
useEffect(() => {
|
|
3314
|
+
if (!loading) {
|
|
3315
|
+
setTimeout(() => {
|
|
3316
|
+
const paginationContainer = document.querySelector('[data-test="dhis2-uiwidgets-pagination"]');
|
|
3317
|
+
if (paginationContainer) {
|
|
3318
|
+
paginationContainer.style.display = 'flex';
|
|
3319
|
+
paginationContainer.style.flexDirection = 'row';
|
|
3320
|
+
paginationContainer.style.justifyContent = 'space-between';
|
|
3321
|
+
paginationContainer.style.alignItems = 'center';
|
|
3322
|
+
}
|
|
3323
|
+
}, 500); // Short delay to ensure styles apply after data renders
|
|
3324
|
+
}
|
|
3325
|
+
}, [loading]);
|
|
3326
|
+
useEffect(() => {
|
|
3327
|
+
setLoading(true);
|
|
3328
|
+
this.lineListService
|
|
3329
|
+
.getLineListData(this.programId, this.orgUnit, this.programStageId, pager.page, pager.pageSize, this.attributeFilters, this.startDate, this.endDate)
|
|
3330
|
+
.subscribe((response) => {
|
|
3331
|
+
let entityColumns = [];
|
|
3332
|
+
let responsePager;
|
|
3333
|
+
let entityData = [];
|
|
3334
|
+
if (this.programStageId) {
|
|
3335
|
+
responsePager = response.data.pager;
|
|
3336
|
+
const { columns, data } = getProgramStageData(response, this.programStageId, pager);
|
|
3337
|
+
entityColumns = columns;
|
|
3338
|
+
entityData = data;
|
|
3339
|
+
}
|
|
3340
|
+
else if ('trackedEntityInstances' in response.data) {
|
|
3341
|
+
responsePager = response.data
|
|
3342
|
+
.pager;
|
|
3343
|
+
const { columns, data } = getTrackedEntityData(response, this.programId, pager, this.filters);
|
|
3344
|
+
entityColumns = columns;
|
|
3345
|
+
entityData = data;
|
|
3346
|
+
}
|
|
3347
|
+
else {
|
|
3348
|
+
responsePager = response.data.pager;
|
|
3349
|
+
const { columns, data } = getEventData(response, pager);
|
|
3350
|
+
entityColumns = columns;
|
|
3351
|
+
entityData = data;
|
|
3352
|
+
}
|
|
3353
|
+
const finalColumns = addActionsColumn([{ label: '#', key: 'index' }, ...entityColumns], this.actionOptions);
|
|
3354
|
+
setLoading(false);
|
|
3355
|
+
console.log('hey');
|
|
3356
|
+
setColumns(...[finalColumns]);
|
|
3357
|
+
setData(...[entityData]);
|
|
3358
|
+
setPager(...[responsePager]);
|
|
3359
|
+
console.log('columns', columns);
|
|
3360
|
+
console.log('data', data);
|
|
3361
|
+
});
|
|
3362
|
+
}, [
|
|
3363
|
+
programIdState,
|
|
3364
|
+
orgUnitState,
|
|
3365
|
+
programStageIdState,
|
|
3366
|
+
attributeFiltersState,
|
|
3367
|
+
startDateState,
|
|
3368
|
+
endDateState,
|
|
3369
|
+
pager.page,
|
|
3370
|
+
pager.pageSize,
|
|
3371
|
+
]);
|
|
3372
|
+
const getDropdownOptions = (row) => {
|
|
3373
|
+
return (this.actionOptions || []).map((option) => ({
|
|
3374
|
+
...option,
|
|
3375
|
+
onClick: () => {
|
|
3376
|
+
option.onClick?.(row);
|
|
3377
|
+
this.actionSelected.emit({ action: option.label, row });
|
|
3378
|
+
},
|
|
3379
|
+
}));
|
|
3380
|
+
};
|
|
3381
|
+
return (React__default.createElement("div", null, loading ? (React__default.createElement(CircularLoader$1, null)) :
|
|
3382
|
+
(React__default.createElement(DataTable, null,
|
|
3383
|
+
React__default.createElement(TableHead, null,
|
|
3384
|
+
React__default.createElement(DataTableRow, null, columns.map((col) => (React__default.createElement(DataTableColumnHeader, { key: col.key }, col.label))))),
|
|
3385
|
+
React__default.createElement(TableBody, null, 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, { dropdownOptions: getDropdownOptions(row), onClick: (option) => option.onClick?.() })) : (row[col.key])))))))),
|
|
3386
|
+
React__default.createElement(TableFoot, null,
|
|
3387
|
+
React__default.createElement(DataTableRow, null,
|
|
3388
|
+
React__default.createElement(DataTableCell, { colSpan: columns.length.toString() },
|
|
3389
|
+
React__default.createElement("div", null,
|
|
3390
|
+
React__default.createElement(Pagination, { page: pager.page, pageCount: pager.pageCount, pageSize: pager.pageSize, total: pager.total, onPageChange: (page) => setPager((prev) => ({ ...prev, page })), onPageSizeChange: (pageSize) => {
|
|
3391
|
+
const newPageSize = Number(pageSize);
|
|
3392
|
+
setPager((prev) => ({
|
|
3393
|
+
...prev,
|
|
3394
|
+
page: 1,
|
|
3395
|
+
pageSize: newPageSize,
|
|
3396
|
+
}));
|
|
3397
|
+
}, pageSizes: ['5', '10', '20', '50'] })))))))));
|
|
3398
|
+
};
|
|
3399
|
+
}
|
|
3400
|
+
ngOnChanges(changes) {
|
|
3401
|
+
if (this.reactStateUpdaters) {
|
|
3402
|
+
if (changes['programId']) {
|
|
3403
|
+
this.reactStateUpdaters.setProgramIdState(this.programId);
|
|
3404
|
+
}
|
|
3405
|
+
if (changes['orgUnit']) {
|
|
3406
|
+
this.reactStateUpdaters.setOrgUnitState(this.orgUnit);
|
|
3407
|
+
}
|
|
3408
|
+
if (changes['programStageId']) {
|
|
3409
|
+
this.reactStateUpdaters.setProgramStageIdState(this.programStageId);
|
|
3410
|
+
}
|
|
3411
|
+
if (changes['attributeFilters']) {
|
|
3412
|
+
this.reactStateUpdaters.setAttributeFiltersState(this.attributeFilters);
|
|
3413
|
+
}
|
|
3414
|
+
if (changes['startDate']) {
|
|
3415
|
+
this.reactStateUpdaters.setStartDateState(this.startDate);
|
|
3416
|
+
}
|
|
3417
|
+
if (changes['endDate']) {
|
|
3418
|
+
this.reactStateUpdaters.setEndDateState(this.endDate);
|
|
3419
|
+
}
|
|
3420
|
+
}
|
|
3421
|
+
}
|
|
3422
|
+
async ngAfterViewInit() {
|
|
3423
|
+
if (!this.elementRef)
|
|
3424
|
+
throw new Error('No element ref');
|
|
3425
|
+
this.reactDomRoot = ReactDOM.createRoot(this.elementRef.nativeElement);
|
|
3426
|
+
this.component = this.LineList;
|
|
3427
|
+
this.render();
|
|
3428
|
+
}
|
|
3429
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: LineListTableComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
3430
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.0.7", type: LineListTableComponent, isStandalone: false, selector: "app-line-list", inputs: { programId: "programId", orgUnit: "orgUnit", programStageId: "programStageId", actionOptions: "actionOptions", attributeFilters: "attributeFilters", startDate: "startDate", endDate: "endDate", filters: "filters" }, outputs: { actionSelected: "actionSelected" }, usesInheritance: true, usesOnChanges: true, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, styles: ["[data-test=dhis2-uiwidgets-pagination]{display:flex!important;flex-direction:row!important;justify-content:space-between!important;align-items:center!important}\n"] }); }
|
|
3431
|
+
}
|
|
3432
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: LineListTableComponent, decorators: [{
|
|
3433
|
+
type: Component,
|
|
3434
|
+
args: [{ selector: 'app-line-list', template: '<ng-content></ng-content>', standalone: false, styles: ["[data-test=dhis2-uiwidgets-pagination]{display:flex!important;flex-direction:row!important;justify-content:space-between!important;align-items:center!important}\n"] }]
|
|
3435
|
+
}], propDecorators: { programId: [{
|
|
3436
|
+
type: Input
|
|
3437
|
+
}], orgUnit: [{
|
|
3438
|
+
type: Input
|
|
3439
|
+
}], programStageId: [{
|
|
3440
|
+
type: Input
|
|
3441
|
+
}], actionOptions: [{
|
|
3442
|
+
type: Input
|
|
3443
|
+
}], attributeFilters: [{
|
|
3444
|
+
type: Input
|
|
3445
|
+
}], startDate: [{
|
|
3446
|
+
type: Input
|
|
3447
|
+
}], endDate: [{
|
|
3448
|
+
type: Input
|
|
3449
|
+
}], filters: [{
|
|
3450
|
+
type: Input
|
|
3451
|
+
}], actionSelected: [{
|
|
3452
|
+
type: Output
|
|
3453
|
+
}] } });
|
|
3454
|
+
|
|
3455
|
+
class LineListModule {
|
|
3456
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: LineListModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
3457
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.0.7", ngImport: i0, type: LineListModule, declarations: [LineListTableComponent], imports: [CommonModule, SharedDhis2UiModule], exports: [LineListTableComponent] }); }
|
|
3458
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: LineListModule, providers: [LineListService], imports: [CommonModule, SharedDhis2UiModule] }); }
|
|
3459
|
+
}
|
|
3460
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.7", ngImport: i0, type: LineListModule, decorators: [{
|
|
3461
|
+
type: NgModule,
|
|
3462
|
+
args: [{
|
|
3463
|
+
declarations: [LineListTableComponent],
|
|
3464
|
+
exports: [LineListTableComponent],
|
|
3465
|
+
imports: [CommonModule, SharedDhis2UiModule],
|
|
3466
|
+
providers: [LineListService],
|
|
3467
|
+
}]
|
|
3468
|
+
}] });
|
|
3469
|
+
|
|
2966
3470
|
/**
|
|
2967
3471
|
* Generated bundle index. Do not edit.
|
|
2968
3472
|
*/
|
|
2969
3473
|
|
|
2970
|
-
export { ButtonComponent, ButtonModule, HeaderBarComponent, HeaderBarModule, OrganisationUnitSelectionConfig, OrganisationUnitSelectorComponent, OrganisationUnitSelectorModalComponent, OrganisationUnitSelectorModule, PeriodSelectorComponent, PeriodSelectorModalComponent, PeriodSelectorModule, ReactWrapperComponent, ReactWrapperModule };
|
|
3474
|
+
export { ButtonComponent, ButtonModule, HeaderBarComponent, HeaderBarModule, LineListModule, LineListTableComponent, OrganisationUnitSelectionConfig, OrganisationUnitSelectorComponent, OrganisationUnitSelectorModalComponent, OrganisationUnitSelectorModule, PeriodSelectorComponent, PeriodSelectorModalComponent, PeriodSelectorModule, ReactWrapperComponent, ReactWrapperModule };
|
|
2971
3475
|
//# sourceMappingURL=iapps-ng-dhis2-ui.mjs.map
|