@iapps/ng-dhis2-ui 1.2.140 → 1.2.153
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 +429 -83
- package/fesm2022/iapps-ng-dhis2-ui.mjs.map +1 -1
- package/index.d.ts +15 -11
- package/package.json +1 -1
|
@@ -3838,37 +3838,273 @@ const DropdownMenu$1 = (props) => {
|
|
|
3838
3838
|
} }))));
|
|
3839
3839
|
};
|
|
3840
3840
|
|
|
3841
|
-
|
|
3842
|
-
//
|
|
3843
|
-
//
|
|
3841
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3842
|
+
// // Copyright 2025 UDSM DHIS2 Lab. All rights reserved.
|
|
3843
|
+
// // Use of this source code is governed by a BSD-style
|
|
3844
|
+
// // license that can be found in the LICENSE file.
|
|
3845
|
+
// import {
|
|
3846
|
+
// ActionOptionOrientation,
|
|
3847
|
+
// DropdownMenuOption,
|
|
3848
|
+
// LineListActionOption,
|
|
3849
|
+
// } from '../../models';
|
|
3850
|
+
// import { DropdownMenu } from '../dropdown-menu';
|
|
3851
|
+
// import React from 'react';
|
|
3852
|
+
// import { ButtonStrip, Button, colors } from '@dhis2/ui';
|
|
3853
|
+
// import { TableRow } from '../../models/line-list.models';
|
|
3854
|
+
// import { LineListRowActionFilterConfig } from '@iapps/d2-web-sdk';
|
|
3855
|
+
// export const DataTableActions = (props: {
|
|
3856
|
+
// data: TableRow;
|
|
3857
|
+
// actionOptions: LineListActionOption[];
|
|
3858
|
+
// rowActionFilterConfig: LineListRowActionFilterConfig;
|
|
3859
|
+
// actionOptionOrientation: ActionOptionOrientation;
|
|
3860
|
+
// onClick: (dropdownOption: LineListActionOption) => void;
|
|
3861
|
+
// }) => {
|
|
3862
|
+
// const {
|
|
3863
|
+
// data,
|
|
3864
|
+
// actionOptions,
|
|
3865
|
+
// rowActionFilterConfig,
|
|
3866
|
+
// actionOptionOrientation,
|
|
3867
|
+
// onClick,
|
|
3868
|
+
// } = props;
|
|
3869
|
+
// console.log(
|
|
3870
|
+
// 'REACHED HERE AND IMPROVE THE FOLLOWING::: ',
|
|
3871
|
+
// // JSON.stringify(data),
|
|
3872
|
+
// rowActionFilterConfig
|
|
3873
|
+
// );
|
|
3874
|
+
// switch (actionOptionOrientation) {
|
|
3875
|
+
// case 'BUTTON':
|
|
3876
|
+
// return (
|
|
3877
|
+
// <ButtonStrip>
|
|
3878
|
+
// {actionOptions.map((option) => (
|
|
3879
|
+
// <Button
|
|
3880
|
+
// key={option.label}
|
|
3881
|
+
// onClick={() => onClick(option)}
|
|
3882
|
+
// icon={option.icon ? <option.icon /> : <></>}
|
|
3883
|
+
// small
|
|
3884
|
+
// destructive={option.destructive}
|
|
3885
|
+
// >
|
|
3886
|
+
// {!option.iconOnly ? option.label : ''}
|
|
3887
|
+
// </Button>
|
|
3888
|
+
// ))}
|
|
3889
|
+
// </ButtonStrip>
|
|
3890
|
+
// );
|
|
3891
|
+
// case 'DROPDOWN':
|
|
3892
|
+
// return (
|
|
3893
|
+
// <DropdownMenu
|
|
3894
|
+
// dropdownOptions={actionOptions}
|
|
3895
|
+
// onClick={(option) => {
|
|
3896
|
+
// onClick(option);
|
|
3897
|
+
// }}
|
|
3898
|
+
// />
|
|
3899
|
+
// );
|
|
3900
|
+
// case 'LINK':
|
|
3901
|
+
// return (
|
|
3902
|
+
// <div
|
|
3903
|
+
// style={{
|
|
3904
|
+
// display: 'flex',
|
|
3905
|
+
// gap: 16,
|
|
3906
|
+
// alignItems: 'center',
|
|
3907
|
+
// }}
|
|
3908
|
+
// >
|
|
3909
|
+
// {actionOptions.map((option) => (
|
|
3910
|
+
// <a
|
|
3911
|
+
// style={{
|
|
3912
|
+
// cursor: 'pointer',
|
|
3913
|
+
// textDecoration: 'underline',
|
|
3914
|
+
// fontSize: 14,
|
|
3915
|
+
// display: 'flex',
|
|
3916
|
+
// alignItems: 'center',
|
|
3917
|
+
// gap: 4,
|
|
3918
|
+
// color: option.destructive ? colors.red700 : colors.grey900,
|
|
3919
|
+
// }}
|
|
3920
|
+
// key={option.label}
|
|
3921
|
+
// onClick={(event: any) => {
|
|
3922
|
+
// event.stopPropagation();
|
|
3923
|
+
// onClick(option);
|
|
3924
|
+
// }}
|
|
3925
|
+
// >
|
|
3926
|
+
// {option.icon ? <option.icon /> : <></>}
|
|
3927
|
+
// {!option.iconOnly ? <span>{option.label}</span> : <></>}
|
|
3928
|
+
// </a>
|
|
3929
|
+
// ))}
|
|
3930
|
+
// </div>
|
|
3931
|
+
// );
|
|
3932
|
+
// default:
|
|
3933
|
+
// return <></>;
|
|
3934
|
+
// }
|
|
3935
|
+
// };
|
|
3936
|
+
const extractTeiPayloadFromRow = (row) => row?.responseData?.value ?? null;
|
|
3937
|
+
/** Operator matcher (fast + predictable) */
|
|
3938
|
+
const matchesOperator = (actual, op, expected) => {
|
|
3939
|
+
const actualStr = actual == null ? '' : String(actual);
|
|
3940
|
+
const expectedList = Array.isArray(expected)
|
|
3941
|
+
? expected.map(String)
|
|
3942
|
+
: expected != null
|
|
3943
|
+
? [String(expected)]
|
|
3944
|
+
: [];
|
|
3945
|
+
switch (op) {
|
|
3946
|
+
case 'EXISTS':
|
|
3947
|
+
return actual != null;
|
|
3948
|
+
case 'NOT_EXISTS':
|
|
3949
|
+
return actual == null;
|
|
3950
|
+
case 'HAS_VALUE':
|
|
3951
|
+
return actualStr.trim().length > 0;
|
|
3952
|
+
case 'NO_VALUE':
|
|
3953
|
+
return actualStr.trim().length === 0;
|
|
3954
|
+
case 'EQ':
|
|
3955
|
+
return actualStr === (expectedList[0] ?? '');
|
|
3956
|
+
case 'NEQ':
|
|
3957
|
+
return actualStr !== (expectedList[0] ?? '');
|
|
3958
|
+
case 'IN':
|
|
3959
|
+
return expectedList.includes(actualStr);
|
|
3960
|
+
case 'NOT_IN':
|
|
3961
|
+
return !expectedList.includes(actualStr);
|
|
3962
|
+
default:
|
|
3963
|
+
return false;
|
|
3964
|
+
}
|
|
3965
|
+
};
|
|
3966
|
+
/**
|
|
3967
|
+
* Extract facts needed for rule evaluation from the TEI payload.
|
|
3968
|
+
* This is O(enrollments + events + attributes) and uses plain objects for speed.
|
|
3969
|
+
*/
|
|
3970
|
+
const buildTeiFacts = (teiPayload) => {
|
|
3971
|
+
const enrolledProgramIds = new Set();
|
|
3972
|
+
const eventCountByStageId = Object.create(null);
|
|
3973
|
+
const attributeValueById = Object.create(null);
|
|
3974
|
+
const attributeValueByCode = Object.create(null);
|
|
3975
|
+
const enrollments = teiPayload.enrollments ?? [];
|
|
3976
|
+
for (const enrollment of enrollments) {
|
|
3977
|
+
if (enrollment?.program)
|
|
3978
|
+
enrolledProgramIds.add(enrollment.program);
|
|
3979
|
+
const events = enrollment?.events ?? [];
|
|
3980
|
+
for (const ev of events) {
|
|
3981
|
+
const stageId = ev?.programStage;
|
|
3982
|
+
if (!stageId)
|
|
3983
|
+
continue;
|
|
3984
|
+
eventCountByStageId[stageId] = (eventCountByStageId[stageId] ?? 0) + 1;
|
|
3985
|
+
}
|
|
3986
|
+
}
|
|
3987
|
+
const attributes = teiPayload.attributes ?? [];
|
|
3988
|
+
for (const attr of attributes) {
|
|
3989
|
+
const attrId = attr?.attribute;
|
|
3990
|
+
const attrCode = attr?.code;
|
|
3991
|
+
const value = attr?.value;
|
|
3992
|
+
if (attrId)
|
|
3993
|
+
attributeValueById[attrId] = value;
|
|
3994
|
+
if (attrCode)
|
|
3995
|
+
attributeValueByCode[attrCode] = value;
|
|
3996
|
+
}
|
|
3997
|
+
return {
|
|
3998
|
+
enrolledProgramIds,
|
|
3999
|
+
eventCountByStageId,
|
|
4000
|
+
attributeValueById,
|
|
4001
|
+
attributeValueByCode,
|
|
4002
|
+
};
|
|
4003
|
+
};
|
|
4004
|
+
const doesRuleMatchFacts = (rule, facts) => {
|
|
4005
|
+
// Enrollment condition
|
|
4006
|
+
if (rule.enrollment) {
|
|
4007
|
+
const hasEnrollment = facts.enrolledProgramIds.has(rule.enrollment.programId);
|
|
4008
|
+
if (hasEnrollment !== rule.enrollment.mustExist)
|
|
4009
|
+
return false;
|
|
4010
|
+
}
|
|
4011
|
+
// Stage event existence condition
|
|
4012
|
+
if (rule.stageEvent) {
|
|
4013
|
+
const count = facts.eventCountByStageId[rule.stageEvent.programStageId] ?? 0;
|
|
4014
|
+
const hasStageEvent = count > 0;
|
|
4015
|
+
if (hasStageEvent !== rule.stageEvent.mustExist)
|
|
4016
|
+
return false;
|
|
4017
|
+
}
|
|
4018
|
+
// Attribute condition (support by id OR code)
|
|
4019
|
+
if (rule.attribute) {
|
|
4020
|
+
const value = rule.attribute.attributeId
|
|
4021
|
+
? facts.attributeValueById[rule.attribute.attributeId]
|
|
4022
|
+
: undefined;
|
|
4023
|
+
if (!matchesOperator(value, rule.attribute.op, rule.attribute.value))
|
|
4024
|
+
return false;
|
|
4025
|
+
}
|
|
4026
|
+
return true;
|
|
4027
|
+
};
|
|
4028
|
+
const compileFilterConfig = (config) => {
|
|
4029
|
+
if (!config?.enabled)
|
|
4030
|
+
return null;
|
|
4031
|
+
// Sort once, not per row render
|
|
4032
|
+
const rulesSorted = [...(config.rules ?? [])].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
|
|
4033
|
+
return {
|
|
4034
|
+
enabled: true,
|
|
4035
|
+
fallback: config.fallback ?? 'NO_FILTER',
|
|
4036
|
+
combine: config.combine ?? 'APPLY_ALL',
|
|
4037
|
+
rulesSorted,
|
|
4038
|
+
};
|
|
4039
|
+
};
|
|
4040
|
+
const computeAllowedActionIdsForRow = (baseActionIds, compiled, facts) => {
|
|
4041
|
+
const matchedRules = [];
|
|
4042
|
+
for (const rule of compiled.rulesSorted) {
|
|
4043
|
+
if (doesRuleMatchFacts(rule, facts)) {
|
|
4044
|
+
matchedRules.push(rule);
|
|
4045
|
+
if (compiled.combine === 'FIRST_MATCH')
|
|
4046
|
+
break;
|
|
4047
|
+
}
|
|
4048
|
+
}
|
|
4049
|
+
// If nothing matched: return base or empty depending on fallback
|
|
4050
|
+
if (matchedRules.length === 0) {
|
|
4051
|
+
return compiled.fallback === 'NO_FILTER' ? baseActionIds : [];
|
|
4052
|
+
}
|
|
4053
|
+
let allowed = new Set(baseActionIds);
|
|
4054
|
+
for (const rule of matchedRules) {
|
|
4055
|
+
if (rule.effect === 'HIDE') {
|
|
4056
|
+
for (const actionId of rule.actions)
|
|
4057
|
+
allowed.delete(actionId);
|
|
4058
|
+
}
|
|
4059
|
+
else {
|
|
4060
|
+
// SHOW_ONLY
|
|
4061
|
+
allowed = new Set(rule.actions.filter((id) => allowed.has(id)));
|
|
4062
|
+
}
|
|
4063
|
+
}
|
|
4064
|
+
return Array.from(allowed);
|
|
4065
|
+
};
|
|
3844
4066
|
const DataTableActions = (props) => {
|
|
3845
|
-
const { data, actionOptions, actionOptionOrientation, onClick } = props;
|
|
3846
|
-
|
|
4067
|
+
const { data: row, actionOptions, rowActionFilterConfig, actionOptionOrientation: orientation, onClick: onActionClick, } = props;
|
|
4068
|
+
const compiledConfig = useMemo(() => compileFilterConfig(rowActionFilterConfig), [rowActionFilterConfig]);
|
|
4069
|
+
const visibleActions = useMemo(() => {
|
|
4070
|
+
const base = actionOptions ?? [];
|
|
4071
|
+
// No config => show base
|
|
4072
|
+
if (!compiledConfig)
|
|
4073
|
+
return base;
|
|
4074
|
+
// Extract embedded TEI payload (already available in row)
|
|
4075
|
+
const teiPayload = extractTeiPayloadFromRow(row);
|
|
4076
|
+
// If no payload => do not filter (your requirement)
|
|
4077
|
+
if (!teiPayload)
|
|
4078
|
+
return base;
|
|
4079
|
+
// Prefer stable action id; fallback to label as legacy
|
|
4080
|
+
const baseActionIds = base.map((a) => a.id ?? a.label);
|
|
4081
|
+
const facts = buildTeiFacts(teiPayload);
|
|
4082
|
+
const allowedIds = computeAllowedActionIdsForRow(baseActionIds, compiledConfig, facts);
|
|
4083
|
+
// If allowedIds equals baseActionIds, this filter is basically no-op,
|
|
4084
|
+
// but we keep a cheap Set to filter objects correctly.
|
|
4085
|
+
const allowedSet = new Set(allowedIds);
|
|
4086
|
+
return base.filter((a) => allowedSet.has(a.id ?? a.label));
|
|
4087
|
+
}, [actionOptions, row, compiledConfig]);
|
|
4088
|
+
switch (orientation) {
|
|
3847
4089
|
case 'BUTTON':
|
|
3848
|
-
return (React__default.createElement(ButtonStrip$1, null,
|
|
4090
|
+
return (React__default.createElement(ButtonStrip$1, null, visibleActions.map((action) => (React__default.createElement(Button$1, { key: action.id ?? action.label, onClick: () => onActionClick(action), icon: action.icon ? React__default.createElement(action.icon, null) : React__default.createElement(React__default.Fragment, null), small: true, destructive: action.destructive }, !action.iconOnly ? action.label : '')))));
|
|
3849
4091
|
case 'DROPDOWN':
|
|
3850
|
-
return (React__default.createElement(DropdownMenu$1, { dropdownOptions:
|
|
3851
|
-
onClick(option);
|
|
3852
|
-
} }));
|
|
4092
|
+
return (React__default.createElement(DropdownMenu$1, { dropdownOptions: visibleActions, onClick: (action) => onActionClick(action) }));
|
|
3853
4093
|
case 'LINK':
|
|
3854
|
-
return (React__default.createElement("div", { style: {
|
|
3855
|
-
display: 'flex',
|
|
3856
|
-
gap: 16,
|
|
3857
|
-
alignItems: 'center',
|
|
3858
|
-
} }, actionOptions.map((option) => (React__default.createElement("a", { style: {
|
|
4094
|
+
return (React__default.createElement("div", { style: { display: 'flex', gap: 16, alignItems: 'center' } }, visibleActions.map((action) => (React__default.createElement("a", { key: action.id ?? action.label, style: {
|
|
3859
4095
|
cursor: 'pointer',
|
|
3860
4096
|
textDecoration: 'underline',
|
|
3861
4097
|
fontSize: 14,
|
|
3862
4098
|
display: 'flex',
|
|
3863
4099
|
alignItems: 'center',
|
|
3864
4100
|
gap: 4,
|
|
3865
|
-
color:
|
|
3866
|
-
},
|
|
4101
|
+
color: action.destructive ? colors$1.red700 : colors$1.grey900,
|
|
4102
|
+
}, onClick: (event) => {
|
|
3867
4103
|
event.stopPropagation();
|
|
3868
|
-
|
|
4104
|
+
onActionClick(action);
|
|
3869
4105
|
} },
|
|
3870
|
-
|
|
3871
|
-
!
|
|
4106
|
+
action.icon ? React__default.createElement(action.icon, null) : React__default.createElement(React__default.Fragment, null),
|
|
4107
|
+
!action.iconOnly ? React__default.createElement("span", null, action.label) : React__default.createElement(React__default.Fragment, null))))));
|
|
3872
4108
|
default:
|
|
3873
4109
|
return React__default.createElement(React__default.Fragment, null);
|
|
3874
4110
|
}
|
|
@@ -3900,7 +4136,7 @@ const TableCell = ({ cell, getTextColorFromBackGround, }) => {
|
|
|
3900
4136
|
React__default.createElement("div", { className: "text-sm text-gray-800" }, cell?.value || '-')));
|
|
3901
4137
|
};
|
|
3902
4138
|
|
|
3903
|
-
const TableRow = ({ row, columns, getTextColorFromBackGround, actionOptions, actionOptionOrientation, actionSelected, isChecked, onToggle, selectable, showActionButtons, }) => {
|
|
4139
|
+
const TableRow = ({ row, columns, getTextColorFromBackGround, actionOptions, rowActionFilterConfig, actionOptionOrientation, actionSelected, isChecked, onToggle, selectable, showActionButtons, }) => {
|
|
3904
4140
|
const handleCheckBoxChange = () => {
|
|
3905
4141
|
onToggle(row);
|
|
3906
4142
|
};
|
|
@@ -3909,7 +4145,7 @@ const TableRow = ({ row, columns, getTextColorFromBackGround, actionOptions, act
|
|
|
3909
4145
|
React__default.createElement(Checkbox, { onChange: handleCheckBoxChange, checked: isChecked }))),
|
|
3910
4146
|
columns.map((col) => {
|
|
3911
4147
|
if (col.key === 'actions') {
|
|
3912
|
-
return (React__default.createElement(DataTableCell, { key: col.key }, actionOptions && showActionButtons && (React__default.createElement(DataTableActions, { actionOptions: actionOptions, data: row, actionOptionOrientation: actionOptionOrientation, onClick: (option) => {
|
|
4148
|
+
return (React__default.createElement(DataTableCell, { key: col.key }, actionOptions && showActionButtons && (React__default.createElement(DataTableActions, { actionOptions: actionOptions, rowActionFilterConfig: rowActionFilterConfig, data: row, actionOptionOrientation: actionOptionOrientation, onClick: (option) => {
|
|
3913
4149
|
const data = row['responseData']?.['value'];
|
|
3914
4150
|
if (option.onClick)
|
|
3915
4151
|
option.onClick(data);
|
|
@@ -3961,13 +4197,13 @@ const TableRow = ({ row, columns, getTextColorFromBackGround, actionOptions, act
|
|
|
3961
4197
|
// )}
|
|
3962
4198
|
// </DHISTableBody>
|
|
3963
4199
|
// );
|
|
3964
|
-
const TableBody = ({ data, columns, getTextColorFromBackGround, actionOptions, actionOptionOrientation, actionSelected, selectedRows, onRowToggle, selectable, showActionButtons, error, }) => (React__default.createElement(TableBody$1, null, error ? (React__default.createElement(DataTableRow, null,
|
|
4200
|
+
const TableBody = ({ data, columns, getTextColorFromBackGround, actionOptions, rowActionFilterConfig, actionOptionOrientation, actionSelected, selectedRows, onRowToggle, selectable, showActionButtons, error, }) => (React__default.createElement(TableBody$1, null, error ? (React__default.createElement(DataTableRow, null,
|
|
3965
4201
|
React__default.createElement(DataTableCell, { colSpan: columns.length.toString(), style: {
|
|
3966
4202
|
textAlign: 'center',
|
|
3967
4203
|
fontWeight: 'bold',
|
|
3968
4204
|
color: 'red',
|
|
3969
4205
|
padding: '20px',
|
|
3970
|
-
} }, error))) : data.length > 0 ? (data.map((row) => (React__default.createElement(TableRow, { key: row.index.value, row: row, columns: columns, getTextColorFromBackGround: getTextColorFromBackGround, actionOptions: actionOptions, actionOptionOrientation: actionOptionOrientation, actionSelected: actionSelected, isChecked: selectedRows.some((r) => r.index?.value === row.index?.value), onToggle: onRowToggle, selectable: selectable, showActionButtons: showActionButtons })))) : (React__default.createElement(DataTableRow, null,
|
|
4206
|
+
} }, error))) : data.length > 0 ? (data.map((row) => (React__default.createElement(TableRow, { key: row.index.value, row: row, columns: columns, getTextColorFromBackGround: getTextColorFromBackGround, actionOptions: actionOptions, rowActionFilterConfig: rowActionFilterConfig, actionOptionOrientation: actionOptionOrientation, actionSelected: actionSelected, isChecked: selectedRows.some((r) => r.index?.value === row.index?.value), onToggle: onRowToggle, selectable: selectable, showActionButtons: showActionButtons })))) : (React__default.createElement(DataTableRow, null,
|
|
3971
4207
|
React__default.createElement(DataTableCell, { colSpan: columns.length.toString(), style: {
|
|
3972
4208
|
textAlign: 'center',
|
|
3973
4209
|
fontWeight: 'bold',
|
|
@@ -4024,7 +4260,7 @@ const TableFooter = ({ pager, setPager, columns }) => {
|
|
|
4024
4260
|
// </DataTable>
|
|
4025
4261
|
// );
|
|
4026
4262
|
// };
|
|
4027
|
-
const LineListTable = ({ columns, data, pager, setPager, getTextColorFromBackGround, actionOptions, actionOptionOrientation, actionSelected, selectable, rowsSelected, showActionButtons, error }) => {
|
|
4263
|
+
const LineListTable = ({ columns, data, pager, setPager, getTextColorFromBackGround, actionOptions, rowActionFilterConfig, actionOptionOrientation, actionSelected, selectable, rowsSelected, showActionButtons, error }) => {
|
|
4028
4264
|
const [selectedRows, setSelectedRows] = useState([]);
|
|
4029
4265
|
const handleRowToggle = (row) => {
|
|
4030
4266
|
const exists = selectedRows.find((r) => r.index?.value === row.index?.value);
|
|
@@ -4041,7 +4277,7 @@ const LineListTable = ({ columns, data, pager, setPager, getTextColorFromBackGro
|
|
|
4041
4277
|
};
|
|
4042
4278
|
return (React__default.createElement(DataTable, null,
|
|
4043
4279
|
React__default.createElement(TableHeader, { columns: columns, allSelected: selectedRows.length === data.length && data.length > 0, onSelectAll: handleSelectAll, selectable: selectable, showActionButtons: showActionButtons }),
|
|
4044
|
-
React__default.createElement(TableBody, { data: data, columns: columns, getTextColorFromBackGround: getTextColorFromBackGround, actionOptions: actionOptions, actionOptionOrientation: actionOptionOrientation, actionSelected: actionSelected, selectedRows: selectedRows, onRowToggle: handleRowToggle, selectable: selectable, showActionButtons: showActionButtons, error: error }),
|
|
4280
|
+
React__default.createElement(TableBody, { data: data, columns: columns, getTextColorFromBackGround: getTextColorFromBackGround, actionOptions: actionOptions, rowActionFilterConfig: rowActionFilterConfig, actionOptionOrientation: actionOptionOrientation, actionSelected: actionSelected, selectedRows: selectedRows, onRowToggle: handleRowToggle, selectable: selectable, showActionButtons: showActionButtons, error: error }),
|
|
4045
4281
|
React__default.createElement(TableFooter, { columns: columns, pager: pager, setPager: setPager })));
|
|
4046
4282
|
};
|
|
4047
4283
|
|
|
@@ -4397,6 +4633,7 @@ class LineListTableComponent extends ReactWrapperModule {
|
|
|
4397
4633
|
super(...arguments);
|
|
4398
4634
|
this.actionOptionOrientation = 'DROPDOWN';
|
|
4399
4635
|
this.dataQueryFilters = [];
|
|
4636
|
+
this.hydrateForActionFiltering = true;
|
|
4400
4637
|
this.isButtonLoading = false;
|
|
4401
4638
|
this.filterRootOrgUnit = false;
|
|
4402
4639
|
this.showFilters = false;
|
|
@@ -4432,6 +4669,7 @@ class LineListTableComponent extends ReactWrapperModule {
|
|
|
4432
4669
|
const [attributeFiltersState, setAttributeFiltersState] = useState(this.attributeFilters);
|
|
4433
4670
|
const [dataQueryFiltersState, setDataQueryFiltersState] = useState(this.dataQueryFilters);
|
|
4434
4671
|
const [tempDataQueryFiltersState, setTempDataQueryFiltersState] = useState(this.dataQueryFilters);
|
|
4672
|
+
const [rowActionFilterConfig, setRowActionFilterConfig] = useState(this.lineListRowActionFilterConfig);
|
|
4435
4673
|
const [startDateState, setStartDateState] = useState(this.startDate);
|
|
4436
4674
|
const [tempStartDateState, setTempStartDateState] = useState(this.startDate);
|
|
4437
4675
|
const [endDateState, setEndDateState] = useState(this.endDate);
|
|
@@ -4926,6 +5164,7 @@ class LineListTableComponent extends ReactWrapperModule {
|
|
|
4926
5164
|
setPager: setPager,
|
|
4927
5165
|
getTextColorFromBackGround: getTextColorFromBackGround,
|
|
4928
5166
|
actionOptions: this.actionOptions,
|
|
5167
|
+
rowActionFilterConfig: this.lineListRowActionFilterConfig,
|
|
4929
5168
|
actionOptionOrientation: this.actionOptionOrientation,
|
|
4930
5169
|
actionSelected: this.actionSelected,
|
|
4931
5170
|
selectable: selectable,
|
|
@@ -4981,7 +5220,7 @@ class LineListTableComponent extends ReactWrapperModule {
|
|
|
4981
5220
|
this.render();
|
|
4982
5221
|
}
|
|
4983
5222
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: LineListTableComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
4984
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.3", type: LineListTableComponent, isStandalone: false, selector: "ng-dhis2-ui-line-list", inputs: { triggerToken: "triggerToken", programId: "programId", orgUnit: "orgUnit", programStageId: "programStageId", actionOptions: "actionOptions", actionOptionOrientation: "actionOptionOrientation", attributeFilters: "attributeFilters", useOuModeWithOlderDHIS2Instance: "useOuModeWithOlderDHIS2Instance", startDate: "startDate", endDate: "endDate", dataQueryFilters: "dataQueryFilters", ouMode: "ouMode", isButtonLoading: "isButtonLoading", buttonFilter: "buttonFilter", filterRootOrgUnit: "filterRootOrgUnit", showFilters: "showFilters", isOptionSetNameVisible: "isOptionSetNameVisible", allowRowsSelection: "allowRowsSelection", enrollmentStatus: "enrollmentStatus", filterableColumnIds: "filterableColumnIds", triggerRefetch: "triggerRefetch", eventStatus: "eventStatus", showActionButtons: "showActionButtons", showEnrollmentDates: "showEnrollmentDates", showDownloadButton: "showDownloadButton", searcheableDataElements: "searcheableDataElements" }, outputs: { actionSelected: "actionSelected", rowsSelected: "rowsSelected" }, usesInheritance: true, usesOnChanges: true, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, styles: ["@charset \"UTF-8\";::ng-deep .custom-input{display:flex!important;gap:4px!important;align-items:center!important;border:1px solid #9fadba!important;border-radius:4px!important;margin:0;padding:0 0 0 8px;background-color:#fff!important}::ng-deep .custom-input div{margin:0!important}::ng-deep .custom-input label{font-size:12px;padding-right:0}::ng-deep .custom-input input{border:none!important;font-size:12px!important;display:flex;align-items:center;padding-left:4px;height:40px;outline:none!important;box-shadow:none!important}::ng-deep .custom-input input:focus{border:none!important;box-shadow:none!important}::ng-deep .custom-input [data-test=calendar-clear-button]{background-color:#f2f2f2!important;color:#870808!important;border-radius:6px!important;padding:4px 10px!important;font-size:0!important}::ng-deep .custom-input [data-test=calendar-clear-button]:before{content:\"\\2715\";font-size:14px;color:#870808}::ng-deep .custom-input [data-test=calendar-clear-button]:hover{background-color:#ddd}::ng-deep .calendar-input-wrapper{position:relative}::ng-deep .calendar-input-wrapper .custom-input{position:relative;width:100%;display:flex;align-items:center}::ng-deep .calendar-input-wrapper [data-test=calendar-clear-button]{right:8px;bottom:14px;margin:auto;display:flex;align-items:center;justify-content:center;width:16px;height:16px;font-size:0!important;background-color:#a0adba!important;border-radius:50%;padding:2px;cursor:pointer}::ng-deep .calendar-input-wrapper [data-test=calendar-clear-button]:before{content:\"\\2715\";font-size:10px;color:#f0f0f0}::ng-deep .calendar-input-wrapper input{padding-right:40px!important;height:40px;line-height:40px;box-sizing:border-box}::ng-deep .custom-input label{color:gray!important;opacity:1}::ng-deep .table-top-toolbar{background-color:var(--colors-grey100)!important;color:var(--colors-grey900)!important}::ng-deep .custom-select-input{display:flex!important;gap:4px!important;align-items:center!important;border:1px solid #9fadba!important;border-radius:4px!important;margin:0;padding:0 0 0 8px;background-color:#fff!important}::ng-deep .custom-select-input div{margin:0!important}::ng-deep .custom-select-input [data-test=dhis2-uiwidgets-singleselectfield-label]{font-size:12px;color:gray!important}::ng-deep .custom-select-input [data-test=dhis2-uicore-select-input]{border:none!important;font-size:12px!important;display:flex;align-items:center;padding-left:4px;height:40px;outline:none!important;box-shadow:none!important}::ng-deep .custom-select-input [data-test=dhis2-uicore-select-input]:focus-within{box-shadow:none!important}::ng-deep .custom-select-input input:focus{border:none!important;box-shadow:none!important}\n"] }); }
|
|
5223
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.3", type: LineListTableComponent, isStandalone: false, selector: "ng-dhis2-ui-line-list", inputs: { triggerToken: "triggerToken", programId: "programId", orgUnit: "orgUnit", programStageId: "programStageId", actionOptions: "actionOptions", actionOptionOrientation: "actionOptionOrientation", attributeFilters: "attributeFilters", useOuModeWithOlderDHIS2Instance: "useOuModeWithOlderDHIS2Instance", startDate: "startDate", endDate: "endDate", dataQueryFilters: "dataQueryFilters", lineListRowActionFilterConfig: "lineListRowActionFilterConfig", hydrateForActionFiltering: "hydrateForActionFiltering", ouMode: "ouMode", isButtonLoading: "isButtonLoading", buttonFilter: "buttonFilter", filterRootOrgUnit: "filterRootOrgUnit", showFilters: "showFilters", isOptionSetNameVisible: "isOptionSetNameVisible", allowRowsSelection: "allowRowsSelection", enrollmentStatus: "enrollmentStatus", filterableColumnIds: "filterableColumnIds", triggerRefetch: "triggerRefetch", eventStatus: "eventStatus", showActionButtons: "showActionButtons", showEnrollmentDates: "showEnrollmentDates", showDownloadButton: "showDownloadButton", searcheableDataElements: "searcheableDataElements" }, outputs: { actionSelected: "actionSelected", rowsSelected: "rowsSelected" }, usesInheritance: true, usesOnChanges: true, ngImport: i0, template: '<ng-content></ng-content>', isInline: true, styles: ["@charset \"UTF-8\";::ng-deep .custom-input{display:flex!important;gap:4px!important;align-items:center!important;border:1px solid #9fadba!important;border-radius:4px!important;margin:0;padding:0 0 0 8px;background-color:#fff!important}::ng-deep .custom-input div{margin:0!important}::ng-deep .custom-input label{font-size:12px;padding-right:0}::ng-deep .custom-input input{border:none!important;font-size:12px!important;display:flex;align-items:center;padding-left:4px;height:40px;outline:none!important;box-shadow:none!important}::ng-deep .custom-input input:focus{border:none!important;box-shadow:none!important}::ng-deep .custom-input [data-test=calendar-clear-button]{background-color:#f2f2f2!important;color:#870808!important;border-radius:6px!important;padding:4px 10px!important;font-size:0!important}::ng-deep .custom-input [data-test=calendar-clear-button]:before{content:\"\\2715\";font-size:14px;color:#870808}::ng-deep .custom-input [data-test=calendar-clear-button]:hover{background-color:#ddd}::ng-deep .calendar-input-wrapper{position:relative}::ng-deep .calendar-input-wrapper .custom-input{position:relative;width:100%;display:flex;align-items:center}::ng-deep .calendar-input-wrapper [data-test=calendar-clear-button]{right:8px;bottom:14px;margin:auto;display:flex;align-items:center;justify-content:center;width:16px;height:16px;font-size:0!important;background-color:#a0adba!important;border-radius:50%;padding:2px;cursor:pointer}::ng-deep .calendar-input-wrapper [data-test=calendar-clear-button]:before{content:\"\\2715\";font-size:10px;color:#f0f0f0}::ng-deep .calendar-input-wrapper input{padding-right:40px!important;height:40px;line-height:40px;box-sizing:border-box}::ng-deep .custom-input label{color:gray!important;opacity:1}::ng-deep .table-top-toolbar{background-color:var(--colors-grey100)!important;color:var(--colors-grey900)!important}::ng-deep .custom-select-input{display:flex!important;gap:4px!important;align-items:center!important;border:1px solid #9fadba!important;border-radius:4px!important;margin:0;padding:0 0 0 8px;background-color:#fff!important}::ng-deep .custom-select-input div{margin:0!important}::ng-deep .custom-select-input [data-test=dhis2-uiwidgets-singleselectfield-label]{font-size:12px;color:gray!important}::ng-deep .custom-select-input [data-test=dhis2-uicore-select-input]{border:none!important;font-size:12px!important;display:flex;align-items:center;padding-left:4px;height:40px;outline:none!important;box-shadow:none!important}::ng-deep .custom-select-input [data-test=dhis2-uicore-select-input]:focus-within{box-shadow:none!important}::ng-deep .custom-select-input input:focus{border:none!important;box-shadow:none!important}\n"] }); }
|
|
4985
5224
|
}
|
|
4986
5225
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: LineListTableComponent, decorators: [{
|
|
4987
5226
|
type: Component,
|
|
@@ -5008,6 +5247,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImpor
|
|
|
5008
5247
|
type: Input
|
|
5009
5248
|
}], dataQueryFilters: [{
|
|
5010
5249
|
type: Input
|
|
5250
|
+
}], lineListRowActionFilterConfig: [{
|
|
5251
|
+
type: Input
|
|
5252
|
+
}], hydrateForActionFiltering: [{
|
|
5253
|
+
type: Input
|
|
5011
5254
|
}], ouMode: [{
|
|
5012
5255
|
type: Input
|
|
5013
5256
|
}], isButtonLoading: [{
|
|
@@ -5843,8 +6086,8 @@ Only proceed if you are certain this is intentional and acceptable according to
|
|
|
5843
6086
|
? `Location outside allowed area (not permitted)`
|
|
5844
6087
|
: `Location outside allowed area (review required)`;
|
|
5845
6088
|
// Optional debug logs (keep outside JSX)
|
|
5846
|
-
console.log('rawAllowedArea:', rawAllowedArea);
|
|
5847
|
-
console.log('safeAllowedAreaGeoJson:', safeAllowedAreaGeoJson);
|
|
6089
|
+
// console.log('rawAllowedArea:', rawAllowedArea);
|
|
6090
|
+
// console.log('safeAllowedAreaGeoJson:', safeAllowedAreaGeoJson);
|
|
5848
6091
|
return (React__default.createElement(React__default.Fragment, null,
|
|
5849
6092
|
React__default.createElement(Field, { label: label, name: name, required: required, error: Boolean(combinedErrorMessage), validationText: combinedErrorMessage ?? undefined },
|
|
5850
6093
|
React__default.createElement("div", { style: {
|
|
@@ -7667,7 +7910,7 @@ class FieldCascadeUtil {
|
|
|
7667
7910
|
if (!childPath || !parentLeaf)
|
|
7668
7911
|
return false;
|
|
7669
7912
|
const token = `${separator}${parentLeaf}`;
|
|
7670
|
-
return
|
|
7913
|
+
return childPath.endsWith(token) || childPath.includes(token + separator);
|
|
7671
7914
|
}
|
|
7672
7915
|
/**
|
|
7673
7916
|
* Safe property getter for "unknown object shapes" (keeps TS happy).
|
|
@@ -7692,6 +7935,87 @@ class FieldCascadeUtil {
|
|
|
7692
7935
|
}
|
|
7693
7936
|
return FieldCascadeUtil.normalizePath(raw, merged.optionPathSeparator);
|
|
7694
7937
|
}
|
|
7938
|
+
// static matchesChildToParent(
|
|
7939
|
+
// childPathRaw: string,
|
|
7940
|
+
// parentEffectiveRaw: string,
|
|
7941
|
+
// mode: ParentMatchMode,
|
|
7942
|
+
// separator = '/'
|
|
7943
|
+
// ): boolean {
|
|
7944
|
+
// const childPath = FieldCascadeUtil.normalizePath(childPathRaw, separator);
|
|
7945
|
+
// console.log("STABILITY::: ", JSON.stringify(childPath));
|
|
7946
|
+
// const parentEffective = FieldCascadeUtil.normalizePath(
|
|
7947
|
+
// parentEffectiveRaw,
|
|
7948
|
+
// separator
|
|
7949
|
+
// );
|
|
7950
|
+
// if (!childPath || !parentEffective) return false;
|
|
7951
|
+
// switch (mode) {
|
|
7952
|
+
// // case ParentMatchMode.PREFIX: {
|
|
7953
|
+
// // // Allow exact node match and boundary-safe prefix
|
|
7954
|
+
// // if (childPath === parentEffective) return true;
|
|
7955
|
+
// // const prefix = parentEffective.endsWith(separator)
|
|
7956
|
+
// // ? parentEffective
|
|
7957
|
+
// // : parentEffective + separator;
|
|
7958
|
+
// // return childPath.startsWith(prefix);
|
|
7959
|
+
// // }
|
|
7960
|
+
// default: {
|
|
7961
|
+
// const leaf = FieldCascadeUtil.getPathLeaf(parentEffective, separator);
|
|
7962
|
+
// return FieldCascadeUtil.containsLeafToken(childPath, leaf, separator);
|
|
7963
|
+
// }
|
|
7964
|
+
// }
|
|
7965
|
+
// }
|
|
7966
|
+
/**
|
|
7967
|
+
* Segment-safe "contains" match:
|
|
7968
|
+
* checks if parent segments appear contiguously anywhere in child segments
|
|
7969
|
+
*/
|
|
7970
|
+
static containsPathSegments(childPath, parentPath, separator = '/') {
|
|
7971
|
+
const childSegs = FieldCascadeUtil.splitSegments(childPath, separator);
|
|
7972
|
+
const parentSegs = FieldCascadeUtil.splitSegments(parentPath, separator);
|
|
7973
|
+
if (parentSegs.length === 0 || childSegs.length === 0)
|
|
7974
|
+
return false;
|
|
7975
|
+
if (parentSegs.length > childSegs.length)
|
|
7976
|
+
return false;
|
|
7977
|
+
// sliding window
|
|
7978
|
+
for (let start = 0; start <= childSegs.length - parentSegs.length; start++) {
|
|
7979
|
+
let ok = true;
|
|
7980
|
+
for (let j = 0; j < parentSegs.length; j++) {
|
|
7981
|
+
if (childSegs[start + j] !== parentSegs[j]) {
|
|
7982
|
+
ok = false;
|
|
7983
|
+
break;
|
|
7984
|
+
}
|
|
7985
|
+
}
|
|
7986
|
+
if (ok)
|
|
7987
|
+
return true;
|
|
7988
|
+
}
|
|
7989
|
+
return false;
|
|
7990
|
+
}
|
|
7991
|
+
/** Split normalized path into non-empty segments */
|
|
7992
|
+
static splitSegments(path, separator = '/') {
|
|
7993
|
+
const p = FieldCascadeUtil.normalizePath(path, separator);
|
|
7994
|
+
if (!p)
|
|
7995
|
+
return [];
|
|
7996
|
+
return p
|
|
7997
|
+
.split(separator)
|
|
7998
|
+
.map((s) => s.trim())
|
|
7999
|
+
.filter(Boolean);
|
|
8000
|
+
}
|
|
8001
|
+
/**
|
|
8002
|
+
* Segment-safe "prefix" match:
|
|
8003
|
+
* - exact match OR child starts with parent + separator boundary
|
|
8004
|
+
*/
|
|
8005
|
+
static startsWithPathSegments(childPath, parentPath, separator = '/') {
|
|
8006
|
+
const childSegs = FieldCascadeUtil.splitSegments(childPath, separator);
|
|
8007
|
+
const parentSegs = FieldCascadeUtil.splitSegments(parentPath, separator);
|
|
8008
|
+
if (parentSegs.length === 0 || childSegs.length === 0)
|
|
8009
|
+
return false;
|
|
8010
|
+
if (parentSegs.length > childSegs.length)
|
|
8011
|
+
return false;
|
|
8012
|
+
for (let i = 0; i < parentSegs.length; i++) {
|
|
8013
|
+
if (childSegs[i] !== parentSegs[i])
|
|
8014
|
+
return false;
|
|
8015
|
+
}
|
|
8016
|
+
return true;
|
|
8017
|
+
}
|
|
8018
|
+
/** Your requested method: no includes/contains; all segment-safe */
|
|
7695
8019
|
static matchesChildToParent(childPathRaw, parentEffectiveRaw, mode, separator = '/') {
|
|
7696
8020
|
const childPath = FieldCascadeUtil.normalizePath(childPathRaw, separator);
|
|
7697
8021
|
const parentEffective = FieldCascadeUtil.normalizePath(parentEffectiveRaw, separator);
|
|
@@ -7699,15 +8023,24 @@ class FieldCascadeUtil {
|
|
|
7699
8023
|
return false;
|
|
7700
8024
|
switch (mode) {
|
|
7701
8025
|
case ParentMatchMode.PREFIX: {
|
|
7702
|
-
//
|
|
7703
|
-
|
|
7704
|
-
|
|
7705
|
-
|
|
7706
|
-
|
|
7707
|
-
|
|
7708
|
-
|
|
8026
|
+
// exact node match or boundary-safe prefix (segment-based)
|
|
8027
|
+
return FieldCascadeUtil.startsWithPathSegments(childPath, parentEffective, separator);
|
|
8028
|
+
}
|
|
8029
|
+
case ParentMatchMode.CONTAINS: {
|
|
8030
|
+
// boundary-safe contains (segment-based)
|
|
8031
|
+
// const nana = FieldCascadeUtil.containsPathSegments(
|
|
8032
|
+
// childPath,
|
|
8033
|
+
// parentEffective,
|
|
8034
|
+
// separator
|
|
8035
|
+
// );
|
|
8036
|
+
// console.log("MAMA::: ", childPath);
|
|
8037
|
+
// console.log("MAMA 1111::: ", parentEffective);
|
|
8038
|
+
// console.log("MAMA 2222::: ", separator);
|
|
8039
|
+
// console.log("MAMA 2222::: ", separator);
|
|
8040
|
+
return FieldCascadeUtil.containsPathSegments(childPath, parentEffective, separator);
|
|
7709
8041
|
}
|
|
7710
8042
|
default: {
|
|
8043
|
+
// your existing behavior: match by leaf token, but segment-safe
|
|
7711
8044
|
const leaf = FieldCascadeUtil.getPathLeaf(parentEffective, separator);
|
|
7712
8045
|
return FieldCascadeUtil.containsLeafToken(childPath, leaf, separator);
|
|
7713
8046
|
}
|
|
@@ -7723,12 +8056,12 @@ class FieldCascadeUtil {
|
|
|
7723
8056
|
if (!parentEffective)
|
|
7724
8057
|
return true;
|
|
7725
8058
|
if (merged.parentValueType === ParentValueType.CODE) {
|
|
7726
|
-
return (FieldCascadeUtil.getPathLeaf(parentEffective, merged.optionPathSeparator)
|
|
7727
|
-
.length === 0);
|
|
8059
|
+
return (FieldCascadeUtil.getPathLeaf(parentEffective, merged.optionPathSeparator).length === 0);
|
|
7728
8060
|
}
|
|
7729
8061
|
return false;
|
|
7730
8062
|
}
|
|
7731
8063
|
static filterChildOptionsByParentPath(childOptions, cascade, parentValue) {
|
|
8064
|
+
console.log('MERGE::: ', cascade);
|
|
7732
8065
|
if (!cascade || cascade.kind !== CascadeKind.OPTIONS_PATH) {
|
|
7733
8066
|
return (childOptions ?? []);
|
|
7734
8067
|
}
|
|
@@ -7748,6 +8081,10 @@ class FieldCascadeUtil {
|
|
|
7748
8081
|
const childPath = FieldCascadeUtil.getChildOptionPath(opt, pathField, sep);
|
|
7749
8082
|
if (!childPath)
|
|
7750
8083
|
continue;
|
|
8084
|
+
// console.log('CHILDPATH::: ', childPath);
|
|
8085
|
+
// console.log('PARENTEFFECTIVE::: ', parentEffective);
|
|
8086
|
+
// console.log('MODE::: ', mode);
|
|
8087
|
+
// console.log('SEP::: ', sep);
|
|
7751
8088
|
if (FieldCascadeUtil.matchesChildToParent(childPath, parentEffective, mode, sep)) {
|
|
7752
8089
|
out.push(opt);
|
|
7753
8090
|
}
|
|
@@ -8593,6 +8930,9 @@ class BaseFormFieldComponent extends ReactWrapperModule {
|
|
|
8593
8930
|
setDisabled(baseDisabled || cascadeDisable);
|
|
8594
8931
|
// filter options
|
|
8595
8932
|
const nextOptions = FieldCascadeUtil.filterChildOptionsByParentPath(field.options ?? [], cascade, parentStr);
|
|
8933
|
+
// console.log("NOEW PARENT STR::: ", JSON.stringify(parentStr));
|
|
8934
|
+
// console.log("NOEW FIELD OPTIONS::: ", JSON.stringify(field.options));
|
|
8935
|
+
// console.log("NOEW PARENT CASCADE::: ", JSON.stringify(cascade));
|
|
8596
8936
|
setFilteredOptions(nextOptions);
|
|
8597
8937
|
// clear invalid child on parent change if configured
|
|
8598
8938
|
if (merged.clearOnParentChange && reason === 'parentChange') {
|
|
@@ -11379,78 +11719,84 @@ class ProgramEntryFormFieldUtil {
|
|
|
11379
11719
|
});
|
|
11380
11720
|
}
|
|
11381
11721
|
// #getGeometryField(): IFormField<string> | null {
|
|
11382
|
-
//
|
|
11383
|
-
//
|
|
11384
|
-
//
|
|
11385
|
-
// JSON.stringify(rawFeatureType)
|
|
11386
|
-
// );
|
|
11387
|
-
// const featureType = rawFeatureType.toUpperCase() as
|
|
11722
|
+
// if (this.config?.hideGeometryField) return null;
|
|
11723
|
+
// const effectiveFeatureTypeRaw = this.#resolveEffectiveFeatureType();
|
|
11724
|
+
// const normalizedFeatureType = effectiveFeatureTypeRaw.toUpperCase() as
|
|
11388
11725
|
// | 'NONE'
|
|
11389
11726
|
// | 'POINT'
|
|
11390
11727
|
// | 'POLYGON'
|
|
11391
11728
|
// | 'MULTI_POLYGON'
|
|
11392
11729
|
// | '';
|
|
11393
|
-
// const
|
|
11394
|
-
//
|
|
11395
|
-
//
|
|
11396
|
-
//
|
|
11397
|
-
// if (!
|
|
11398
|
-
//
|
|
11399
|
-
//
|
|
11400
|
-
//
|
|
11401
|
-
//
|
|
11402
|
-
// }
|
|
11403
|
-
// const sharedOptions = {
|
|
11404
|
-
// required: true,
|
|
11405
|
-
// disabled: this.config?.disableRegistrationUnit ?? false,
|
|
11406
|
-
// isGeometryField: true as const,
|
|
11407
|
-
// };
|
|
11408
|
-
// const label =
|
|
11409
|
-
// featureType === 'POINT' ? 'Location (coordinates)' : 'Boundary (polygon)';
|
|
11730
|
+
// const supportsGeometry =
|
|
11731
|
+
// normalizedFeatureType === 'POINT' ||
|
|
11732
|
+
// normalizedFeatureType === 'POLYGON' ||
|
|
11733
|
+
// normalizedFeatureType === 'MULTI_POLYGON';
|
|
11734
|
+
// if (!supportsGeometry) return null;
|
|
11735
|
+
// const geometryFieldLabel =
|
|
11736
|
+
// normalizedFeatureType === 'POINT'
|
|
11737
|
+
// ? 'Location (coordinates)'
|
|
11738
|
+
// : 'Boundary (polygon)';
|
|
11410
11739
|
// return new FormField<string>({
|
|
11411
11740
|
// id: 'geometry',
|
|
11412
11741
|
// key: 'geometry',
|
|
11413
|
-
// label,
|
|
11414
11742
|
// code: 'geometry',
|
|
11743
|
+
// label: geometryFieldLabel,
|
|
11415
11744
|
// controlType: 'coordinate',
|
|
11416
|
-
//
|
|
11745
|
+
// required: true,
|
|
11746
|
+
// disabled: this.config?.disableGeometryField ?? false,
|
|
11747
|
+
// isGeometryField: true,
|
|
11417
11748
|
// });
|
|
11418
11749
|
// }
|
|
11419
11750
|
#getGeometryField() {
|
|
11420
|
-
|
|
11751
|
+
// Hide by default unless explicitly enabled
|
|
11752
|
+
if (this.config?.hideGeometryField !== false)
|
|
11421
11753
|
return null;
|
|
11422
|
-
const
|
|
11423
|
-
const
|
|
11424
|
-
|
|
11425
|
-
|
|
11426
|
-
normalizedFeatureType === 'MULTI_POLYGON';
|
|
11754
|
+
const featureType = this.#resolveEffectiveFeatureType().toUpperCase();
|
|
11755
|
+
const supportsGeometry = featureType === 'POINT' ||
|
|
11756
|
+
featureType === 'POLYGON' ||
|
|
11757
|
+
featureType === 'MULTI_POLYGON';
|
|
11427
11758
|
if (!supportsGeometry)
|
|
11428
11759
|
return null;
|
|
11429
|
-
const
|
|
11430
|
-
? 'Location (coordinates)'
|
|
11431
|
-
: 'Boundary (polygon)';
|
|
11760
|
+
const label = featureType === 'POINT' ? 'Location (coordinates)' : 'Boundary (polygon)';
|
|
11432
11761
|
return new FormField({
|
|
11433
11762
|
id: 'geometry',
|
|
11434
11763
|
key: 'geometry',
|
|
11435
11764
|
code: 'geometry',
|
|
11436
|
-
label
|
|
11765
|
+
label,
|
|
11437
11766
|
controlType: 'coordinate',
|
|
11438
11767
|
required: true,
|
|
11439
|
-
disabled: this.config?.
|
|
11768
|
+
disabled: this.config?.disableGeometryField ?? false,
|
|
11440
11769
|
isGeometryField: true,
|
|
11441
11770
|
});
|
|
11442
11771
|
}
|
|
11772
|
+
// #resolveEffectiveFeatureType(): string {
|
|
11773
|
+
// const programMeta = this.program;
|
|
11774
|
+
// const configuredProgramStageId = this.config?.programStage;
|
|
11775
|
+
// if (configuredProgramStageId && programMeta?.programStages?.length) {
|
|
11776
|
+
// const configuredStage = programMeta.programStages.find(
|
|
11777
|
+
// (stage: ProgramStage) => stage?.id === configuredProgramStageId
|
|
11778
|
+
// );
|
|
11779
|
+
// const stageFeatureTypeRaw = configuredStage?.featureType ?? '';
|
|
11780
|
+
// if (stageFeatureTypeRaw) return String(stageFeatureTypeRaw);
|
|
11781
|
+
// }
|
|
11782
|
+
// const programFeatureTypeRaw = (programMeta as Program)?.featureType ?? '';
|
|
11783
|
+
// return String(programFeatureTypeRaw);
|
|
11784
|
+
// }
|
|
11443
11785
|
#resolveEffectiveFeatureType() {
|
|
11444
11786
|
const programMeta = this.program;
|
|
11445
|
-
|
|
11446
|
-
|
|
11447
|
-
|
|
11448
|
-
|
|
11449
|
-
|
|
11450
|
-
|
|
11451
|
-
|
|
11452
|
-
|
|
11453
|
-
|
|
11787
|
+
if (!programMeta)
|
|
11788
|
+
return '';
|
|
11789
|
+
const configuredStageId = this.config?.programStage;
|
|
11790
|
+
if (configuredStageId && Array.isArray(programMeta.programStages)) {
|
|
11791
|
+
const stage = programMeta.programStages.find((s) => s?.id === configuredStageId);
|
|
11792
|
+
const stageFeatureType = String(stage?.featureType ?? '').trim();
|
|
11793
|
+
if (stageFeatureType) {
|
|
11794
|
+
return stageFeatureType;
|
|
11795
|
+
}
|
|
11796
|
+
}
|
|
11797
|
+
// 2) Fallback to program (enrollment-level) featureType
|
|
11798
|
+
const programFeatureType = String(programMeta?.featureType ?? '').trim();
|
|
11799
|
+
return programFeatureType;
|
|
11454
11800
|
}
|
|
11455
11801
|
#getEnrollmentDateField() {
|
|
11456
11802
|
if (this.config.hideEnrollmentDate || this.config.programStage) {
|