@nnkogift/dhis2-form-utils-devtools 0.1.0-alpha.4 → 0.1.0-alpha.6
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/dist/EffectBadge.d.ts +17 -0
- package/dist/EffectBadge.js +55 -0
- package/dist/EffectBadge.js.map +1 -0
- package/dist/RuleDetailsModal.d.ts +30 -0
- package/dist/RuleDetailsModal.js +403 -0
- package/dist/RuleDetailsModal.js.map +1 -0
- package/dist/RuleDevtoolsScope.d.ts +14 -0
- package/dist/RuleDevtoolsScope.js +25 -0
- package/dist/RuleDevtoolsScope.js.map +1 -0
- package/dist/RuleGraphModal.d.ts +17 -0
- package/dist/RuleGraphModal.js +35 -0
- package/dist/RuleGraphModal.js.map +1 -0
- package/dist/RuleGraphView.d.ts +43 -0
- package/dist/RuleGraphView.js +401 -0
- package/dist/RuleGraphView.js.map +1 -0
- package/dist/RulesPanel.d.ts +19 -0
- package/dist/RulesPanel.js +463 -0
- package/dist/RulesPanel.js.map +1 -0
- package/dist/RulesPanelGraphFallback.d.ts +5 -0
- package/dist/RulesPanelGraphFallback.js +19 -0
- package/dist/RulesPanelGraphFallback.js.map +1 -0
- package/dist/TraceTimeline.d.ts +16 -0
- package/dist/TraceTimeline.js +227 -0
- package/dist/TraceTimeline.js.map +1 -0
- package/dist/attach.d.ts +12 -0
- package/dist/attach.js +14 -0
- package/dist/attach.js.map +1 -0
- package/dist/buildGraph.d.ts +25 -0
- package/dist/buildGraph.js +67 -0
- package/dist/buildGraph.js.map +1 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +5 -0
- package/dist/constants.js.map +1 -0
- package/dist/createLabelLookup.d.ts +25 -0
- package/dist/createLabelLookup.js +81 -0
- package/dist/createLabelLookup.js.map +1 -0
- package/dist/effectStyles.d.ts +28 -0
- package/dist/effectStyles.js +142 -0
- package/dist/effectStyles.js.map +1 -0
- package/dist/formatAgo.d.ts +3 -0
- package/dist/formatAgo.js +25 -0
- package/dist/formatAgo.js.map +1 -0
- package/dist/formatRuleActionSummary.d.ts +33 -0
- package/dist/formatRuleActionSummary.js +61 -0
- package/dist/formatRuleActionSummary.js.map +1 -0
- package/dist/graphLayout.d.ts +48 -0
- package/dist/graphLayout.js +137 -0
- package/dist/graphLayout.js.map +1 -0
- package/dist/graphNodeStyles.d.ts +9 -0
- package/dist/graphNodeStyles.js +22 -0
- package/dist/graphNodeStyles.js.map +1 -0
- package/dist/i18n.d.ts +4 -0
- package/dist/i18n.js +9 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index.cjs +1364 -651
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.cts +12 -63
- package/dist/index.d.ts +12 -63
- package/dist/index.js +5 -1669
- package/dist/index.js.map +1 -1
- package/dist/programRuleDetailQuery.d.ts +70 -0
- package/dist/programRuleDetailQuery.js +42 -0
- package/dist/programRuleDetailQuery.js.map +1 -0
- package/dist/resolveProgramRulesList.d.ts +21 -0
- package/dist/resolveProgramRulesList.js +45 -0
- package/dist/resolveProgramRulesList.js.map +1 -0
- package/dist/traceEntry.d.ts +12 -0
- package/dist/traceEntry.js +20 -0
- package/dist/traceEntry.js.map +1 -0
- package/dist/traceStore.d.ts +12 -0
- package/dist/traceStore.js +39 -0
- package/dist/traceStore.js.map +1 -0
- package/dist/useFlipReorder.d.ts +8 -0
- package/dist/useFlipReorder.js +52 -0
- package/dist/useFlipReorder.js.map +1 -0
- package/dist/useProgramRuleDetail.d.ts +17 -0
- package/dist/useProgramRuleDetail.js +30 -0
- package/dist/useProgramRuleDetail.js.map +1 -0
- package/package.json +27 -4
package/dist/index.cjs
CHANGED
|
@@ -1,22 +1,34 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var ui = require('@dhis2/ui');
|
|
4
|
-
var dhis2FormUtilsHooks = require('@nnkogift/dhis2-form-utils-hooks');
|
|
5
|
-
var react = require('react');
|
|
6
|
-
var dhis2FormUtilsMetadata = require('@nnkogift/dhis2-form-utils-metadata');
|
|
7
4
|
var i18n = require('@dhis2/d2-i18n');
|
|
8
5
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
+
var react = require('react');
|
|
9
7
|
var react$1 = require('@xyflow/react');
|
|
8
|
+
var dhis2FormUtilsHooks = require('@nnkogift/dhis2-form-utils-hooks');
|
|
9
|
+
var dhis2FormUtilsMetadata = require('@nnkogift/dhis2-form-utils-metadata');
|
|
10
|
+
var appRuntime = require('@dhis2/app-runtime');
|
|
10
11
|
|
|
11
12
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
13
|
|
|
13
14
|
var i18n__default = /*#__PURE__*/_interopDefault(i18n);
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
var __defProp = Object.defineProperty;
|
|
17
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
18
|
+
var __esm = (fn, res, err) => function __init() {
|
|
19
|
+
if (err) throw err[0];
|
|
20
|
+
try {
|
|
21
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
22
|
+
} catch (e) {
|
|
23
|
+
throw err = [e], e;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
var __export = (target, all) => {
|
|
27
|
+
for (var name in all)
|
|
28
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
29
|
+
};
|
|
16
30
|
|
|
17
31
|
// src/buildGraph.ts
|
|
18
|
-
var nodeKey = (kind, id) => `${kind}:${id}`;
|
|
19
|
-
var edgeKey = (source, ruleId, target, effectType) => `${source}|${ruleId}|${target}|${effectType ?? ""}`;
|
|
20
32
|
function accumulateGraph(previous, entry, labelLookup) {
|
|
21
33
|
const nodes = new Map(previous.nodes.map((node) => [node.id, node]));
|
|
22
34
|
const edges = new Map(previous.edges.map((edge) => [edge.id, edge]));
|
|
@@ -78,167 +90,13 @@ function buildGraphFromTrace(entries, labelLookup) {
|
|
|
78
90
|
{ nodes: [], edges: [] }
|
|
79
91
|
);
|
|
80
92
|
}
|
|
81
|
-
var
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
});
|
|
87
|
-
function buildEventLookup(source) {
|
|
88
|
-
const rules = /* @__PURE__ */ new Map();
|
|
89
|
-
const fields = /* @__PURE__ */ new Map();
|
|
90
|
-
const sections = /* @__PURE__ */ new Map();
|
|
91
|
-
const stages = /* @__PURE__ */ new Map();
|
|
92
|
-
for (const rule of source.metadata.programRules) {
|
|
93
|
-
if (!rule.id) {
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
rules.set(rule.id, rule.displayName ?? rule.id);
|
|
97
|
-
}
|
|
98
|
-
const stage = dhis2FormUtilsMetadata.selectProgramStage(
|
|
99
|
-
source.metadata,
|
|
100
|
-
source.programStageId
|
|
101
|
-
) ?? {
|
|
102
|
-
id: source.programStageId,
|
|
103
|
-
programStageDataElements: [],
|
|
104
|
-
programStageSections: []
|
|
105
|
-
};
|
|
106
|
-
for (const psde of stage.programStageDataElements ?? []) {
|
|
107
|
-
const de = psde.dataElement;
|
|
108
|
-
if (!de?.id) {
|
|
109
|
-
continue;
|
|
110
|
-
}
|
|
111
|
-
fields.set(de.id, de.displayFormName ?? de.displayName ?? de.id);
|
|
112
|
-
}
|
|
113
|
-
for (const section of stage.programStageSections ?? []) {
|
|
114
|
-
sections.set(section.id, section.displayName ?? section.id);
|
|
115
|
-
}
|
|
116
|
-
for (const programStage of source.metadata.programStages) {
|
|
117
|
-
if (!programStage.id) {
|
|
118
|
-
continue;
|
|
119
|
-
}
|
|
120
|
-
stages.set(programStage.id, programStage.displayName ?? programStage.id);
|
|
121
|
-
}
|
|
122
|
-
return createLookup(rules, fields, sections, stages);
|
|
123
|
-
}
|
|
124
|
-
function buildTrackerLookup(source) {
|
|
125
|
-
const rules = /* @__PURE__ */ new Map();
|
|
126
|
-
const fields = /* @__PURE__ */ new Map();
|
|
127
|
-
const sections = /* @__PURE__ */ new Map();
|
|
128
|
-
const stages = /* @__PURE__ */ new Map();
|
|
129
|
-
for (const rule of source.metadata.programRules) {
|
|
130
|
-
if (!rule.id) {
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
133
|
-
rules.set(rule.id, rule.name ?? rule.id);
|
|
134
|
-
}
|
|
135
|
-
for (const ptea of source.metadata.programTrackedEntityAttributes) {
|
|
136
|
-
const tea = ptea.trackedEntityAttribute;
|
|
137
|
-
if (!tea.id) {
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
fields.set(tea.id, tea.formName ?? tea.displayName ?? tea.id);
|
|
141
|
-
}
|
|
142
|
-
for (const section of source.metadata.programSections ?? []) {
|
|
143
|
-
sections.set(section.id, section.displayName ?? section.id);
|
|
144
|
-
}
|
|
145
|
-
for (const programStage of source.programStages ?? []) {
|
|
146
|
-
if (!programStage.id) {
|
|
147
|
-
continue;
|
|
148
|
-
}
|
|
149
|
-
stages.set(programStage.id, programStage.displayName ?? programStage.id);
|
|
150
|
-
}
|
|
151
|
-
return createLookup(rules, fields, sections, stages);
|
|
152
|
-
}
|
|
153
|
-
function createLabelLookup(source) {
|
|
154
|
-
return source.formKind === "event" ? buildEventLookup(source) : buildTrackerLookup(source);
|
|
155
|
-
}
|
|
156
|
-
var EFFECT_VARIANT = {
|
|
157
|
-
HIDEFIELD: "hide",
|
|
158
|
-
HIDEOPTION: "hide",
|
|
159
|
-
HIDEOPTIONGROUP: "hide",
|
|
160
|
-
HIDESECTION: "hide",
|
|
161
|
-
HIDEPROGRAMSTAGE: "hide",
|
|
162
|
-
SHOWFIELD: "show",
|
|
163
|
-
SHOWOPTION: "show",
|
|
164
|
-
SHOWOPTIONGROUP: "show",
|
|
165
|
-
ASSIGN: "assign",
|
|
166
|
-
SETMANDATORYFIELD: "mandatory",
|
|
167
|
-
UNSETMANDATORYFIELD: "mandatory",
|
|
168
|
-
SHOWWARNING: "warning",
|
|
169
|
-
WARNINGONCOMPLETE: "warning",
|
|
170
|
-
SHOWERROR: "error",
|
|
171
|
-
ERRORONCOMPLETE: "error",
|
|
172
|
-
DISPLAYTEXT: "feedback",
|
|
173
|
-
DISPLAYKEYVALUEPAIR: "feedback",
|
|
174
|
-
SENDMESSAGE: "message",
|
|
175
|
-
SCHEDULEMESSAGE: "message",
|
|
176
|
-
read: "read"
|
|
177
|
-
};
|
|
178
|
-
var EFFECT_VISUALS = {
|
|
179
|
-
hide: {
|
|
180
|
-
tagClassName: "bg-dhis2-grey-200 text-dhis2-grey-900",
|
|
181
|
-
edgeStroke: "#494949",
|
|
182
|
-
shortLabel: "hide"
|
|
183
|
-
},
|
|
184
|
-
show: {
|
|
185
|
-
tagClassName: "bg-dhis2-green-100 text-dhis2-green-900",
|
|
186
|
-
edgeStroke: "#388e3c",
|
|
187
|
-
shortLabel: "show"
|
|
188
|
-
},
|
|
189
|
-
assign: {
|
|
190
|
-
tagClassName: "bg-dhis2-blue-100 text-dhis2-blue-900",
|
|
191
|
-
edgeStroke: "#1565c0",
|
|
192
|
-
shortLabel: "assign"
|
|
193
|
-
},
|
|
194
|
-
mandatory: {
|
|
195
|
-
tagClassName: "bg-dhis2-yellow-100 text-dhis2-yellow-900",
|
|
196
|
-
edgeStroke: "#e56408",
|
|
197
|
-
shortLabel: "required"
|
|
198
|
-
},
|
|
199
|
-
warning: {
|
|
200
|
-
tagClassName: "bg-dhis2-yellow-100 text-dhis2-yellow-900",
|
|
201
|
-
edgeStroke: "#ff8302",
|
|
202
|
-
shortLabel: "warn"
|
|
203
|
-
},
|
|
204
|
-
error: {
|
|
205
|
-
tagClassName: "bg-dhis2-red-100 text-dhis2-red-900",
|
|
206
|
-
edgeStroke: "#c62828",
|
|
207
|
-
shortLabel: "error"
|
|
208
|
-
},
|
|
209
|
-
feedback: {
|
|
210
|
-
tagClassName: "bg-dhis2-purple-100 text-dhis2-purple-900",
|
|
211
|
-
edgeStroke: "#9c27b0",
|
|
212
|
-
shortLabel: "feedback"
|
|
213
|
-
},
|
|
214
|
-
message: {
|
|
215
|
-
tagClassName: "bg-dhis2-teal-100 text-dhis2-teal-900",
|
|
216
|
-
edgeStroke: "#00796b",
|
|
217
|
-
shortLabel: "message"
|
|
218
|
-
},
|
|
219
|
-
read: {
|
|
220
|
-
tagClassName: "bg-dhis2-teal-100 text-dhis2-teal-900",
|
|
221
|
-
edgeStroke: "#00796b",
|
|
222
|
-
shortLabel: "read"
|
|
223
|
-
},
|
|
224
|
-
default: {
|
|
225
|
-
tagClassName: "bg-dhis2-grey-100 text-dhis2-grey-800",
|
|
226
|
-
edgeStroke: "#494949",
|
|
227
|
-
shortLabel: "effect"
|
|
93
|
+
var nodeKey, edgeKey;
|
|
94
|
+
var init_buildGraph = __esm({
|
|
95
|
+
"src/buildGraph.ts"() {
|
|
96
|
+
nodeKey = (kind, id) => `${kind}:${id}`;
|
|
97
|
+
edgeKey = (source, ruleId, target, effectType) => `${source}|${ruleId}|${target}|${effectType ?? ""}`;
|
|
228
98
|
}
|
|
229
|
-
};
|
|
230
|
-
var EFFECT_ICONS = {
|
|
231
|
-
hide: ui.IconViewOff16,
|
|
232
|
-
show: ui.IconView16,
|
|
233
|
-
assign: ui.IconEdit16,
|
|
234
|
-
mandatory: ui.IconStarFilled16,
|
|
235
|
-
warning: ui.IconWarningFilled16,
|
|
236
|
-
error: ui.IconErrorFilled16,
|
|
237
|
-
feedback: ui.IconMessages16,
|
|
238
|
-
message: ui.IconMail16,
|
|
239
|
-
read: ui.IconLink16,
|
|
240
|
-
default: ui.IconInfo16
|
|
241
|
-
};
|
|
99
|
+
});
|
|
242
100
|
function getEffectVariant(type) {
|
|
243
101
|
return EFFECT_VARIANT[type] ?? "default";
|
|
244
102
|
}
|
|
@@ -246,7 +104,6 @@ function getEffectVisual(type) {
|
|
|
246
104
|
const variant = getEffectVariant(type);
|
|
247
105
|
return { variant, ...EFFECT_VISUALS[variant] };
|
|
248
106
|
}
|
|
249
|
-
var TAG_LAYOUT_CLASS = "max-w-full break-words";
|
|
250
107
|
function tagPropsForVariant(variant) {
|
|
251
108
|
switch (variant) {
|
|
252
109
|
case "show":
|
|
@@ -286,12 +143,108 @@ function getEffectEdgeStroke(type, highlighted = true) {
|
|
|
286
143
|
function getEffectShortLabel(type) {
|
|
287
144
|
return getEffectVisual(type).shortLabel;
|
|
288
145
|
}
|
|
146
|
+
var EFFECT_VARIANT, EFFECT_VISUALS; exports.EFFECT_ICONS = void 0; var TAG_LAYOUT_CLASS;
|
|
147
|
+
var init_effectStyles = __esm({
|
|
148
|
+
"src/effectStyles.ts"() {
|
|
149
|
+
EFFECT_VARIANT = {
|
|
150
|
+
HIDEFIELD: "hide",
|
|
151
|
+
HIDEOPTION: "hide",
|
|
152
|
+
HIDEOPTIONGROUP: "hide",
|
|
153
|
+
HIDESECTION: "hide",
|
|
154
|
+
HIDEPROGRAMSTAGE: "hide",
|
|
155
|
+
SHOWFIELD: "show",
|
|
156
|
+
SHOWOPTION: "show",
|
|
157
|
+
SHOWOPTIONGROUP: "show",
|
|
158
|
+
ASSIGN: "assign",
|
|
159
|
+
SETMANDATORYFIELD: "mandatory",
|
|
160
|
+
UNSETMANDATORYFIELD: "mandatory",
|
|
161
|
+
SHOWWARNING: "warning",
|
|
162
|
+
WARNINGONCOMPLETE: "warning",
|
|
163
|
+
SHOWERROR: "error",
|
|
164
|
+
ERRORONCOMPLETE: "error",
|
|
165
|
+
DISPLAYTEXT: "feedback",
|
|
166
|
+
DISPLAYKEYVALUEPAIR: "feedback",
|
|
167
|
+
SENDMESSAGE: "message",
|
|
168
|
+
SCHEDULEMESSAGE: "message",
|
|
169
|
+
read: "read"
|
|
170
|
+
};
|
|
171
|
+
EFFECT_VISUALS = {
|
|
172
|
+
hide: {
|
|
173
|
+
tagClassName: "bg-dhis2-grey-200 text-dhis2-grey-900",
|
|
174
|
+
edgeStroke: "#494949",
|
|
175
|
+
shortLabel: "hide"
|
|
176
|
+
},
|
|
177
|
+
show: {
|
|
178
|
+
tagClassName: "bg-dhis2-green-100 text-dhis2-green-900",
|
|
179
|
+
edgeStroke: "#388e3c",
|
|
180
|
+
shortLabel: "show"
|
|
181
|
+
},
|
|
182
|
+
assign: {
|
|
183
|
+
tagClassName: "bg-dhis2-blue-100 text-dhis2-blue-900",
|
|
184
|
+
edgeStroke: "#1565c0",
|
|
185
|
+
shortLabel: "assign"
|
|
186
|
+
},
|
|
187
|
+
mandatory: {
|
|
188
|
+
tagClassName: "bg-dhis2-yellow-100 text-dhis2-yellow-900",
|
|
189
|
+
edgeStroke: "#e56408",
|
|
190
|
+
shortLabel: "required"
|
|
191
|
+
},
|
|
192
|
+
warning: {
|
|
193
|
+
tagClassName: "bg-dhis2-yellow-100 text-dhis2-yellow-900",
|
|
194
|
+
edgeStroke: "#ff8302",
|
|
195
|
+
shortLabel: "warn"
|
|
196
|
+
},
|
|
197
|
+
error: {
|
|
198
|
+
tagClassName: "bg-dhis2-red-100 text-dhis2-red-900",
|
|
199
|
+
edgeStroke: "#c62828",
|
|
200
|
+
shortLabel: "error"
|
|
201
|
+
},
|
|
202
|
+
feedback: {
|
|
203
|
+
tagClassName: "bg-dhis2-purple-100 text-dhis2-purple-900",
|
|
204
|
+
edgeStroke: "#9c27b0",
|
|
205
|
+
shortLabel: "feedback"
|
|
206
|
+
},
|
|
207
|
+
message: {
|
|
208
|
+
tagClassName: "bg-dhis2-teal-100 text-dhis2-teal-900",
|
|
209
|
+
edgeStroke: "#00796b",
|
|
210
|
+
shortLabel: "message"
|
|
211
|
+
},
|
|
212
|
+
read: {
|
|
213
|
+
tagClassName: "bg-dhis2-teal-100 text-dhis2-teal-900",
|
|
214
|
+
edgeStroke: "#00796b",
|
|
215
|
+
shortLabel: "read"
|
|
216
|
+
},
|
|
217
|
+
default: {
|
|
218
|
+
tagClassName: "bg-dhis2-grey-100 text-dhis2-grey-800",
|
|
219
|
+
edgeStroke: "#494949",
|
|
220
|
+
shortLabel: "effect"
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
exports.EFFECT_ICONS = {
|
|
224
|
+
hide: ui.IconViewOff16,
|
|
225
|
+
show: ui.IconView16,
|
|
226
|
+
assign: ui.IconEdit16,
|
|
227
|
+
mandatory: ui.IconStarFilled16,
|
|
228
|
+
warning: ui.IconWarningFilled16,
|
|
229
|
+
error: ui.IconErrorFilled16,
|
|
230
|
+
feedback: ui.IconMessages16,
|
|
231
|
+
message: ui.IconMail16,
|
|
232
|
+
read: ui.IconLink16,
|
|
233
|
+
default: ui.IconInfo16
|
|
234
|
+
};
|
|
235
|
+
TAG_LAYOUT_CLASS = "max-w-full break-words";
|
|
236
|
+
}
|
|
237
|
+
});
|
|
289
238
|
function translate(message, variables) {
|
|
290
239
|
return String(i18n__default.default.t(message, variables));
|
|
291
240
|
}
|
|
241
|
+
var init_i18n = __esm({
|
|
242
|
+
"src/i18n.ts"() {
|
|
243
|
+
}
|
|
244
|
+
});
|
|
292
245
|
function EffectBadge({ type, children, className = "" }) {
|
|
293
246
|
const variant = getEffectVariant(type);
|
|
294
|
-
const Icon = EFFECT_ICONS[variant];
|
|
247
|
+
const Icon = exports.EFFECT_ICONS[variant];
|
|
295
248
|
const tagProps = getEffectTagRenderProps(type);
|
|
296
249
|
const label = children ?? type;
|
|
297
250
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -305,23 +258,13 @@ function EffectBadge({ type, children, className = "" }) {
|
|
|
305
258
|
}
|
|
306
259
|
);
|
|
307
260
|
}
|
|
308
|
-
var LEGEND_VARIANTS = [
|
|
309
|
-
{ variant: "read", label: translate("Read") },
|
|
310
|
-
{ variant: "hide", label: translate("Hide") },
|
|
311
|
-
{ variant: "show", label: translate("Show") },
|
|
312
|
-
{ variant: "assign", label: translate("Assign") },
|
|
313
|
-
{ variant: "mandatory", label: translate("Required") },
|
|
314
|
-
{ variant: "warning", label: translate("Warning") },
|
|
315
|
-
{ variant: "error", label: translate("Error") },
|
|
316
|
-
{ variant: "feedback", label: translate("Feedback") }
|
|
317
|
-
];
|
|
318
261
|
function EffectLegend({ activeVariants }) {
|
|
319
262
|
const items = LEGEND_VARIANTS.filter((item) => activeVariants.has(item.variant));
|
|
320
263
|
if (!items.length) {
|
|
321
264
|
return null;
|
|
322
265
|
}
|
|
323
266
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-w-0 flex-wrap items-center gap-dp8 border-t border-dhis2-grey-200 px-dp12 py-dp8", children: items.map((item) => {
|
|
324
|
-
const Icon = EFFECT_ICONS[item.variant];
|
|
267
|
+
const Icon = exports.EFFECT_ICONS[item.variant];
|
|
325
268
|
const tagProps = getEffectTagRenderPropsForVariant(item.variant);
|
|
326
269
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
327
270
|
ui.Tag,
|
|
@@ -335,230 +278,74 @@ function EffectLegend({ activeVariants }) {
|
|
|
335
278
|
);
|
|
336
279
|
}) });
|
|
337
280
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
281
|
+
var LEGEND_VARIANTS;
|
|
282
|
+
var init_EffectBadge = __esm({
|
|
283
|
+
"src/EffectBadge.tsx"() {
|
|
284
|
+
init_effectStyles();
|
|
285
|
+
init_i18n();
|
|
286
|
+
LEGEND_VARIANTS = [
|
|
287
|
+
{ variant: "read", label: translate("Read") },
|
|
288
|
+
{ variant: "hide", label: translate("Hide") },
|
|
289
|
+
{ variant: "show", label: translate("Show") },
|
|
290
|
+
{ variant: "assign", label: translate("Assign") },
|
|
291
|
+
{ variant: "mandatory", label: translate("Required") },
|
|
292
|
+
{ variant: "warning", label: translate("Warning") },
|
|
293
|
+
{ variant: "error", label: translate("Error") },
|
|
294
|
+
{ variant: "feedback", label: translate("Feedback") }
|
|
295
|
+
];
|
|
348
296
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
// src/traceEntry.ts
|
|
300
|
+
function resolveGraphTraceEntry(entries, selectedEntryId) {
|
|
301
|
+
if (!entries.length) {
|
|
302
|
+
return null;
|
|
352
303
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
return translate("{{hours}}h ago", { hours });
|
|
304
|
+
if (selectedEntryId) {
|
|
305
|
+
return entries.find((entry) => entry.id === selectedEntryId) ?? entries[entries.length - 1];
|
|
356
306
|
}
|
|
357
|
-
return
|
|
307
|
+
return entries[entries.length - 1];
|
|
358
308
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
const resolved = labelLookup.resolveFieldName(id);
|
|
364
|
-
if (resolved !== id) {
|
|
365
|
-
return resolved;
|
|
366
|
-
}
|
|
309
|
+
function getActiveRuleIds(entries) {
|
|
310
|
+
const latest = resolveGraphTraceEntry(entries);
|
|
311
|
+
if (!latest) {
|
|
312
|
+
return /* @__PURE__ */ new Set();
|
|
367
313
|
}
|
|
368
|
-
return
|
|
314
|
+
return new Set(latest.ruleResults.map((result) => result.ruleId));
|
|
369
315
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
const resolved = labelLookup.resolveSectionName(id);
|
|
373
|
-
if (resolved !== id) {
|
|
374
|
-
return resolved;
|
|
375
|
-
}
|
|
316
|
+
var init_traceEntry = __esm({
|
|
317
|
+
"src/traceEntry.ts"() {
|
|
376
318
|
}
|
|
377
|
-
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// src/graphLayout.ts
|
|
322
|
+
function median(values) {
|
|
323
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
324
|
+
const mid = Math.floor(sorted.length / 2);
|
|
325
|
+
return sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
|
|
378
326
|
}
|
|
379
|
-
function
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
if (action.dataElement?.id) {
|
|
383
|
-
targetLabel = resolveFieldLabel(
|
|
384
|
-
action.dataElement.id,
|
|
385
|
-
action.dataElement.displayName,
|
|
386
|
-
labelLookup
|
|
387
|
-
);
|
|
388
|
-
} else if (action.trackedEntityAttribute?.id) {
|
|
389
|
-
targetLabel = resolveFieldLabel(
|
|
390
|
-
action.trackedEntityAttribute.id,
|
|
391
|
-
action.trackedEntityAttribute.displayName,
|
|
392
|
-
labelLookup
|
|
393
|
-
);
|
|
394
|
-
} else if (action.programStageSection?.id) {
|
|
395
|
-
targetLabel = resolveSectionLabel(
|
|
396
|
-
action.programStageSection.id,
|
|
397
|
-
action.programStageSection.displayName,
|
|
398
|
-
labelLookup
|
|
399
|
-
);
|
|
400
|
-
} else if (action.programSection?.id) {
|
|
401
|
-
targetLabel = resolveSectionLabel(
|
|
402
|
-
action.programSection.id,
|
|
403
|
-
action.programSection.displayName,
|
|
404
|
-
labelLookup
|
|
405
|
-
);
|
|
406
|
-
} else if (type === "DISPLAYTEXT" || type === "DISPLAYKEYVALUEPAIR") {
|
|
407
|
-
targetLabel = action.location ?? "feedback";
|
|
408
|
-
}
|
|
409
|
-
let detail;
|
|
410
|
-
if (action.data) {
|
|
411
|
-
detail = action.data;
|
|
412
|
-
}
|
|
413
|
-
if (action.content && (type === "DISPLAYTEXT" || type === "DISPLAYKEYVALUEPAIR")) {
|
|
414
|
-
detail = action.content;
|
|
327
|
+
function staggerOffset(indexInBucket) {
|
|
328
|
+
if (indexInBucket === 0) {
|
|
329
|
+
return 0;
|
|
415
330
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
// src/resolveProgramRulesList.ts
|
|
420
|
-
function sortByPriority(rules) {
|
|
421
|
-
return [...rules].sort((left, right) => (left.priority ?? 0) - (right.priority ?? 0));
|
|
331
|
+
const magnitude = Math.ceil(indexInBucket / 2) * STAGGER;
|
|
332
|
+
return indexInBucket % 2 === 1 ? magnitude : -magnitude;
|
|
422
333
|
}
|
|
423
|
-
function
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
priority: rule.priority,
|
|
429
|
-
programRuleActions: [...rule.programRuleActions ?? []],
|
|
430
|
-
programStageId: rule.programStageId
|
|
431
|
-
};
|
|
334
|
+
function columnFor(node) {
|
|
335
|
+
if (node.role === "target" && node.kind === "feedback") {
|
|
336
|
+
return FEEDBACK_COLUMN;
|
|
337
|
+
}
|
|
338
|
+
return ROLE_COLUMNS[node.role];
|
|
432
339
|
}
|
|
433
|
-
function
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
programRuleActions: rule.programRuleActions,
|
|
443
|
-
programStageId: rule.programStage?.id ?? null
|
|
444
|
-
})
|
|
445
|
-
)
|
|
446
|
-
);
|
|
447
|
-
}
|
|
448
|
-
return sortByPriority(
|
|
449
|
-
metadata.metadata.programRules.filter((rule) => Boolean(rule.id)).map(
|
|
450
|
-
(rule) => toCatalogRule({
|
|
451
|
-
id: rule.id,
|
|
452
|
-
name: rule.name ?? rule.id,
|
|
453
|
-
condition: rule.condition,
|
|
454
|
-
priority: rule.priority,
|
|
455
|
-
programRuleActions: rule.programRuleActions,
|
|
456
|
-
programStageId: rule.programStage?.id ?? null
|
|
457
|
-
})
|
|
458
|
-
)
|
|
459
|
-
);
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
// src/constants.ts
|
|
463
|
-
var DEFAULT_TRACE_MAX_ENTRIES = 200;
|
|
464
|
-
|
|
465
|
-
// src/traceStore.ts
|
|
466
|
-
function createRuleTraceStore(maxEntries = DEFAULT_TRACE_MAX_ENTRIES) {
|
|
467
|
-
let entries = [];
|
|
468
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
469
|
-
let disposeTraceSubscription = null;
|
|
470
|
-
const notify = () => {
|
|
471
|
-
for (const listener of listeners) {
|
|
472
|
-
listener();
|
|
473
|
-
}
|
|
474
|
-
};
|
|
475
|
-
return {
|
|
476
|
-
record(entry) {
|
|
477
|
-
entries = [...entries, entry].slice(-maxEntries);
|
|
478
|
-
notify();
|
|
479
|
-
},
|
|
480
|
-
getSnapshot() {
|
|
481
|
-
return entries;
|
|
482
|
-
},
|
|
483
|
-
subscribe(listener) {
|
|
484
|
-
listeners.add(listener);
|
|
485
|
-
return () => {
|
|
486
|
-
listeners.delete(listener);
|
|
487
|
-
};
|
|
488
|
-
},
|
|
489
|
-
dispose() {
|
|
490
|
-
disposeTraceSubscription?.();
|
|
491
|
-
disposeTraceSubscription = null;
|
|
492
|
-
listeners.clear();
|
|
493
|
-
},
|
|
494
|
-
bindTraceSubscription(unsubscribe) {
|
|
495
|
-
disposeTraceSubscription = unsubscribe;
|
|
496
|
-
}
|
|
497
|
-
};
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
// src/attach.ts
|
|
501
|
-
function attachRuleDevtools(formStore, options) {
|
|
502
|
-
const store = createRuleTraceStore(
|
|
503
|
-
DEFAULT_TRACE_MAX_ENTRIES
|
|
504
|
-
);
|
|
505
|
-
store.bindTraceSubscription(formStore.subscribeTrace(store.record));
|
|
506
|
-
return store;
|
|
507
|
-
}
|
|
508
|
-
var RuleTraceContext = react.createContext(null);
|
|
509
|
-
function RuleDevtoolsScope({ formStore, children }) {
|
|
510
|
-
const traceStore = react.useMemo(() => attachRuleDevtools(formStore), [formStore]);
|
|
511
|
-
react.useEffect(() => {
|
|
512
|
-
return () => {
|
|
513
|
-
traceStore.dispose();
|
|
514
|
-
};
|
|
515
|
-
}, [traceStore]);
|
|
516
|
-
return /* @__PURE__ */ jsxRuntime.jsx(RuleTraceContext.Provider, { value: traceStore, children });
|
|
517
|
-
}
|
|
518
|
-
function useRuleTraceStore() {
|
|
519
|
-
const store = react.useContext(RuleTraceContext);
|
|
520
|
-
if (!store) {
|
|
521
|
-
throw new Error("useRuleTraceStore must be used within RuleDevtoolsScope");
|
|
522
|
-
}
|
|
523
|
-
return store;
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
// src/graphLayout.ts
|
|
527
|
-
var ROLE_COLUMNS = {
|
|
528
|
-
source: 0,
|
|
529
|
-
rule: 280,
|
|
530
|
-
target: 560
|
|
531
|
-
};
|
|
532
|
-
var FEEDBACK_COLUMN = 720;
|
|
533
|
-
var LANE_HEIGHT = 104;
|
|
534
|
-
var STAGGER = 24;
|
|
535
|
-
function median(values) {
|
|
536
|
-
const sorted = [...values].sort((a, b) => a - b);
|
|
537
|
-
const mid = Math.floor(sorted.length / 2);
|
|
538
|
-
return sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
|
|
539
|
-
}
|
|
540
|
-
function staggerOffset(indexInBucket) {
|
|
541
|
-
if (indexInBucket === 0) {
|
|
542
|
-
return 0;
|
|
543
|
-
}
|
|
544
|
-
const magnitude = Math.ceil(indexInBucket / 2) * STAGGER;
|
|
545
|
-
return indexInBucket % 2 === 1 ? magnitude : -magnitude;
|
|
546
|
-
}
|
|
547
|
-
function columnFor(node) {
|
|
548
|
-
if (node.role === "target" && node.kind === "feedback") {
|
|
549
|
-
return FEEDBACK_COLUMN;
|
|
550
|
-
}
|
|
551
|
-
return ROLE_COLUMNS[node.role];
|
|
552
|
-
}
|
|
553
|
-
function prepareFlowGraph(graph) {
|
|
554
|
-
const readSources = /* @__PURE__ */ new Set();
|
|
555
|
-
const effectTargets = /* @__PURE__ */ new Set();
|
|
556
|
-
for (const edge of graph.edges) {
|
|
557
|
-
if (edge.effectType === "read") {
|
|
558
|
-
readSources.add(edge.source);
|
|
559
|
-
} else {
|
|
560
|
-
effectTargets.add(edge.target);
|
|
561
|
-
}
|
|
340
|
+
function prepareFlowGraph(graph) {
|
|
341
|
+
const readSources = /* @__PURE__ */ new Set();
|
|
342
|
+
const effectTargets = /* @__PURE__ */ new Set();
|
|
343
|
+
for (const edge of graph.edges) {
|
|
344
|
+
if (edge.effectType === "read") {
|
|
345
|
+
readSources.add(edge.source);
|
|
346
|
+
} else {
|
|
347
|
+
effectTargets.add(edge.target);
|
|
348
|
+
}
|
|
562
349
|
}
|
|
563
350
|
const flowNodes = [];
|
|
564
351
|
const endpointByGraphId = /* @__PURE__ */ new Map();
|
|
@@ -651,59 +438,57 @@ function computeGraphLayout(flowNodes, flowEdges) {
|
|
|
651
438
|
}
|
|
652
439
|
return positions;
|
|
653
440
|
}
|
|
441
|
+
var ROLE_COLUMNS, FEEDBACK_COLUMN, LANE_HEIGHT, STAGGER;
|
|
442
|
+
var init_graphLayout = __esm({
|
|
443
|
+
"src/graphLayout.ts"() {
|
|
444
|
+
ROLE_COLUMNS = {
|
|
445
|
+
source: 0,
|
|
446
|
+
rule: 280,
|
|
447
|
+
target: 560
|
|
448
|
+
};
|
|
449
|
+
FEEDBACK_COLUMN = 720;
|
|
450
|
+
LANE_HEIGHT = 104;
|
|
451
|
+
STAGGER = 24;
|
|
452
|
+
({
|
|
453
|
+
field: ROLE_COLUMNS.source,
|
|
454
|
+
rule: ROLE_COLUMNS.rule,
|
|
455
|
+
section: ROLE_COLUMNS.target,
|
|
456
|
+
feedback: FEEDBACK_COLUMN
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
});
|
|
654
460
|
|
|
655
461
|
// src/graphNodeStyles.ts
|
|
656
|
-
var GRAPH_NODE_CLASSES = {
|
|
657
|
-
field: "border-dhis2-blue-500",
|
|
658
|
-
rule: "border-dhis2-teal-600 bg-dhis2-teal-050",
|
|
659
|
-
section: "border-dhis2-grey-600",
|
|
660
|
-
feedback: "border-dhis2-purple-500"
|
|
661
|
-
};
|
|
662
|
-
var LEGEND_SWATCH_CLASSES = {
|
|
663
|
-
field: "bg-dhis2-blue-500",
|
|
664
|
-
rule: "bg-dhis2-teal-600",
|
|
665
|
-
section: "bg-dhis2-grey-600",
|
|
666
|
-
feedback: "bg-dhis2-purple-500"
|
|
667
|
-
};
|
|
668
462
|
function getGraphNodeClassName(kind, highlighted) {
|
|
669
463
|
return `p-dp8 rounded border bg-white min-w-28 max-w-[180px] text-xs shadow-[0_1px_2px_rgb(0_0_0/6%)] transition-opacity duration-150 ease-out cursor-grab active:cursor-grabbing ${GRAPH_NODE_CLASSES[kind]} ${highlighted ? "" : "opacity-35"}`;
|
|
670
464
|
}
|
|
671
465
|
function getLegendSwatchClassName(kind) {
|
|
672
466
|
return `size-2.5 shrink-0 rounded-sm ${LEGEND_SWATCH_CLASSES[kind]}`;
|
|
673
467
|
}
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
}
|
|
690
|
-
return new Set(latest.ruleResults.map((result) => result.ruleId));
|
|
691
|
-
}
|
|
692
|
-
var KIND_LABELS = {
|
|
693
|
-
field: "Field",
|
|
694
|
-
rule: "Rule",
|
|
695
|
-
section: "Section",
|
|
696
|
-
feedback: "Feedback"
|
|
697
|
-
};
|
|
698
|
-
var READ_LABEL_EDGE_THRESHOLD = 4;
|
|
699
|
-
var DEFAULT_EDGE_OPTIONS = {
|
|
700
|
-
type: "ruleGraphEdge",
|
|
701
|
-
style: { stroke: getEffectEdgeStroke("default") },
|
|
702
|
-
markerEnd: {
|
|
703
|
-
type: react$1.MarkerType.ArrowClosed,
|
|
704
|
-
color: getEffectEdgeStroke("default")
|
|
468
|
+
var GRAPH_NODE_CLASSES, LEGEND_SWATCH_CLASSES;
|
|
469
|
+
var init_graphNodeStyles = __esm({
|
|
470
|
+
"src/graphNodeStyles.ts"() {
|
|
471
|
+
GRAPH_NODE_CLASSES = {
|
|
472
|
+
field: "border-dhis2-blue-500",
|
|
473
|
+
rule: "border-dhis2-teal-600 bg-dhis2-teal-050",
|
|
474
|
+
section: "border-dhis2-grey-600",
|
|
475
|
+
feedback: "border-dhis2-purple-500"
|
|
476
|
+
};
|
|
477
|
+
LEGEND_SWATCH_CLASSES = {
|
|
478
|
+
field: "bg-dhis2-blue-500",
|
|
479
|
+
rule: "bg-dhis2-teal-600",
|
|
480
|
+
section: "bg-dhis2-grey-600",
|
|
481
|
+
feedback: "bg-dhis2-purple-500"
|
|
482
|
+
};
|
|
705
483
|
}
|
|
706
|
-
};
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
// src/RuleGraphView.tsx
|
|
487
|
+
var RuleGraphView_exports = {};
|
|
488
|
+
__export(RuleGraphView_exports, {
|
|
489
|
+
RuleGraphView: () => RuleGraphView,
|
|
490
|
+
toFlowGraph: () => toFlowGraph
|
|
491
|
+
});
|
|
707
492
|
function RuleGraphNode({ data }) {
|
|
708
493
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: getGraphNodeClassName(data.kind, data.highlighted), children: [
|
|
709
494
|
/* @__PURE__ */ jsxRuntime.jsx(react$1.Handle, { type: "target", position: react$1.Position.Left }),
|
|
@@ -713,9 +498,6 @@ function RuleGraphNode({ data }) {
|
|
|
713
498
|
/* @__PURE__ */ jsxRuntime.jsx(react$1.Handle, { type: "source", position: react$1.Position.Right })
|
|
714
499
|
] });
|
|
715
500
|
}
|
|
716
|
-
var nodeTypes = {
|
|
717
|
-
ruleGraphNode: react.memo(RuleGraphNode)
|
|
718
|
-
};
|
|
719
501
|
function RuleGraphEdge({
|
|
720
502
|
id,
|
|
721
503
|
sourceX,
|
|
@@ -777,9 +559,6 @@ function RuleGraphEdge({
|
|
|
777
559
|
) }) : null
|
|
778
560
|
] });
|
|
779
561
|
}
|
|
780
|
-
var edgeTypes = {
|
|
781
|
-
ruleGraphEdge: react.memo(RuleGraphEdge)
|
|
782
|
-
};
|
|
783
562
|
function effectNodeKey(effect) {
|
|
784
563
|
const kind = effect.type === "HIDESECTION" ? "section" : effect.type === "DISPLAYTEXT" || effect.type === "DISPLAYKEYVALUEPAIR" ? "feedback" : "field";
|
|
785
564
|
return `${kind}:${effect.targetId}`;
|
|
@@ -809,15 +588,6 @@ function ruleGraphKeys(entry, ruleId) {
|
|
|
809
588
|
}
|
|
810
589
|
return keys;
|
|
811
590
|
}
|
|
812
|
-
var formatDisplayValue = (value) => {
|
|
813
|
-
if (value === void 0 || value === null || value === "") {
|
|
814
|
-
return void 0;
|
|
815
|
-
}
|
|
816
|
-
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
817
|
-
return String(value);
|
|
818
|
-
}
|
|
819
|
-
return JSON.stringify(value);
|
|
820
|
-
};
|
|
821
591
|
function toFlowGraph(graph, fieldState, formValues, highlightedKeys) {
|
|
822
592
|
const { nodes: flowNodes, edges: flowEdges } = prepareFlowGraph(graph);
|
|
823
593
|
const positions = computeGraphLayout(flowNodes, flowEdges);
|
|
@@ -896,210 +666,1015 @@ function GraphToolbar({
|
|
|
896
666
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shrink-0 border-b border-dhis2-grey-200 bg-white", children: [
|
|
897
667
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-dp8 px-dp12 py-dp8", children: [
|
|
898
668
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
899
|
-
"div",
|
|
669
|
+
"div",
|
|
670
|
+
{
|
|
671
|
+
className: "flex min-w-0 flex-wrap items-center gap-x-dp12 gap-y-dp8",
|
|
672
|
+
"aria-hidden": "true",
|
|
673
|
+
children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
674
|
+
"span",
|
|
675
|
+
{
|
|
676
|
+
className: "inline-flex items-center gap-dp8 text-xs text-dhis2-grey-800",
|
|
677
|
+
children: [
|
|
678
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: getLegendSwatchClassName(item.kind) }),
|
|
679
|
+
item.label
|
|
680
|
+
]
|
|
681
|
+
},
|
|
682
|
+
item.kind
|
|
683
|
+
))
|
|
684
|
+
}
|
|
685
|
+
),
|
|
686
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-dp8", children: [
|
|
687
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs tabular-nums text-dhis2-grey-600", children: translate("{{nodes}} nodes \xB7 {{edges}} edges", {
|
|
688
|
+
nodes: nodeCount,
|
|
689
|
+
edges: edgeCount
|
|
690
|
+
}) }),
|
|
691
|
+
headerActions
|
|
692
|
+
] })
|
|
693
|
+
] }),
|
|
694
|
+
/* @__PURE__ */ jsxRuntime.jsx(EffectLegend, { activeVariants: activeEffectVariants })
|
|
695
|
+
] });
|
|
696
|
+
}
|
|
697
|
+
function FitViewOnChange({
|
|
698
|
+
layoutKey,
|
|
699
|
+
nodeCount,
|
|
700
|
+
edgeCount
|
|
701
|
+
}) {
|
|
702
|
+
const { fitView } = react$1.useReactFlow();
|
|
703
|
+
react.useEffect(() => {
|
|
704
|
+
const frameId = requestAnimationFrame(() => {
|
|
705
|
+
void fitView({ padding: 0.2, duration: 150 });
|
|
706
|
+
});
|
|
707
|
+
return () => {
|
|
708
|
+
cancelAnimationFrame(frameId);
|
|
709
|
+
};
|
|
710
|
+
}, [edgeCount, fitView, layoutKey, nodeCount]);
|
|
711
|
+
return null;
|
|
712
|
+
}
|
|
713
|
+
function mergeNodePositions(next, current) {
|
|
714
|
+
const currentById = new Map(current.map((node) => [node.id, node]));
|
|
715
|
+
return next.map((node) => {
|
|
716
|
+
const existing = currentById.get(node.id);
|
|
717
|
+
if (existing) {
|
|
718
|
+
return { ...node, position: existing.position };
|
|
719
|
+
}
|
|
720
|
+
return node;
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
function RuleGraphCanvas({
|
|
724
|
+
nodes: layoutNodes,
|
|
725
|
+
edges: layoutEdges,
|
|
726
|
+
layoutKey
|
|
727
|
+
}) {
|
|
728
|
+
const [nodes, setNodes, onNodesChange] = react$1.useNodesState(layoutNodes);
|
|
729
|
+
const [edges, setEdges, onEdgesChange] = react$1.useEdgesState(layoutEdges);
|
|
730
|
+
react.useEffect(() => {
|
|
731
|
+
setNodes((current) => mergeNodePositions(layoutNodes, current));
|
|
732
|
+
}, [layoutNodes, setNodes]);
|
|
733
|
+
react.useEffect(() => {
|
|
734
|
+
setEdges(layoutEdges);
|
|
735
|
+
}, [layoutEdges, setEdges]);
|
|
736
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
737
|
+
react$1.ReactFlow,
|
|
738
|
+
{
|
|
739
|
+
nodes,
|
|
740
|
+
edges,
|
|
741
|
+
nodeTypes,
|
|
742
|
+
edgeTypes,
|
|
743
|
+
onNodesChange,
|
|
744
|
+
onEdgesChange,
|
|
745
|
+
nodesDraggable: true,
|
|
746
|
+
defaultEdgeOptions: DEFAULT_EDGE_OPTIONS,
|
|
747
|
+
elevateEdgesOnSelect: true,
|
|
748
|
+
fitView: true,
|
|
749
|
+
proOptions: { hideAttribution: true },
|
|
750
|
+
className: "h-full w-full",
|
|
751
|
+
children: [
|
|
752
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
753
|
+
FitViewOnChange,
|
|
754
|
+
{
|
|
755
|
+
layoutKey,
|
|
756
|
+
nodeCount: nodes.length,
|
|
757
|
+
edgeCount: edges.length
|
|
758
|
+
}
|
|
759
|
+
),
|
|
760
|
+
/* @__PURE__ */ jsxRuntime.jsx(react$1.Background, { gap: 16, size: 1 }),
|
|
761
|
+
/* @__PURE__ */ jsxRuntime.jsx(react$1.Controls, { showInteractive: false })
|
|
762
|
+
]
|
|
763
|
+
}
|
|
764
|
+
);
|
|
765
|
+
}
|
|
766
|
+
function RuleGraphView({
|
|
767
|
+
entries,
|
|
768
|
+
fieldState,
|
|
769
|
+
formValues,
|
|
770
|
+
selectedEntryId,
|
|
771
|
+
highlightRuleId,
|
|
772
|
+
labelLookup,
|
|
773
|
+
className,
|
|
774
|
+
layoutKey,
|
|
775
|
+
headerActions,
|
|
776
|
+
minHeightClassName = "min-h-[360px]"
|
|
777
|
+
}) {
|
|
778
|
+
const graphEntry = react.useMemo(
|
|
779
|
+
() => resolveGraphTraceEntry(entries, selectedEntryId),
|
|
780
|
+
[entries, selectedEntryId]
|
|
781
|
+
);
|
|
782
|
+
const graph = react.useMemo(
|
|
783
|
+
() => graphEntry ? buildGraphFromTrace([graphEntry], labelLookup) : { nodes: [], edges: [] },
|
|
784
|
+
[graphEntry, labelLookup]
|
|
785
|
+
);
|
|
786
|
+
const highlightedKeys = react.useMemo(() => {
|
|
787
|
+
if (!graphEntry || !selectedEntryId && !highlightRuleId) {
|
|
788
|
+
return null;
|
|
789
|
+
}
|
|
790
|
+
const keys = /* @__PURE__ */ new Set();
|
|
791
|
+
if (selectedEntryId) {
|
|
792
|
+
for (const key of entryGraphKeys(graphEntry)) {
|
|
793
|
+
keys.add(key);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
if (highlightRuleId) {
|
|
797
|
+
for (const key of ruleGraphKeys(graphEntry, highlightRuleId)) {
|
|
798
|
+
keys.add(key);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
return keys;
|
|
802
|
+
}, [graphEntry, highlightRuleId, selectedEntryId]);
|
|
803
|
+
const { nodes, edges } = react.useMemo(
|
|
804
|
+
() => toFlowGraph(graph, fieldState, formValues, highlightedKeys),
|
|
805
|
+
[graph, fieldState, formValues, highlightedKeys]
|
|
806
|
+
);
|
|
807
|
+
const activeEffectVariants = react.useMemo(() => {
|
|
808
|
+
const variants = /* @__PURE__ */ new Set();
|
|
809
|
+
for (const edge of graph.edges) {
|
|
810
|
+
if (edge.effectType) {
|
|
811
|
+
variants.add(getEffectVariant(edge.effectType));
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
return variants;
|
|
815
|
+
}, [graph.edges]);
|
|
816
|
+
if (!graph.nodes.length) {
|
|
817
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-dp8", children: /* @__PURE__ */ jsxRuntime.jsx(ui.NoticeBox, { title: translate("No rule relationships yet"), children: translate(
|
|
818
|
+
"Interact with the form to build the dependency graph. Only rules active in the latest evaluation are shown. Select a trace entry to inspect a past evaluation. Connections flow Field \u2192 Rule \u2192 Target (read / effect)."
|
|
819
|
+
) }) });
|
|
820
|
+
}
|
|
821
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
822
|
+
"div",
|
|
823
|
+
{
|
|
824
|
+
className: `flex h-full min-h-[480px] flex-1 flex-col overflow-hidden rounded-md border border-dhis2-grey-200 bg-white ${className ?? ""}`,
|
|
825
|
+
children: [
|
|
826
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
827
|
+
GraphToolbar,
|
|
828
|
+
{
|
|
829
|
+
nodeCount: graph.nodes.length,
|
|
830
|
+
edgeCount: graph.edges.length,
|
|
831
|
+
headerActions,
|
|
832
|
+
activeEffectVariants
|
|
833
|
+
}
|
|
834
|
+
),
|
|
835
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
836
|
+
"div",
|
|
837
|
+
{
|
|
838
|
+
className: `${minHeightClassName} flex-1 bg-white`,
|
|
839
|
+
style: { width: "100%", height: "100%" },
|
|
840
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(react$1.ReactFlowProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(RuleGraphCanvas, { nodes, edges, layoutKey }) })
|
|
841
|
+
}
|
|
842
|
+
)
|
|
843
|
+
]
|
|
844
|
+
}
|
|
845
|
+
);
|
|
846
|
+
}
|
|
847
|
+
var KIND_LABELS, READ_LABEL_EDGE_THRESHOLD, DEFAULT_EDGE_OPTIONS, nodeTypes, edgeTypes, formatDisplayValue;
|
|
848
|
+
var init_RuleGraphView = __esm({
|
|
849
|
+
"src/RuleGraphView.tsx"() {
|
|
850
|
+
init_buildGraph();
|
|
851
|
+
init_graphLayout();
|
|
852
|
+
init_EffectBadge();
|
|
853
|
+
init_effectStyles();
|
|
854
|
+
init_graphNodeStyles();
|
|
855
|
+
init_i18n();
|
|
856
|
+
init_traceEntry();
|
|
857
|
+
KIND_LABELS = {
|
|
858
|
+
field: "Field",
|
|
859
|
+
rule: "Rule",
|
|
860
|
+
section: "Section",
|
|
861
|
+
feedback: "Feedback"
|
|
862
|
+
};
|
|
863
|
+
READ_LABEL_EDGE_THRESHOLD = 4;
|
|
864
|
+
DEFAULT_EDGE_OPTIONS = {
|
|
865
|
+
type: "ruleGraphEdge",
|
|
866
|
+
style: { stroke: getEffectEdgeStroke("default") },
|
|
867
|
+
markerEnd: {
|
|
868
|
+
type: react$1.MarkerType.ArrowClosed,
|
|
869
|
+
color: getEffectEdgeStroke("default")
|
|
870
|
+
}
|
|
871
|
+
};
|
|
872
|
+
nodeTypes = {
|
|
873
|
+
ruleGraphNode: react.memo(RuleGraphNode)
|
|
874
|
+
};
|
|
875
|
+
edgeTypes = {
|
|
876
|
+
ruleGraphEdge: react.memo(RuleGraphEdge)
|
|
877
|
+
};
|
|
878
|
+
formatDisplayValue = (value) => {
|
|
879
|
+
if (value === void 0 || value === null || value === "") {
|
|
880
|
+
return void 0;
|
|
881
|
+
}
|
|
882
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
883
|
+
return String(value);
|
|
884
|
+
}
|
|
885
|
+
return JSON.stringify(value);
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
});
|
|
889
|
+
|
|
890
|
+
// src/RuleGraphModal.tsx
|
|
891
|
+
var RuleGraphModal_exports = {};
|
|
892
|
+
__export(RuleGraphModal_exports, {
|
|
893
|
+
RuleGraphModal: () => RuleGraphModal
|
|
894
|
+
});
|
|
895
|
+
function RuleGraphModal({
|
|
896
|
+
open,
|
|
897
|
+
onClose,
|
|
898
|
+
subtitle,
|
|
899
|
+
layoutKey,
|
|
900
|
+
...graphProps
|
|
901
|
+
}) {
|
|
902
|
+
if (!open) {
|
|
903
|
+
return null;
|
|
904
|
+
}
|
|
905
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Modal, { large: true, fluid: true, onClose, children: [
|
|
906
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.ModalTitle, { children: [
|
|
907
|
+
translate("Rule dependency graph"),
|
|
908
|
+
subtitle ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-dp4 block text-sm font-normal text-dhis2-grey-700", children: subtitle }) : null
|
|
909
|
+
] }),
|
|
910
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.ModalContent, { className: "p-0", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[min(85vh,900px)] w-full min-w-[min(1200px,92vw)]", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
911
|
+
RuleGraphView,
|
|
912
|
+
{
|
|
913
|
+
...graphProps,
|
|
914
|
+
layoutKey: layoutKey ?? "modal",
|
|
915
|
+
className: "h-full min-h-0 rounded-none border-0",
|
|
916
|
+
minHeightClassName: "min-h-0 h-full"
|
|
917
|
+
}
|
|
918
|
+
) }) })
|
|
919
|
+
] });
|
|
920
|
+
}
|
|
921
|
+
var init_RuleGraphModal = __esm({
|
|
922
|
+
"src/RuleGraphModal.tsx"() {
|
|
923
|
+
init_i18n();
|
|
924
|
+
init_RuleGraphView();
|
|
925
|
+
}
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
// src/RulesPanel.tsx
|
|
929
|
+
init_buildGraph();
|
|
930
|
+
var createLookup = (rules, fields, sections, stages) => ({
|
|
931
|
+
resolveRuleName: (id) => rules.get(id) ?? id,
|
|
932
|
+
resolveFieldName: (id) => fields.get(id) ?? id,
|
|
933
|
+
resolveSectionName: (id) => sections.get(id) ?? id,
|
|
934
|
+
resolveStageName: (id) => stages.get(id) ?? id
|
|
935
|
+
});
|
|
936
|
+
function buildEventLookup(source) {
|
|
937
|
+
const rules = /* @__PURE__ */ new Map();
|
|
938
|
+
const fields = /* @__PURE__ */ new Map();
|
|
939
|
+
const sections = /* @__PURE__ */ new Map();
|
|
940
|
+
const stages = /* @__PURE__ */ new Map();
|
|
941
|
+
for (const rule of source.metadata.programRules) {
|
|
942
|
+
if (!rule.id) {
|
|
943
|
+
continue;
|
|
944
|
+
}
|
|
945
|
+
rules.set(rule.id, rule.displayName ?? rule.id);
|
|
946
|
+
}
|
|
947
|
+
const stage = dhis2FormUtilsMetadata.selectProgramStage(
|
|
948
|
+
source.metadata,
|
|
949
|
+
source.programStageId
|
|
950
|
+
) ?? {
|
|
951
|
+
id: source.programStageId,
|
|
952
|
+
programStageDataElements: [],
|
|
953
|
+
programStageSections: []
|
|
954
|
+
};
|
|
955
|
+
for (const psde of stage.programStageDataElements ?? []) {
|
|
956
|
+
const de = psde.dataElement;
|
|
957
|
+
if (!de?.id) {
|
|
958
|
+
continue;
|
|
959
|
+
}
|
|
960
|
+
fields.set(de.id, de.displayFormName ?? de.displayName ?? de.id);
|
|
961
|
+
}
|
|
962
|
+
for (const section of stage.programStageSections ?? []) {
|
|
963
|
+
sections.set(section.id, section.displayName ?? section.id);
|
|
964
|
+
}
|
|
965
|
+
for (const programStage of source.metadata.programStages) {
|
|
966
|
+
if (!programStage.id) {
|
|
967
|
+
continue;
|
|
968
|
+
}
|
|
969
|
+
stages.set(programStage.id, programStage.displayName ?? programStage.id);
|
|
970
|
+
}
|
|
971
|
+
return createLookup(rules, fields, sections, stages);
|
|
972
|
+
}
|
|
973
|
+
function buildTrackerLookup(source) {
|
|
974
|
+
const rules = /* @__PURE__ */ new Map();
|
|
975
|
+
const fields = /* @__PURE__ */ new Map();
|
|
976
|
+
const sections = /* @__PURE__ */ new Map();
|
|
977
|
+
const stages = /* @__PURE__ */ new Map();
|
|
978
|
+
for (const rule of source.metadata.programRules) {
|
|
979
|
+
if (!rule.id) {
|
|
980
|
+
continue;
|
|
981
|
+
}
|
|
982
|
+
rules.set(rule.id, rule.name ?? rule.id);
|
|
983
|
+
}
|
|
984
|
+
for (const ptea of source.metadata.programTrackedEntityAttributes) {
|
|
985
|
+
const tea = ptea.trackedEntityAttribute;
|
|
986
|
+
if (!tea.id) {
|
|
987
|
+
continue;
|
|
988
|
+
}
|
|
989
|
+
fields.set(tea.id, tea.formName ?? tea.displayName ?? tea.id);
|
|
990
|
+
}
|
|
991
|
+
for (const section of source.metadata.programSections ?? []) {
|
|
992
|
+
sections.set(section.id, section.displayName ?? section.id);
|
|
993
|
+
}
|
|
994
|
+
for (const programStage of source.programStages ?? []) {
|
|
995
|
+
if (!programStage.id) {
|
|
996
|
+
continue;
|
|
997
|
+
}
|
|
998
|
+
stages.set(programStage.id, programStage.displayName ?? programStage.id);
|
|
999
|
+
}
|
|
1000
|
+
return createLookup(rules, fields, sections, stages);
|
|
1001
|
+
}
|
|
1002
|
+
function createLabelLookup(source) {
|
|
1003
|
+
return source.formKind === "event" ? buildEventLookup(source) : buildTrackerLookup(source);
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
// src/RulesPanel.tsx
|
|
1007
|
+
init_EffectBadge();
|
|
1008
|
+
|
|
1009
|
+
// src/formatAgo.ts
|
|
1010
|
+
init_i18n();
|
|
1011
|
+
function formatAgo(timestamp, now = Date.now()) {
|
|
1012
|
+
const diffMs = Math.max(0, now - timestamp);
|
|
1013
|
+
if (diffMs < 5e3) {
|
|
1014
|
+
return translate("just now");
|
|
1015
|
+
}
|
|
1016
|
+
const seconds = Math.floor(diffMs / 1e3);
|
|
1017
|
+
if (seconds < 60) {
|
|
1018
|
+
return translate("{{seconds}}s ago", { seconds });
|
|
1019
|
+
}
|
|
1020
|
+
const minutes = Math.floor(seconds / 60);
|
|
1021
|
+
if (minutes < 60) {
|
|
1022
|
+
return translate("{{minutes}}m ago", { minutes });
|
|
1023
|
+
}
|
|
1024
|
+
const hours = Math.floor(minutes / 60);
|
|
1025
|
+
if (hours < 24) {
|
|
1026
|
+
return translate("{{hours}}h ago", { hours });
|
|
1027
|
+
}
|
|
1028
|
+
return new Date(timestamp).toLocaleString();
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
// src/formatRuleActionSummary.ts
|
|
1032
|
+
function resolveFieldLabel(id, displayName, labelLookup) {
|
|
1033
|
+
if (labelLookup) {
|
|
1034
|
+
const resolved = labelLookup.resolveFieldName(id);
|
|
1035
|
+
if (resolved !== id) {
|
|
1036
|
+
return resolved;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
return displayName ?? id;
|
|
1040
|
+
}
|
|
1041
|
+
function resolveSectionLabel(id, displayName, labelLookup) {
|
|
1042
|
+
if (labelLookup) {
|
|
1043
|
+
const resolved = labelLookup.resolveSectionName(id);
|
|
1044
|
+
if (resolved !== id) {
|
|
1045
|
+
return resolved;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
return displayName ?? id;
|
|
1049
|
+
}
|
|
1050
|
+
function formatRuleActionSummary(action, labelLookup) {
|
|
1051
|
+
const type = action.programRuleActionType ?? "UNKNOWN";
|
|
1052
|
+
let targetLabel = "";
|
|
1053
|
+
if (action.dataElement?.id) {
|
|
1054
|
+
targetLabel = resolveFieldLabel(
|
|
1055
|
+
action.dataElement.id,
|
|
1056
|
+
action.dataElement.displayName,
|
|
1057
|
+
labelLookup
|
|
1058
|
+
);
|
|
1059
|
+
} else if (action.trackedEntityAttribute?.id) {
|
|
1060
|
+
targetLabel = resolveFieldLabel(
|
|
1061
|
+
action.trackedEntityAttribute.id,
|
|
1062
|
+
action.trackedEntityAttribute.displayName,
|
|
1063
|
+
labelLookup
|
|
1064
|
+
);
|
|
1065
|
+
} else if (action.programStageSection?.id) {
|
|
1066
|
+
targetLabel = resolveSectionLabel(
|
|
1067
|
+
action.programStageSection.id,
|
|
1068
|
+
action.programStageSection.displayName,
|
|
1069
|
+
labelLookup
|
|
1070
|
+
);
|
|
1071
|
+
} else if (action.programSection?.id) {
|
|
1072
|
+
targetLabel = resolveSectionLabel(
|
|
1073
|
+
action.programSection.id,
|
|
1074
|
+
action.programSection.displayName,
|
|
1075
|
+
labelLookup
|
|
1076
|
+
);
|
|
1077
|
+
} else if (type === "DISPLAYTEXT" || type === "DISPLAYKEYVALUEPAIR") {
|
|
1078
|
+
targetLabel = action.location ?? "feedback";
|
|
1079
|
+
}
|
|
1080
|
+
let detail;
|
|
1081
|
+
if (action.data) {
|
|
1082
|
+
detail = action.data;
|
|
1083
|
+
}
|
|
1084
|
+
if (action.content && (type === "DISPLAYTEXT" || type === "DISPLAYKEYVALUEPAIR")) {
|
|
1085
|
+
detail = action.content;
|
|
1086
|
+
}
|
|
1087
|
+
return { type, targetLabel, detail };
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
// src/RulesPanel.tsx
|
|
1091
|
+
init_i18n();
|
|
1092
|
+
|
|
1093
|
+
// src/resolveProgramRulesList.ts
|
|
1094
|
+
function sortByPriority(rules) {
|
|
1095
|
+
return [...rules].sort((left, right) => (left.priority ?? 0) - (right.priority ?? 0));
|
|
1096
|
+
}
|
|
1097
|
+
function toCatalogRule(rule) {
|
|
1098
|
+
return {
|
|
1099
|
+
id: rule.id,
|
|
1100
|
+
name: rule.name,
|
|
1101
|
+
condition: rule.condition,
|
|
1102
|
+
priority: rule.priority,
|
|
1103
|
+
programRuleActions: [...rule.programRuleActions ?? []],
|
|
1104
|
+
programStageId: rule.programStageId
|
|
1105
|
+
};
|
|
1106
|
+
}
|
|
1107
|
+
function resolveProgramRulesList(metadata) {
|
|
1108
|
+
if (metadata.formKind === "event") {
|
|
1109
|
+
return sortByPriority(
|
|
1110
|
+
metadata.metadata.programRules.filter((rule) => Boolean(rule.id)).map(
|
|
1111
|
+
(rule) => toCatalogRule({
|
|
1112
|
+
id: rule.id,
|
|
1113
|
+
name: rule.displayName ?? rule.id,
|
|
1114
|
+
condition: rule.condition,
|
|
1115
|
+
priority: rule.priority,
|
|
1116
|
+
programRuleActions: rule.programRuleActions,
|
|
1117
|
+
programStageId: rule.programStage?.id ?? null
|
|
1118
|
+
})
|
|
1119
|
+
)
|
|
1120
|
+
);
|
|
1121
|
+
}
|
|
1122
|
+
return sortByPriority(
|
|
1123
|
+
metadata.metadata.programRules.filter((rule) => Boolean(rule.id)).map(
|
|
1124
|
+
(rule) => toCatalogRule({
|
|
1125
|
+
id: rule.id,
|
|
1126
|
+
name: rule.name ?? rule.id,
|
|
1127
|
+
condition: rule.condition,
|
|
1128
|
+
priority: rule.priority,
|
|
1129
|
+
programRuleActions: rule.programRuleActions,
|
|
1130
|
+
programStageId: rule.programStage?.id ?? null
|
|
1131
|
+
})
|
|
1132
|
+
)
|
|
1133
|
+
);
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
// src/constants.ts
|
|
1137
|
+
var DEFAULT_TRACE_MAX_ENTRIES = 200;
|
|
1138
|
+
|
|
1139
|
+
// src/traceStore.ts
|
|
1140
|
+
function createRuleTraceStore(maxEntries = DEFAULT_TRACE_MAX_ENTRIES) {
|
|
1141
|
+
let entries = [];
|
|
1142
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1143
|
+
let disposeTraceSubscription = null;
|
|
1144
|
+
const notify = () => {
|
|
1145
|
+
for (const listener of listeners) {
|
|
1146
|
+
listener();
|
|
1147
|
+
}
|
|
1148
|
+
};
|
|
1149
|
+
return {
|
|
1150
|
+
record(entry) {
|
|
1151
|
+
entries = [...entries, entry].slice(-maxEntries);
|
|
1152
|
+
notify();
|
|
1153
|
+
},
|
|
1154
|
+
getSnapshot() {
|
|
1155
|
+
return entries;
|
|
1156
|
+
},
|
|
1157
|
+
subscribe(listener) {
|
|
1158
|
+
listeners.add(listener);
|
|
1159
|
+
return () => {
|
|
1160
|
+
listeners.delete(listener);
|
|
1161
|
+
};
|
|
1162
|
+
},
|
|
1163
|
+
dispose() {
|
|
1164
|
+
disposeTraceSubscription?.();
|
|
1165
|
+
disposeTraceSubscription = null;
|
|
1166
|
+
listeners.clear();
|
|
1167
|
+
},
|
|
1168
|
+
bindTraceSubscription(unsubscribe) {
|
|
1169
|
+
disposeTraceSubscription = unsubscribe;
|
|
1170
|
+
}
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
// src/attach.ts
|
|
1175
|
+
function attachRuleDevtools(formStore, options) {
|
|
1176
|
+
const store = createRuleTraceStore(
|
|
1177
|
+
DEFAULT_TRACE_MAX_ENTRIES
|
|
1178
|
+
);
|
|
1179
|
+
store.bindTraceSubscription(formStore.subscribeTrace(store.record));
|
|
1180
|
+
return store;
|
|
1181
|
+
}
|
|
1182
|
+
var RuleTraceContext = react.createContext(null);
|
|
1183
|
+
function RuleDevtoolsScope({ formStore, children }) {
|
|
1184
|
+
const traceStore = react.useMemo(() => attachRuleDevtools(formStore), [formStore]);
|
|
1185
|
+
react.useEffect(() => {
|
|
1186
|
+
return () => {
|
|
1187
|
+
traceStore.dispose();
|
|
1188
|
+
};
|
|
1189
|
+
}, [traceStore]);
|
|
1190
|
+
return /* @__PURE__ */ jsxRuntime.jsx(RuleTraceContext.Provider, { value: traceStore, children });
|
|
1191
|
+
}
|
|
1192
|
+
function useRuleTraceStore() {
|
|
1193
|
+
const store = react.useContext(RuleTraceContext);
|
|
1194
|
+
if (!store) {
|
|
1195
|
+
throw new Error("useRuleTraceStore must be used within RuleDevtoolsScope");
|
|
1196
|
+
}
|
|
1197
|
+
return store;
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
// src/RuleDetailsModal.tsx
|
|
1201
|
+
init_effectStyles();
|
|
1202
|
+
init_i18n();
|
|
1203
|
+
|
|
1204
|
+
// src/programRuleDetailQuery.ts
|
|
1205
|
+
var PROGRAM_RULE_ACTION_DETAIL_FIELDS = [
|
|
1206
|
+
"id",
|
|
1207
|
+
"programRuleActionType",
|
|
1208
|
+
"priority",
|
|
1209
|
+
"content",
|
|
1210
|
+
"data",
|
|
1211
|
+
"location",
|
|
1212
|
+
"dataElement[id,displayName]",
|
|
1213
|
+
"trackedEntityAttribute[id,displayName]",
|
|
1214
|
+
"option[id,displayName]",
|
|
1215
|
+
"optionGroup[id,displayName]",
|
|
1216
|
+
"programStageSection[id,displayName]",
|
|
1217
|
+
"programStage[id,displayName]",
|
|
1218
|
+
"programSection[id,displayName]"
|
|
1219
|
+
].join(",");
|
|
1220
|
+
var PROGRAM_RULE_DETAIL_FIELDS = [
|
|
1221
|
+
"id",
|
|
1222
|
+
"name",
|
|
1223
|
+
"displayName",
|
|
1224
|
+
"code",
|
|
1225
|
+
"description",
|
|
1226
|
+
"condition",
|
|
1227
|
+
"priority",
|
|
1228
|
+
"lastUpdated",
|
|
1229
|
+
"lastUpdatedBy[displayName]",
|
|
1230
|
+
"program[id,displayName]",
|
|
1231
|
+
"programStage[id,displayName]",
|
|
1232
|
+
`programRuleActions[${PROGRAM_RULE_ACTION_DETAIL_FIELDS}]`
|
|
1233
|
+
].join(",");
|
|
1234
|
+
var programRuleDetailQuery = {
|
|
1235
|
+
programRule: {
|
|
1236
|
+
resource: "programRules",
|
|
1237
|
+
id: (variables) => variables.ruleId,
|
|
1238
|
+
params: {
|
|
1239
|
+
fields: PROGRAM_RULE_DETAIL_FIELDS
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
};
|
|
1243
|
+
|
|
1244
|
+
// src/useProgramRuleDetail.ts
|
|
1245
|
+
function isCurrentDetail(ruleId, detail) {
|
|
1246
|
+
return ruleId != null && detail?.id === ruleId;
|
|
1247
|
+
}
|
|
1248
|
+
function resolveDetailState(ruleId, detail, hasError) {
|
|
1249
|
+
const isCurrent = isCurrentDetail(ruleId, detail);
|
|
1250
|
+
return {
|
|
1251
|
+
detail: isCurrent ? detail : void 0,
|
|
1252
|
+
loading: ruleId != null && !isCurrent && !hasError
|
|
1253
|
+
};
|
|
1254
|
+
}
|
|
1255
|
+
function useProgramRuleDetail(ruleId) {
|
|
1256
|
+
const { data, error, refetch } = appRuntime.useDataQuery(
|
|
1257
|
+
programRuleDetailQuery,
|
|
1258
|
+
{ lazy: true }
|
|
1259
|
+
);
|
|
1260
|
+
react.useEffect(() => {
|
|
1261
|
+
if (ruleId) {
|
|
1262
|
+
void refetch({ ruleId });
|
|
1263
|
+
}
|
|
1264
|
+
}, [ruleId, refetch]);
|
|
1265
|
+
return { ...resolveDetailState(ruleId, data?.programRule, Boolean(error)), error };
|
|
1266
|
+
}
|
|
1267
|
+
var EM_DASH = "\u2014";
|
|
1268
|
+
function formatTimestamp(value) {
|
|
1269
|
+
if (!value) {
|
|
1270
|
+
return null;
|
|
1271
|
+
}
|
|
1272
|
+
const date = new Date(value);
|
|
1273
|
+
if (Number.isNaN(date.getTime())) {
|
|
1274
|
+
return null;
|
|
1275
|
+
}
|
|
1276
|
+
const day = String(date.getDate());
|
|
1277
|
+
const month = date.toLocaleString("en-GB", { month: "short" });
|
|
1278
|
+
const year = String(date.getFullYear());
|
|
1279
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
1280
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
1281
|
+
return `${day} ${month} ${year} ${hours}:${minutes}`;
|
|
1282
|
+
}
|
|
1283
|
+
function resolveStatusChip(status) {
|
|
1284
|
+
switch (status) {
|
|
1285
|
+
case "firing":
|
|
1286
|
+
return {
|
|
1287
|
+
label: translate("Firing"),
|
|
1288
|
+
className: "bg-dhis2-teal-100 text-dhis2-teal-900"
|
|
1289
|
+
};
|
|
1290
|
+
case "idle":
|
|
1291
|
+
return { label: translate("Idle"), className: "bg-dhis2-grey-200 text-dhis2-grey-700" };
|
|
1292
|
+
case "out-of-scope":
|
|
1293
|
+
return {
|
|
1294
|
+
label: translate("Out of scope"),
|
|
1295
|
+
className: "bg-dhis2-grey-200 text-dhis2-grey-600"
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
var VARIABLE_PATTERNS = [
|
|
1300
|
+
{
|
|
1301
|
+
pattern: /#\{[^}]*\}/g,
|
|
1302
|
+
kind: "Data element",
|
|
1303
|
+
className: "bg-dhis2-blue-100 text-dhis2-blue-900"
|
|
1304
|
+
},
|
|
1305
|
+
{
|
|
1306
|
+
pattern: /A\{[^}]*\}/g,
|
|
1307
|
+
kind: "Tracked entity attribute",
|
|
1308
|
+
className: "bg-dhis2-teal-100 text-dhis2-teal-900"
|
|
1309
|
+
},
|
|
1310
|
+
{
|
|
1311
|
+
pattern: /V\{[^}]*\}/g,
|
|
1312
|
+
kind: "Environment variable",
|
|
1313
|
+
className: "bg-dhis2-grey-200 text-dhis2-grey-900"
|
|
1314
|
+
},
|
|
1315
|
+
{
|
|
1316
|
+
pattern: /d2:\w+(?=\()/g,
|
|
1317
|
+
kind: "Function",
|
|
1318
|
+
className: "bg-dhis2-yellow-100 text-dhis2-yellow-900"
|
|
1319
|
+
}
|
|
1320
|
+
];
|
|
1321
|
+
var RESOLVABLE_VARIABLE_KINDS = /* @__PURE__ */ new Set([
|
|
1322
|
+
"Data element",
|
|
1323
|
+
"Tracked entity attribute"
|
|
1324
|
+
]);
|
|
1325
|
+
function resolveVariableDisplayName(name, programRuleVariables) {
|
|
1326
|
+
const variable = programRuleVariables.find((candidate) => candidate.name === name);
|
|
1327
|
+
return variable?.dataElement?.displayName ?? variable?.trackedEntityAttribute?.displayName;
|
|
1328
|
+
}
|
|
1329
|
+
function resolveVariableToken(token, kind, programRuleVariables) {
|
|
1330
|
+
if (kind === "Function") {
|
|
1331
|
+
return `${token}()`;
|
|
1332
|
+
}
|
|
1333
|
+
if (!RESOLVABLE_VARIABLE_KINDS.has(kind)) {
|
|
1334
|
+
return token;
|
|
1335
|
+
}
|
|
1336
|
+
return resolveVariableDisplayName(token.slice(2, -1), programRuleVariables) ?? token;
|
|
1337
|
+
}
|
|
1338
|
+
function matchDistinctTokens(condition, pattern, seen) {
|
|
1339
|
+
const matches = condition.match(pattern) ?? [];
|
|
1340
|
+
const distinct = matches.filter((token) => !seen.has(token));
|
|
1341
|
+
for (const token of distinct) {
|
|
1342
|
+
seen.add(token);
|
|
1343
|
+
}
|
|
1344
|
+
return distinct;
|
|
1345
|
+
}
|
|
1346
|
+
function parseConditionVariables(condition, programRuleVariables) {
|
|
1347
|
+
if (!condition) {
|
|
1348
|
+
return [];
|
|
1349
|
+
}
|
|
1350
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1351
|
+
return VARIABLE_PATTERNS.flatMap(
|
|
1352
|
+
({ pattern, kind, className }) => matchDistinctTokens(condition, pattern, seen).map((token) => ({
|
|
1353
|
+
token,
|
|
1354
|
+
kind,
|
|
1355
|
+
label: resolveVariableToken(token, kind, programRuleVariables),
|
|
1356
|
+
className
|
|
1357
|
+
}))
|
|
1358
|
+
);
|
|
1359
|
+
}
|
|
1360
|
+
var ACTION_TARGET_RESOLVERS = [
|
|
1361
|
+
{ label: "Data element", getRef: (action) => action.dataElement },
|
|
1362
|
+
{ label: "Tracked entity attribute", getRef: (action) => action.trackedEntityAttribute },
|
|
1363
|
+
{ label: "Program stage section", getRef: (action) => action.programStageSection },
|
|
1364
|
+
{ label: "Program stage", getRef: (action) => action.programStage },
|
|
1365
|
+
{ label: "Option", getRef: (action) => action.option },
|
|
1366
|
+
{ label: "Option group", getRef: (action) => action.optionGroup }
|
|
1367
|
+
];
|
|
1368
|
+
function formatActionTargetValue(ref) {
|
|
1369
|
+
return `${ref.displayName ?? ref.id} \xB7 ${ref.id}`;
|
|
1370
|
+
}
|
|
1371
|
+
function resolveActionTarget(action) {
|
|
1372
|
+
for (const { label, getRef } of ACTION_TARGET_RESOLVERS) {
|
|
1373
|
+
const ref = getRef(action);
|
|
1374
|
+
if (ref?.id) {
|
|
1375
|
+
return { label: translate(label), value: formatActionTargetValue(ref) };
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
return null;
|
|
1379
|
+
}
|
|
1380
|
+
function BasicDetailCell({ label, value, mono }) {
|
|
1381
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
|
|
1382
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "m-0 text-xs text-dhis2-grey-600", children: label }),
|
|
1383
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: `m-0 mt-[2px] text-sm text-dhis2-grey-900 ${mono ? "font-mono" : ""}`, children: value })
|
|
1384
|
+
] });
|
|
1385
|
+
}
|
|
1386
|
+
var FEEDBACK_ACTION_TYPES = /* @__PURE__ */ new Set(["DISPLAYTEXT", "DISPLAYKEYVALUEPAIR"]);
|
|
1387
|
+
function resolveDataRow(action) {
|
|
1388
|
+
return action.data ? { label: translate("Data (expression)"), value: action.data, mono: true } : null;
|
|
1389
|
+
}
|
|
1390
|
+
function resolveContentRow(action) {
|
|
1391
|
+
const showContent = Boolean(action.content) && FEEDBACK_ACTION_TYPES.has(action.programRuleActionType);
|
|
1392
|
+
return showContent ? { label: translate("Content (static text)"), value: action.content ?? "" } : null;
|
|
1393
|
+
}
|
|
1394
|
+
function resolveLocationRow(action) {
|
|
1395
|
+
return action.location ? { label: translate("Location"), value: action.location, mono: true } : null;
|
|
1396
|
+
}
|
|
1397
|
+
function resolveActionRows(action) {
|
|
1398
|
+
const rows = [
|
|
1399
|
+
resolveActionTarget(action),
|
|
1400
|
+
resolveDataRow(action),
|
|
1401
|
+
resolveContentRow(action),
|
|
1402
|
+
resolveLocationRow(action)
|
|
1403
|
+
];
|
|
1404
|
+
return rows.filter((row) => row !== null);
|
|
1405
|
+
}
|
|
1406
|
+
function ActionCard({ action, index }) {
|
|
1407
|
+
const type = action.programRuleActionType;
|
|
1408
|
+
const visual = getEffectVisual(type);
|
|
1409
|
+
const Icon = exports.EFFECT_ICONS[visual.variant];
|
|
1410
|
+
const rows = resolveActionRows(action);
|
|
1411
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1412
|
+
"div",
|
|
1413
|
+
{
|
|
1414
|
+
className: "relative rounded-[3px] border border-dhis2-grey-300 bg-white py-dp12 pe-dp16 ps-dp16 shadow-[0_1px_2px_rgb(0_0_0/4%)]",
|
|
1415
|
+
style: { borderInlineStartWidth: 3, borderInlineStartColor: visual.edgeStroke },
|
|
1416
|
+
children: [
|
|
1417
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-dp12 flex items-center justify-between gap-dp8", children: [
|
|
1418
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1419
|
+
"span",
|
|
1420
|
+
{
|
|
1421
|
+
className: `inline-flex items-center gap-[4px] rounded-[4px] px-dp8 py-[2px] text-xs font-semibold ${visual.tagClassName}`,
|
|
1422
|
+
children: [
|
|
1423
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon, {}),
|
|
1424
|
+
type
|
|
1425
|
+
]
|
|
1426
|
+
}
|
|
1427
|
+
),
|
|
1428
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] uppercase tracking-wide text-dhis2-grey-600", children: translate("Action {{n}}", { n: index + 1 }) })
|
|
1429
|
+
] }),
|
|
1430
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-dp10", children: rows.map((row) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-[172px_minmax(0,1fr)] gap-dp10", children: [
|
|
1431
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-dhis2-grey-600", children: row.label }),
|
|
1432
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1433
|
+
"span",
|
|
1434
|
+
{
|
|
1435
|
+
className: `min-w-0 break-words text-sm ${row.mono ? "font-mono text-[12px]" : ""}`,
|
|
1436
|
+
children: row.value
|
|
1437
|
+
}
|
|
1438
|
+
)
|
|
1439
|
+
] }, row.label)) })
|
|
1440
|
+
]
|
|
1441
|
+
}
|
|
1442
|
+
);
|
|
1443
|
+
}
|
|
1444
|
+
function orDash(value) {
|
|
1445
|
+
return value ?? EM_DASH;
|
|
1446
|
+
}
|
|
1447
|
+
function resolveDetailDisplayName(rule) {
|
|
1448
|
+
return rule?.displayName;
|
|
1449
|
+
}
|
|
1450
|
+
function resolveDetailName(rule) {
|
|
1451
|
+
return rule?.name;
|
|
1452
|
+
}
|
|
1453
|
+
function resolveRuleDisplayName(rule, fallback) {
|
|
1454
|
+
const displayName = resolveDetailDisplayName(rule) ?? resolveDetailName(rule);
|
|
1455
|
+
return displayName ?? fallback;
|
|
1456
|
+
}
|
|
1457
|
+
function resolveRuleStageName(rule) {
|
|
1458
|
+
return rule?.programStage?.displayName;
|
|
1459
|
+
}
|
|
1460
|
+
function resolveStageName(rule, programStageName) {
|
|
1461
|
+
if (programStageName === null) {
|
|
1462
|
+
return translate("All stages (registration)");
|
|
1463
|
+
}
|
|
1464
|
+
return orDash(resolveRuleStageName(rule) ?? programStageName);
|
|
1465
|
+
}
|
|
1466
|
+
function resolveLastUpdatedByName(rule) {
|
|
1467
|
+
return rule?.lastUpdatedBy?.displayName;
|
|
1468
|
+
}
|
|
1469
|
+
function resolveLastUpdatedValue(rule) {
|
|
1470
|
+
const label = formatTimestamp(rule?.lastUpdated);
|
|
1471
|
+
if (!label) {
|
|
1472
|
+
return EM_DASH;
|
|
1473
|
+
}
|
|
1474
|
+
const by = resolveLastUpdatedByName(rule);
|
|
1475
|
+
return by ? `${label} \xB7 ${by}` : label;
|
|
1476
|
+
}
|
|
1477
|
+
function resolvePriorityCellValue(rule) {
|
|
1478
|
+
return rule?.priority != null ? String(rule.priority) : EM_DASH;
|
|
1479
|
+
}
|
|
1480
|
+
function resolveActionsCellValue(rule) {
|
|
1481
|
+
return translate("{{n}} action(s)", { n: rule?.programRuleActions?.length ?? 0 });
|
|
1482
|
+
}
|
|
1483
|
+
function resolveBasicDetailCells(rule, ruleName, programStageName) {
|
|
1484
|
+
return [
|
|
1485
|
+
{ label: translate("Name"), value: resolveRuleDisplayName(rule, ruleName) },
|
|
1486
|
+
{ label: translate("Code"), value: orDash(rule?.code), mono: true },
|
|
1487
|
+
{ label: translate("Identifier"), value: orDash(rule?.id), mono: true },
|
|
1488
|
+
{ label: translate("Program"), value: orDash(rule?.program?.displayName) },
|
|
1489
|
+
{ label: translate("Program stage"), value: resolveStageName(rule, programStageName) },
|
|
1490
|
+
{ label: translate("Priority"), value: resolvePriorityCellValue(rule) },
|
|
1491
|
+
{ label: translate("Actions"), value: resolveActionsCellValue(rule) },
|
|
1492
|
+
{ label: translate("Last updated"), value: resolveLastUpdatedValue(rule) }
|
|
1493
|
+
];
|
|
1494
|
+
}
|
|
1495
|
+
function BasicDetails({
|
|
1496
|
+
rule,
|
|
1497
|
+
ruleName,
|
|
1498
|
+
programStageName
|
|
1499
|
+
}) {
|
|
1500
|
+
const cells = resolveBasicDetailCells(rule, ruleName, programStageName);
|
|
1501
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-x-dp24 gap-y-dp12 rounded-[3px] border border-dhis2-grey-300 bg-dhis2-grey-050 p-dp16", children: cells.map((cell) => /* @__PURE__ */ jsxRuntime.jsx(BasicDetailCell, { ...cell }, cell.label)) });
|
|
1502
|
+
}
|
|
1503
|
+
var SECTION_HEADING_CLASS = "m-0 mb-dp8 text-[13px] font-bold uppercase tracking-wide text-dhis2-grey-700";
|
|
1504
|
+
function RuleDetailsHeader({
|
|
1505
|
+
title,
|
|
1506
|
+
chip,
|
|
1507
|
+
description
|
|
1508
|
+
}) {
|
|
1509
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "-mx-dp24 -mt-dp24 mb-0 border-b border-dhis2-grey-300 px-dp24 pb-dp16 pe-[44px] pt-[20px]", children: [
|
|
1510
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "m-0 text-[11px] font-bold uppercase tracking-[.09em] text-dhis2-grey-600", children: translate("Program rule") }),
|
|
1511
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-[6px] flex flex-wrap items-center gap-dp8", children: [
|
|
1512
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "m-0 text-xl font-medium leading-[1.3] text-dhis2-grey-900", children: title }),
|
|
1513
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1514
|
+
"span",
|
|
900
1515
|
{
|
|
901
|
-
className:
|
|
902
|
-
|
|
903
|
-
children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
904
|
-
"span",
|
|
905
|
-
{
|
|
906
|
-
className: "inline-flex items-center gap-dp8 text-xs text-dhis2-grey-800",
|
|
907
|
-
children: [
|
|
908
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: getLegendSwatchClassName(item.kind) }),
|
|
909
|
-
item.label
|
|
910
|
-
]
|
|
911
|
-
},
|
|
912
|
-
item.kind
|
|
913
|
-
))
|
|
1516
|
+
className: `rounded-[4px] px-dp8 py-[2px] text-xs font-semibold ${chip.className}`,
|
|
1517
|
+
children: chip.label
|
|
914
1518
|
}
|
|
915
|
-
)
|
|
916
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-dp8", children: [
|
|
917
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs tabular-nums text-dhis2-grey-600", children: translate("{{nodes}} nodes \xB7 {{edges}} edges", {
|
|
918
|
-
nodes: nodeCount,
|
|
919
|
-
edges: edgeCount
|
|
920
|
-
}) }),
|
|
921
|
-
headerActions
|
|
922
|
-
] })
|
|
1519
|
+
)
|
|
923
1520
|
] }),
|
|
924
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1521
|
+
description ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1522
|
+
"p",
|
|
1523
|
+
{
|
|
1524
|
+
className: "m-0 mt-[6px] text-sm text-dhis2-grey-700",
|
|
1525
|
+
style: { textWrap: "pretty" },
|
|
1526
|
+
children: description
|
|
1527
|
+
}
|
|
1528
|
+
) : null
|
|
925
1529
|
] });
|
|
926
1530
|
}
|
|
927
|
-
function
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
edgeCount
|
|
1531
|
+
function ConditionSection({
|
|
1532
|
+
condition,
|
|
1533
|
+
variables
|
|
931
1534
|
}) {
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
1535
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("section", { children: [
|
|
1536
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: SECTION_HEADING_CLASS, children: translate("Condition") }),
|
|
1537
|
+
/* @__PURE__ */ jsxRuntime.jsx("pre", { className: "m-0 whitespace-pre-wrap break-words rounded-[3px] border border-dhis2-grey-300 bg-dhis2-grey-200 px-dp16 py-[14px] font-mono text-[13px] leading-[1.6] text-dhis2-grey-900", children: condition ?? EM_DASH }),
|
|
1538
|
+
variables.length ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-dp12", children: [
|
|
1539
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "m-0 mb-dp8 text-xs text-dhis2-grey-600", children: translate("Variables referenced") }),
|
|
1540
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-dp8", children: variables.map((variable) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1541
|
+
"span",
|
|
1542
|
+
{
|
|
1543
|
+
className: `inline-flex items-center gap-[4px] rounded-full px-dp8 py-[2px] text-xs ${variable.className}`,
|
|
1544
|
+
children: [
|
|
1545
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono font-medium", children: variable.label }),
|
|
1546
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-70", children: translate(variable.kind) })
|
|
1547
|
+
]
|
|
1548
|
+
},
|
|
1549
|
+
variable.token
|
|
1550
|
+
)) })
|
|
1551
|
+
] }) : null
|
|
1552
|
+
] });
|
|
942
1553
|
}
|
|
943
|
-
function
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
return { ...node, position: existing.position };
|
|
949
|
-
}
|
|
950
|
-
return node;
|
|
951
|
-
});
|
|
1554
|
+
function ActionsSection({ actions }) {
|
|
1555
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("section", { children: [
|
|
1556
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: SECTION_HEADING_CLASS, children: translate("Actions") }),
|
|
1557
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-dp12", children: actions.map((action, index) => /* @__PURE__ */ jsxRuntime.jsx(ActionCard, { action, index }, action.id)) })
|
|
1558
|
+
] });
|
|
952
1559
|
}
|
|
953
|
-
function
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
1560
|
+
function RuleDetailsContent({
|
|
1561
|
+
detail,
|
|
1562
|
+
ruleName,
|
|
1563
|
+
programStageName,
|
|
1564
|
+
variables
|
|
957
1565
|
}) {
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
onNodesChange,
|
|
974
|
-
onEdgesChange,
|
|
975
|
-
nodesDraggable: true,
|
|
976
|
-
defaultEdgeOptions: DEFAULT_EDGE_OPTIONS,
|
|
977
|
-
elevateEdgesOnSelect: true,
|
|
978
|
-
fitView: true,
|
|
979
|
-
proOptions: { hideAttribution: true },
|
|
980
|
-
className: "h-full w-full",
|
|
981
|
-
children: [
|
|
982
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
983
|
-
FitViewOnChange,
|
|
984
|
-
{
|
|
985
|
-
layoutKey,
|
|
986
|
-
nodeCount: nodes.length,
|
|
987
|
-
edgeCount: edges.length
|
|
988
|
-
}
|
|
989
|
-
),
|
|
990
|
-
/* @__PURE__ */ jsxRuntime.jsx(react$1.Background, { gap: 16, size: 1 }),
|
|
991
|
-
/* @__PURE__ */ jsxRuntime.jsx(react$1.Controls, { showInteractive: false })
|
|
992
|
-
]
|
|
993
|
-
}
|
|
994
|
-
);
|
|
1566
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-dp24 pt-dp16", children: [
|
|
1567
|
+
/* @__PURE__ */ jsxRuntime.jsxs("section", { children: [
|
|
1568
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: SECTION_HEADING_CLASS, children: translate("Basic details") }),
|
|
1569
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1570
|
+
BasicDetails,
|
|
1571
|
+
{
|
|
1572
|
+
rule: detail,
|
|
1573
|
+
ruleName,
|
|
1574
|
+
programStageName
|
|
1575
|
+
}
|
|
1576
|
+
)
|
|
1577
|
+
] }),
|
|
1578
|
+
/* @__PURE__ */ jsxRuntime.jsx(ConditionSection, { condition: detail.condition, variables }),
|
|
1579
|
+
/* @__PURE__ */ jsxRuntime.jsx(ActionsSection, { actions: detail.programRuleActions ?? [] })
|
|
1580
|
+
] });
|
|
995
1581
|
}
|
|
996
|
-
function
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
className,
|
|
1004
|
-
layoutKey,
|
|
1005
|
-
headerActions,
|
|
1006
|
-
minHeightClassName = "min-h-[360px]"
|
|
1582
|
+
function RuleDetailsBody({
|
|
1583
|
+
detail,
|
|
1584
|
+
loading,
|
|
1585
|
+
error,
|
|
1586
|
+
ruleName,
|
|
1587
|
+
programStageName,
|
|
1588
|
+
variables
|
|
1007
1589
|
}) {
|
|
1008
|
-
|
|
1009
|
-
(
|
|
1010
|
-
|
|
1011
|
-
)
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
if (highlightRuleId) {
|
|
1027
|
-
for (const key of ruleGraphKeys(graphEntry, highlightRuleId)) {
|
|
1028
|
-
keys.add(key);
|
|
1029
|
-
}
|
|
1590
|
+
if (loading) {
|
|
1591
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[280px] items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.CircularLoader, { small: true }) });
|
|
1592
|
+
}
|
|
1593
|
+
if (error) {
|
|
1594
|
+
return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "m-0 text-sm text-dhis2-red-700", children: translate("Could not load this rule: {{message}}", { message: error.message }) });
|
|
1595
|
+
}
|
|
1596
|
+
if (!detail) {
|
|
1597
|
+
return null;
|
|
1598
|
+
}
|
|
1599
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1600
|
+
RuleDetailsContent,
|
|
1601
|
+
{
|
|
1602
|
+
detail,
|
|
1603
|
+
ruleName,
|
|
1604
|
+
programStageName,
|
|
1605
|
+
variables
|
|
1030
1606
|
}
|
|
1031
|
-
return keys;
|
|
1032
|
-
}, [graphEntry, highlightRuleId, selectedEntryId]);
|
|
1033
|
-
const { nodes, edges } = react.useMemo(
|
|
1034
|
-
() => toFlowGraph(graph, fieldState, formValues, highlightedKeys),
|
|
1035
|
-
[graph, fieldState, formValues, highlightedKeys]
|
|
1036
1607
|
);
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
for (const edge of graph.edges) {
|
|
1040
|
-
if (edge.effectType) {
|
|
1041
|
-
variants.add(getEffectVariant(edge.effectType));
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
return variants;
|
|
1045
|
-
}, [graph.edges]);
|
|
1046
|
-
if (!graph.nodes.length) {
|
|
1047
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-dp8", children: /* @__PURE__ */ jsxRuntime.jsx(ui.NoticeBox, { title: translate("No rule relationships yet"), children: translate(
|
|
1048
|
-
"Interact with the form to build the dependency graph. Only rules active in the latest evaluation are shown. Select a trace entry to inspect a past evaluation. Connections flow Field \u2192 Rule \u2192 Target (read / effect)."
|
|
1049
|
-
) }) });
|
|
1050
|
-
}
|
|
1608
|
+
}
|
|
1609
|
+
function RuleDetailsFooter({ onClose }) {
|
|
1051
1610
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1052
1611
|
"div",
|
|
1053
1612
|
{
|
|
1054
|
-
|
|
1613
|
+
style: { order: 3 },
|
|
1614
|
+
className: "-mx-dp24 -mb-dp24 mt-dp16 flex items-center justify-between gap-dp12 border-t border-dhis2-grey-300 bg-dhis2-grey-050 px-dp24 py-[14px]",
|
|
1055
1615
|
children: [
|
|
1056
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1057
|
-
|
|
1058
|
-
{
|
|
1059
|
-
nodeCount: graph.nodes.length,
|
|
1060
|
-
edgeCount: graph.edges.length,
|
|
1061
|
-
headerActions,
|
|
1062
|
-
activeEffectVariants
|
|
1063
|
-
}
|
|
1064
|
-
),
|
|
1065
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1066
|
-
"div",
|
|
1067
|
-
{
|
|
1068
|
-
className: `${minHeightClassName} flex-1 bg-white`,
|
|
1069
|
-
style: { width: "100%", height: "100%" },
|
|
1070
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(react$1.ReactFlowProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(RuleGraphCanvas, { nodes, edges, layoutKey }) })
|
|
1071
|
-
}
|
|
1072
|
-
)
|
|
1616
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "m-0 text-sm text-dhis2-grey-600", children: translate("Read-only view. Edit rules in the Maintenance app.") }),
|
|
1617
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.ButtonStrip, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { secondary: true, onClick: onClose, children: translate("Close") }) })
|
|
1073
1618
|
]
|
|
1074
1619
|
}
|
|
1075
1620
|
);
|
|
1076
1621
|
}
|
|
1077
|
-
function
|
|
1622
|
+
function resolveActiveRuleId(open, ruleId) {
|
|
1623
|
+
return open ? ruleId : null;
|
|
1624
|
+
}
|
|
1625
|
+
function shouldRenderModal(open, ruleId) {
|
|
1626
|
+
return open && ruleId != null;
|
|
1627
|
+
}
|
|
1628
|
+
function RuleDetailsModal({
|
|
1078
1629
|
open,
|
|
1079
1630
|
onClose,
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1631
|
+
ruleId,
|
|
1632
|
+
ruleName,
|
|
1633
|
+
status,
|
|
1634
|
+
programStageName,
|
|
1635
|
+
programRuleVariables
|
|
1083
1636
|
}) {
|
|
1084
|
-
|
|
1637
|
+
const { detail, loading, error } = useProgramRuleDetail(resolveActiveRuleId(open, ruleId));
|
|
1638
|
+
if (!shouldRenderModal(open, ruleId)) {
|
|
1085
1639
|
return null;
|
|
1086
1640
|
}
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.ModalContent, {
|
|
1093
|
-
|
|
1641
|
+
const chip = resolveStatusChip(status);
|
|
1642
|
+
const variables = parseConditionVariables(detail?.condition, programRuleVariables);
|
|
1643
|
+
const title = resolveRuleDisplayName(detail, ruleName);
|
|
1644
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Modal, { position: "middle", onClose, children: [
|
|
1645
|
+
/* @__PURE__ */ jsxRuntime.jsx(RuleDetailsHeader, { title, chip, description: detail?.description }),
|
|
1646
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.ModalContent, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1647
|
+
RuleDetailsBody,
|
|
1094
1648
|
{
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1649
|
+
detail,
|
|
1650
|
+
loading,
|
|
1651
|
+
error,
|
|
1652
|
+
ruleName,
|
|
1653
|
+
programStageName,
|
|
1654
|
+
variables
|
|
1099
1655
|
}
|
|
1100
|
-
) })
|
|
1656
|
+
) }),
|
|
1657
|
+
/* @__PURE__ */ jsxRuntime.jsx(RuleDetailsFooter, { onClose })
|
|
1101
1658
|
] });
|
|
1102
1659
|
}
|
|
1660
|
+
|
|
1661
|
+
// src/RulesPanelGraphFallback.tsx
|
|
1662
|
+
init_i18n();
|
|
1663
|
+
function RulesPanelGraphFallback() {
|
|
1664
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1665
|
+
"div",
|
|
1666
|
+
{
|
|
1667
|
+
className: "flex min-h-[200px] items-center justify-center",
|
|
1668
|
+
role: "status",
|
|
1669
|
+
"aria-label": translate("Loading graph"),
|
|
1670
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(ui.CircularLoader, { small: true })
|
|
1671
|
+
}
|
|
1672
|
+
);
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
// src/TraceTimeline.tsx
|
|
1676
|
+
init_EffectBadge();
|
|
1677
|
+
init_i18n();
|
|
1103
1678
|
function resolveLabel(id, resolver) {
|
|
1104
1679
|
const label = resolver?.(id) ?? id;
|
|
1105
1680
|
return { label, showId: label !== id };
|
|
@@ -1316,11 +1891,69 @@ function TraceTimeline({
|
|
|
1316
1891
|
) }, entry.id);
|
|
1317
1892
|
}) });
|
|
1318
1893
|
}
|
|
1319
|
-
|
|
1894
|
+
|
|
1895
|
+
// src/RulesPanel.tsx
|
|
1896
|
+
init_traceEntry();
|
|
1897
|
+
var REORDER_DURATION_MS = 220;
|
|
1898
|
+
var REORDER_EASING = "ease-out";
|
|
1899
|
+
function prefersReducedMotion() {
|
|
1900
|
+
return typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1901
|
+
}
|
|
1902
|
+
function useFlipReorder(orderedIds, containerRef) {
|
|
1903
|
+
const positionsRef = react.useRef(/* @__PURE__ */ new Map());
|
|
1904
|
+
react.useLayoutEffect(() => {
|
|
1905
|
+
const container = containerRef.current;
|
|
1906
|
+
if (!container) {
|
|
1907
|
+
return;
|
|
1908
|
+
}
|
|
1909
|
+
const previousPositions = positionsRef.current;
|
|
1910
|
+
const items = container.querySelectorAll("[data-rule-id]");
|
|
1911
|
+
if (!prefersReducedMotion()) {
|
|
1912
|
+
items.forEach((item) => {
|
|
1913
|
+
const id = item.dataset.ruleId;
|
|
1914
|
+
if (!id) {
|
|
1915
|
+
return;
|
|
1916
|
+
}
|
|
1917
|
+
const previousRect = previousPositions.get(id);
|
|
1918
|
+
if (!previousRect) {
|
|
1919
|
+
return;
|
|
1920
|
+
}
|
|
1921
|
+
const currentRect = item.getBoundingClientRect();
|
|
1922
|
+
const deltaY = previousRect.top - currentRect.top;
|
|
1923
|
+
if (deltaY === 0) {
|
|
1924
|
+
return;
|
|
1925
|
+
}
|
|
1926
|
+
item.style.transition = "none";
|
|
1927
|
+
item.style.transform = `translateY(${String(deltaY)}px)`;
|
|
1928
|
+
item.getBoundingClientRect();
|
|
1929
|
+
item.style.transition = `transform ${String(REORDER_DURATION_MS)}ms ${REORDER_EASING}`;
|
|
1930
|
+
item.style.transform = "";
|
|
1931
|
+
});
|
|
1932
|
+
}
|
|
1933
|
+
const nextPositions = /* @__PURE__ */ new Map();
|
|
1934
|
+
items.forEach((item) => {
|
|
1935
|
+
const id = item.dataset.ruleId;
|
|
1936
|
+
if (id) {
|
|
1937
|
+
nextPositions.set(id, item.getBoundingClientRect());
|
|
1938
|
+
}
|
|
1939
|
+
});
|
|
1940
|
+
positionsRef.current = nextPositions;
|
|
1941
|
+
}, [orderedIds.join("|")]);
|
|
1942
|
+
}
|
|
1943
|
+
var LazyRuleGraphView = react.lazy(
|
|
1944
|
+
() => Promise.resolve().then(() => (init_RuleGraphView(), RuleGraphView_exports)).then((module) => ({ default: module.RuleGraphView }))
|
|
1945
|
+
);
|
|
1946
|
+
var LazyRuleGraphModal = react.lazy(
|
|
1947
|
+
() => Promise.resolve().then(() => (init_RuleGraphModal(), RuleGraphModal_exports)).then((module) => ({ default: module.RuleGraphModal }))
|
|
1948
|
+
);
|
|
1949
|
+
function resolveScopeStageId(metadata, activeProgramStageId) {
|
|
1950
|
+
if (activeProgramStageId !== void 0) {
|
|
1951
|
+
return activeProgramStageId;
|
|
1952
|
+
}
|
|
1320
1953
|
return metadata.formKind === "event" ? metadata.programStageId : null;
|
|
1321
1954
|
}
|
|
1322
1955
|
function isRuleInScope(rule, scopeStageId) {
|
|
1323
|
-
return rule.programStageId ===
|
|
1956
|
+
return rule.programStageId === scopeStageId;
|
|
1324
1957
|
}
|
|
1325
1958
|
function resolveAccentClassName(inScope, firing, selected) {
|
|
1326
1959
|
if (selected) {
|
|
@@ -1337,6 +1970,17 @@ function resolveCardStatus(inScope, firing) {
|
|
|
1337
1970
|
}
|
|
1338
1971
|
return firing ? { label: translate("Firing"), className: "text-dhis2-teal-700" } : { label: translate("Idle"), className: "text-dhis2-grey-600" };
|
|
1339
1972
|
}
|
|
1973
|
+
function resolveDetailsStatus(inScope, firing) {
|
|
1974
|
+
if (!inScope) {
|
|
1975
|
+
return "out-of-scope";
|
|
1976
|
+
}
|
|
1977
|
+
return firing ? "firing" : "idle";
|
|
1978
|
+
}
|
|
1979
|
+
function resolveAppliesToCaption(rule, labelLookup) {
|
|
1980
|
+
return rule.programStageId === null ? translate("Applies to registration") : translate("Applies to {{stage}}", {
|
|
1981
|
+
stage: labelLookup.resolveStageName(rule.programStageId)
|
|
1982
|
+
});
|
|
1983
|
+
}
|
|
1340
1984
|
function formatActionLabel(action) {
|
|
1341
1985
|
if (action.targetLabel && action.detail) {
|
|
1342
1986
|
return `${action.type} \xB7 ${action.targetLabel} = ${action.detail}`;
|
|
@@ -1370,7 +2014,11 @@ function resolveTabLabel(key) {
|
|
|
1370
2014
|
return translate("Graph");
|
|
1371
2015
|
}
|
|
1372
2016
|
}
|
|
1373
|
-
function RulesPanel({
|
|
2017
|
+
function RulesPanel({
|
|
2018
|
+
metadata,
|
|
2019
|
+
showConditions = true,
|
|
2020
|
+
activeProgramStageId
|
|
2021
|
+
}) {
|
|
1374
2022
|
const formStore = dhis2FormUtilsHooks.useFormStore();
|
|
1375
2023
|
const { form } = dhis2FormUtilsHooks.useFormStateContext();
|
|
1376
2024
|
const traceStore = useRuleTraceStore();
|
|
@@ -1379,9 +2027,13 @@ function RulesPanel({ metadata, showConditions = true }) {
|
|
|
1379
2027
|
const [highlightRuleId, setHighlightRuleId] = react.useState(null);
|
|
1380
2028
|
const [graphModalOpen, setGraphModalOpen] = react.useState(false);
|
|
1381
2029
|
const [scopeFilter, setScopeFilter] = react.useState("scoped");
|
|
2030
|
+
const [detailsRuleId, setDetailsRuleId] = react.useState(null);
|
|
1382
2031
|
const labelLookup = react.useMemo(() => createLabelLookup(metadata), [metadata]);
|
|
1383
2032
|
const catalog = react.useMemo(() => resolveProgramRulesList(metadata), [metadata]);
|
|
1384
|
-
const scopeStageId = react.useMemo(
|
|
2033
|
+
const scopeStageId = react.useMemo(
|
|
2034
|
+
() => resolveScopeStageId(metadata, activeProgramStageId),
|
|
2035
|
+
[metadata, activeProgramStageId]
|
|
2036
|
+
);
|
|
1385
2037
|
const entries = react.useSyncExternalStore(
|
|
1386
2038
|
react.useCallback((listener) => traceStore.subscribe(listener), [traceStore]),
|
|
1387
2039
|
react.useCallback(() => traceStore.getSnapshot(), [traceStore]),
|
|
@@ -1433,6 +2085,15 @@ function RulesPanel({ metadata, showConditions = true }) {
|
|
|
1433
2085
|
}
|
|
1434
2086
|
return null;
|
|
1435
2087
|
}, [highlightedRuleName, selectedEntry]);
|
|
2088
|
+
const detailsRule = react.useMemo(
|
|
2089
|
+
() => catalog.find((rule) => rule.id === detailsRuleId) ?? null,
|
|
2090
|
+
[catalog, detailsRuleId]
|
|
2091
|
+
);
|
|
2092
|
+
const detailsStatus = detailsRule ? resolveDetailsStatus(
|
|
2093
|
+
isRuleInScope(detailsRule, scopeStageId),
|
|
2094
|
+
activeRuleIds.has(detailsRule.id)
|
|
2095
|
+
) : "idle";
|
|
2096
|
+
const detailsProgramStageName = detailsRule ? detailsRule.programStageId === null ? null : labelLookup.resolveStageName(detailsRule.programStageId) : void 0;
|
|
1436
2097
|
const graphProps = {
|
|
1437
2098
|
entries,
|
|
1438
2099
|
fieldState,
|
|
@@ -1534,6 +2195,9 @@ function RulesPanel({ metadata, showConditions = true }) {
|
|
|
1534
2195
|
onSelectRule: (ruleId) => {
|
|
1535
2196
|
setHighlightRuleId(ruleId);
|
|
1536
2197
|
setTab("graph");
|
|
2198
|
+
},
|
|
2199
|
+
onOpenDetails: (ruleId) => {
|
|
2200
|
+
setDetailsRuleId(ruleId);
|
|
1537
2201
|
}
|
|
1538
2202
|
}
|
|
1539
2203
|
) : tab === "trace" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1553,19 +2217,19 @@ function RulesPanel({ metadata, showConditions = true }) {
|
|
|
1553
2217
|
},
|
|
1554
2218
|
labelLookup
|
|
1555
2219
|
}
|
|
1556
|
-
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
1557
|
-
|
|
2220
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(react.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(RulesPanelGraphFallback, {}), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2221
|
+
LazyRuleGraphView,
|
|
1558
2222
|
{
|
|
1559
2223
|
...graphProps,
|
|
1560
2224
|
headerActions: graphHasNodes ? expandGraphButton : void 0
|
|
1561
2225
|
}
|
|
1562
|
-
) }),
|
|
2226
|
+
) }) }),
|
|
1563
2227
|
entries.length > 0 && tab !== "rules" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "sr-only", "aria-live": "polite", children: translate("{{evaluations}} evaluations \xB7 {{rules}} rules observed", {
|
|
1564
2228
|
evaluations: entries.length,
|
|
1565
2229
|
rules: observedRuleCount
|
|
1566
2230
|
}) }) : null,
|
|
1567
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1568
|
-
|
|
2231
|
+
graphModalOpen ? /* @__PURE__ */ jsxRuntime.jsx(react.Suspense, { fallback: null, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2232
|
+
LazyRuleGraphModal,
|
|
1569
2233
|
{
|
|
1570
2234
|
...graphProps,
|
|
1571
2235
|
open: graphModalOpen,
|
|
@@ -1573,13 +2237,35 @@ function RulesPanel({ metadata, showConditions = true }) {
|
|
|
1573
2237
|
setGraphModalOpen(false);
|
|
1574
2238
|
},
|
|
1575
2239
|
subtitle: graphSubtitle,
|
|
1576
|
-
layoutKey:
|
|
2240
|
+
layoutKey: "open"
|
|
2241
|
+
}
|
|
2242
|
+
) }) : null,
|
|
2243
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2244
|
+
RuleDetailsModal,
|
|
2245
|
+
{
|
|
2246
|
+
open: detailsRuleId != null,
|
|
2247
|
+
onClose: () => {
|
|
2248
|
+
setDetailsRuleId(null);
|
|
2249
|
+
},
|
|
2250
|
+
ruleId: detailsRuleId,
|
|
2251
|
+
ruleName: detailsRule?.name ?? "",
|
|
2252
|
+
status: detailsStatus,
|
|
2253
|
+
programStageName: detailsProgramStageName,
|
|
2254
|
+
programRuleVariables: metadata.metadata.programRuleVariables
|
|
1577
2255
|
}
|
|
1578
2256
|
)
|
|
1579
2257
|
]
|
|
1580
2258
|
}
|
|
1581
2259
|
);
|
|
1582
2260
|
}
|
|
2261
|
+
function sortRulesFiringFirst(visibleRules, activeRuleIds) {
|
|
2262
|
+
const firing = [];
|
|
2263
|
+
const idle = [];
|
|
2264
|
+
for (const rule of visibleRules) {
|
|
2265
|
+
(activeRuleIds.has(rule.id) ? firing : idle).push(rule);
|
|
2266
|
+
}
|
|
2267
|
+
return [...firing, ...idle];
|
|
2268
|
+
}
|
|
1583
2269
|
function RulesTab({
|
|
1584
2270
|
catalog,
|
|
1585
2271
|
scopeStageId,
|
|
@@ -1588,21 +2274,31 @@ function RulesTab({
|
|
|
1588
2274
|
selectedRuleId,
|
|
1589
2275
|
showConditions,
|
|
1590
2276
|
labelLookup,
|
|
1591
|
-
onSelectRule
|
|
2277
|
+
onSelectRule,
|
|
2278
|
+
onOpenDetails
|
|
1592
2279
|
}) {
|
|
2280
|
+
const listRef = react.useRef(null);
|
|
2281
|
+
const visibleRules = scopeFilter === "all" ? catalog : catalog.filter((rule) => isRuleInScope(rule, scopeStageId));
|
|
2282
|
+
const sortedRules = react.useMemo(
|
|
2283
|
+
() => sortRulesFiringFirst(visibleRules, activeRuleIds),
|
|
2284
|
+
[visibleRules, activeRuleIds]
|
|
2285
|
+
);
|
|
2286
|
+
useFlipReorder(
|
|
2287
|
+
react.useMemo(() => sortedRules.map((rule) => rule.id), [sortedRules]),
|
|
2288
|
+
listRef
|
|
2289
|
+
);
|
|
1593
2290
|
if (!catalog.length) {
|
|
1594
2291
|
return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "m-0 text-sm leading-normal text-dhis2-grey-600", children: translate("This program has no rules.") });
|
|
1595
2292
|
}
|
|
1596
|
-
|
|
1597
|
-
if (!visibleRules.length) {
|
|
2293
|
+
if (!sortedRules.length) {
|
|
1598
2294
|
return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "m-0 text-sm leading-normal text-dhis2-grey-600", children: translate("No rules are in scope for this stage.") });
|
|
1599
2295
|
}
|
|
1600
|
-
return /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "m-0 flex list-none flex-col gap-[10px] p-0", children:
|
|
2296
|
+
return /* @__PURE__ */ jsxRuntime.jsx("ul", { ref: listRef, className: "m-0 flex list-none flex-col gap-[10px] p-0", children: sortedRules.map((rule) => {
|
|
1601
2297
|
const inScope = isRuleInScope(rule, scopeStageId);
|
|
1602
2298
|
const firing = activeRuleIds.has(rule.id);
|
|
1603
2299
|
const isSelected = selectedRuleId === rule.id;
|
|
1604
2300
|
const status = resolveCardStatus(inScope, firing);
|
|
1605
|
-
return /* @__PURE__ */ jsxRuntime.jsx("li", { className: "m-0 shrink-0", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2301
|
+
return /* @__PURE__ */ jsxRuntime.jsx("li", { "data-rule-id": rule.id, className: "m-0 shrink-0", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1606
2302
|
"article",
|
|
1607
2303
|
{
|
|
1608
2304
|
role: "button",
|
|
@@ -1633,13 +2329,29 @@ function RulesTab({
|
|
|
1633
2329
|
children: rule.name
|
|
1634
2330
|
}
|
|
1635
2331
|
),
|
|
1636
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
2332
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex shrink-0 items-center gap-[6px]", children: [
|
|
2333
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2334
|
+
"span",
|
|
2335
|
+
{
|
|
2336
|
+
className: `text-[11px] font-semibold ${status.className}`,
|
|
2337
|
+
children: status.label
|
|
2338
|
+
}
|
|
2339
|
+
),
|
|
2340
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2341
|
+
"button",
|
|
2342
|
+
{
|
|
2343
|
+
type: "button",
|
|
2344
|
+
title: translate("Program rule details"),
|
|
2345
|
+
"aria-label": translate("Program rule details"),
|
|
2346
|
+
onClick: (event) => {
|
|
2347
|
+
event.stopPropagation();
|
|
2348
|
+
onOpenDetails(rule.id);
|
|
2349
|
+
},
|
|
2350
|
+
className: "inline-flex size-[22px] shrink-0 cursor-pointer items-center justify-center rounded-[3px] border-0 bg-transparent text-dhis2-grey-600 hover:bg-dhis2-grey-200 hover:text-dhis2-blue-600",
|
|
2351
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconInfo16, {})
|
|
2352
|
+
}
|
|
2353
|
+
)
|
|
2354
|
+
] })
|
|
1643
2355
|
] }),
|
|
1644
2356
|
rule.programRuleActions.length ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-[6px]", children: rule.programRuleActions.map((action, index) => {
|
|
1645
2357
|
const summary = formatRuleActionSummary(
|
|
@@ -1663,16 +2375,17 @@ function RulesTab({
|
|
|
1663
2375
|
children: rule.condition
|
|
1664
2376
|
}
|
|
1665
2377
|
) : null,
|
|
1666
|
-
!inScope
|
|
1667
|
-
stage: labelLookup.resolveStageName(rule.programStageId)
|
|
1668
|
-
}) }) : null
|
|
2378
|
+
!inScope ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] text-dhis2-grey-600", children: resolveAppliesToCaption(rule, labelLookup) }) : null
|
|
1669
2379
|
]
|
|
1670
2380
|
}
|
|
1671
2381
|
) }, rule.id);
|
|
1672
2382
|
}) });
|
|
1673
2383
|
}
|
|
1674
2384
|
|
|
1675
|
-
|
|
2385
|
+
// src/index.ts
|
|
2386
|
+
init_effectStyles();
|
|
2387
|
+
|
|
2388
|
+
exports.RuleDetailsModal = RuleDetailsModal;
|
|
1676
2389
|
exports.RuleDevtoolsScope = RuleDevtoolsScope;
|
|
1677
2390
|
exports.RulesPanel = RulesPanel;
|
|
1678
2391
|
exports.createLabelLookup = createLabelLookup;
|