@nnkogift/dhis2-form-utils-devtools 0.1.0-alpha.5 → 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 +1230 -1089
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.cts +12 -90
- package/dist/index.d.ts +12 -90
- package/dist/index.js +5 -2240
- 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 +24 -4
package/dist/index.cjs
CHANGED
|
@@ -1,23 +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');
|
|
9
|
-
var
|
|
6
|
+
var react = require('react');
|
|
10
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');
|
|
11
11
|
|
|
12
12
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
13
|
|
|
14
14
|
var i18n__default = /*#__PURE__*/_interopDefault(i18n);
|
|
15
15
|
|
|
16
|
-
|
|
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
|
+
};
|
|
17
30
|
|
|
18
31
|
// src/buildGraph.ts
|
|
19
|
-
var nodeKey = (kind, id) => `${kind}:${id}`;
|
|
20
|
-
var edgeKey = (source, ruleId, target, effectType) => `${source}|${ruleId}|${target}|${effectType ?? ""}`;
|
|
21
32
|
function accumulateGraph(previous, entry, labelLookup) {
|
|
22
33
|
const nodes = new Map(previous.nodes.map((node) => [node.id, node]));
|
|
23
34
|
const edges = new Map(previous.edges.map((edge) => [edge.id, edge]));
|
|
@@ -79,167 +90,13 @@ function buildGraphFromTrace(entries, labelLookup) {
|
|
|
79
90
|
{ nodes: [], edges: [] }
|
|
80
91
|
);
|
|
81
92
|
}
|
|
82
|
-
var
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
});
|
|
88
|
-
function buildEventLookup(source) {
|
|
89
|
-
const rules = /* @__PURE__ */ new Map();
|
|
90
|
-
const fields = /* @__PURE__ */ new Map();
|
|
91
|
-
const sections = /* @__PURE__ */ new Map();
|
|
92
|
-
const stages = /* @__PURE__ */ new Map();
|
|
93
|
-
for (const rule of source.metadata.programRules) {
|
|
94
|
-
if (!rule.id) {
|
|
95
|
-
continue;
|
|
96
|
-
}
|
|
97
|
-
rules.set(rule.id, rule.displayName ?? rule.id);
|
|
98
|
-
}
|
|
99
|
-
const stage = dhis2FormUtilsMetadata.selectProgramStage(
|
|
100
|
-
source.metadata,
|
|
101
|
-
source.programStageId
|
|
102
|
-
) ?? {
|
|
103
|
-
id: source.programStageId,
|
|
104
|
-
programStageDataElements: [],
|
|
105
|
-
programStageSections: []
|
|
106
|
-
};
|
|
107
|
-
for (const psde of stage.programStageDataElements ?? []) {
|
|
108
|
-
const de = psde.dataElement;
|
|
109
|
-
if (!de?.id) {
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
fields.set(de.id, de.displayFormName ?? de.displayName ?? de.id);
|
|
113
|
-
}
|
|
114
|
-
for (const section of stage.programStageSections ?? []) {
|
|
115
|
-
sections.set(section.id, section.displayName ?? section.id);
|
|
116
|
-
}
|
|
117
|
-
for (const programStage of source.metadata.programStages) {
|
|
118
|
-
if (!programStage.id) {
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
stages.set(programStage.id, programStage.displayName ?? programStage.id);
|
|
122
|
-
}
|
|
123
|
-
return createLookup(rules, fields, sections, stages);
|
|
124
|
-
}
|
|
125
|
-
function buildTrackerLookup(source) {
|
|
126
|
-
const rules = /* @__PURE__ */ new Map();
|
|
127
|
-
const fields = /* @__PURE__ */ new Map();
|
|
128
|
-
const sections = /* @__PURE__ */ new Map();
|
|
129
|
-
const stages = /* @__PURE__ */ new Map();
|
|
130
|
-
for (const rule of source.metadata.programRules) {
|
|
131
|
-
if (!rule.id) {
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
rules.set(rule.id, rule.name ?? rule.id);
|
|
135
|
-
}
|
|
136
|
-
for (const ptea of source.metadata.programTrackedEntityAttributes) {
|
|
137
|
-
const tea = ptea.trackedEntityAttribute;
|
|
138
|
-
if (!tea.id) {
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
fields.set(tea.id, tea.formName ?? tea.displayName ?? tea.id);
|
|
142
|
-
}
|
|
143
|
-
for (const section of source.metadata.programSections ?? []) {
|
|
144
|
-
sections.set(section.id, section.displayName ?? section.id);
|
|
145
|
-
}
|
|
146
|
-
for (const programStage of source.programStages ?? []) {
|
|
147
|
-
if (!programStage.id) {
|
|
148
|
-
continue;
|
|
149
|
-
}
|
|
150
|
-
stages.set(programStage.id, programStage.displayName ?? programStage.id);
|
|
151
|
-
}
|
|
152
|
-
return createLookup(rules, fields, sections, stages);
|
|
153
|
-
}
|
|
154
|
-
function createLabelLookup(source) {
|
|
155
|
-
return source.formKind === "event" ? buildEventLookup(source) : buildTrackerLookup(source);
|
|
156
|
-
}
|
|
157
|
-
var EFFECT_VARIANT = {
|
|
158
|
-
HIDEFIELD: "hide",
|
|
159
|
-
HIDEOPTION: "hide",
|
|
160
|
-
HIDEOPTIONGROUP: "hide",
|
|
161
|
-
HIDESECTION: "hide",
|
|
162
|
-
HIDEPROGRAMSTAGE: "hide",
|
|
163
|
-
SHOWFIELD: "show",
|
|
164
|
-
SHOWOPTION: "show",
|
|
165
|
-
SHOWOPTIONGROUP: "show",
|
|
166
|
-
ASSIGN: "assign",
|
|
167
|
-
SETMANDATORYFIELD: "mandatory",
|
|
168
|
-
UNSETMANDATORYFIELD: "mandatory",
|
|
169
|
-
SHOWWARNING: "warning",
|
|
170
|
-
WARNINGONCOMPLETE: "warning",
|
|
171
|
-
SHOWERROR: "error",
|
|
172
|
-
ERRORONCOMPLETE: "error",
|
|
173
|
-
DISPLAYTEXT: "feedback",
|
|
174
|
-
DISPLAYKEYVALUEPAIR: "feedback",
|
|
175
|
-
SENDMESSAGE: "message",
|
|
176
|
-
SCHEDULEMESSAGE: "message",
|
|
177
|
-
read: "read"
|
|
178
|
-
};
|
|
179
|
-
var EFFECT_VISUALS = {
|
|
180
|
-
hide: {
|
|
181
|
-
tagClassName: "bg-dhis2-grey-200 text-dhis2-grey-900",
|
|
182
|
-
edgeStroke: "#494949",
|
|
183
|
-
shortLabel: "hide"
|
|
184
|
-
},
|
|
185
|
-
show: {
|
|
186
|
-
tagClassName: "bg-dhis2-green-100 text-dhis2-green-900",
|
|
187
|
-
edgeStroke: "#388e3c",
|
|
188
|
-
shortLabel: "show"
|
|
189
|
-
},
|
|
190
|
-
assign: {
|
|
191
|
-
tagClassName: "bg-dhis2-blue-100 text-dhis2-blue-900",
|
|
192
|
-
edgeStroke: "#1565c0",
|
|
193
|
-
shortLabel: "assign"
|
|
194
|
-
},
|
|
195
|
-
mandatory: {
|
|
196
|
-
tagClassName: "bg-dhis2-yellow-100 text-dhis2-yellow-900",
|
|
197
|
-
edgeStroke: "#e56408",
|
|
198
|
-
shortLabel: "required"
|
|
199
|
-
},
|
|
200
|
-
warning: {
|
|
201
|
-
tagClassName: "bg-dhis2-yellow-100 text-dhis2-yellow-900",
|
|
202
|
-
edgeStroke: "#ff8302",
|
|
203
|
-
shortLabel: "warn"
|
|
204
|
-
},
|
|
205
|
-
error: {
|
|
206
|
-
tagClassName: "bg-dhis2-red-100 text-dhis2-red-900",
|
|
207
|
-
edgeStroke: "#c62828",
|
|
208
|
-
shortLabel: "error"
|
|
209
|
-
},
|
|
210
|
-
feedback: {
|
|
211
|
-
tagClassName: "bg-dhis2-purple-100 text-dhis2-purple-900",
|
|
212
|
-
edgeStroke: "#9c27b0",
|
|
213
|
-
shortLabel: "feedback"
|
|
214
|
-
},
|
|
215
|
-
message: {
|
|
216
|
-
tagClassName: "bg-dhis2-teal-100 text-dhis2-teal-900",
|
|
217
|
-
edgeStroke: "#00796b",
|
|
218
|
-
shortLabel: "message"
|
|
219
|
-
},
|
|
220
|
-
read: {
|
|
221
|
-
tagClassName: "bg-dhis2-teal-100 text-dhis2-teal-900",
|
|
222
|
-
edgeStroke: "#00796b",
|
|
223
|
-
shortLabel: "read"
|
|
224
|
-
},
|
|
225
|
-
default: {
|
|
226
|
-
tagClassName: "bg-dhis2-grey-100 text-dhis2-grey-800",
|
|
227
|
-
edgeStroke: "#494949",
|
|
228
|
-
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 ?? ""}`;
|
|
229
98
|
}
|
|
230
|
-
};
|
|
231
|
-
var EFFECT_ICONS = {
|
|
232
|
-
hide: ui.IconViewOff16,
|
|
233
|
-
show: ui.IconView16,
|
|
234
|
-
assign: ui.IconEdit16,
|
|
235
|
-
mandatory: ui.IconStarFilled16,
|
|
236
|
-
warning: ui.IconWarningFilled16,
|
|
237
|
-
error: ui.IconErrorFilled16,
|
|
238
|
-
feedback: ui.IconMessages16,
|
|
239
|
-
message: ui.IconMail16,
|
|
240
|
-
read: ui.IconLink16,
|
|
241
|
-
default: ui.IconInfo16
|
|
242
|
-
};
|
|
99
|
+
});
|
|
243
100
|
function getEffectVariant(type) {
|
|
244
101
|
return EFFECT_VARIANT[type] ?? "default";
|
|
245
102
|
}
|
|
@@ -247,7 +104,6 @@ function getEffectVisual(type) {
|
|
|
247
104
|
const variant = getEffectVariant(type);
|
|
248
105
|
return { variant, ...EFFECT_VISUALS[variant] };
|
|
249
106
|
}
|
|
250
|
-
var TAG_LAYOUT_CLASS = "max-w-full break-words";
|
|
251
107
|
function tagPropsForVariant(variant) {
|
|
252
108
|
switch (variant) {
|
|
253
109
|
case "show":
|
|
@@ -287,12 +143,108 @@ function getEffectEdgeStroke(type, highlighted = true) {
|
|
|
287
143
|
function getEffectShortLabel(type) {
|
|
288
144
|
return getEffectVisual(type).shortLabel;
|
|
289
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
|
+
});
|
|
290
238
|
function translate(message, variables) {
|
|
291
239
|
return String(i18n__default.default.t(message, variables));
|
|
292
240
|
}
|
|
241
|
+
var init_i18n = __esm({
|
|
242
|
+
"src/i18n.ts"() {
|
|
243
|
+
}
|
|
244
|
+
});
|
|
293
245
|
function EffectBadge({ type, children, className = "" }) {
|
|
294
246
|
const variant = getEffectVariant(type);
|
|
295
|
-
const Icon = EFFECT_ICONS[variant];
|
|
247
|
+
const Icon = exports.EFFECT_ICONS[variant];
|
|
296
248
|
const tagProps = getEffectTagRenderProps(type);
|
|
297
249
|
const label = children ?? type;
|
|
298
250
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -306,23 +258,13 @@ function EffectBadge({ type, children, className = "" }) {
|
|
|
306
258
|
}
|
|
307
259
|
);
|
|
308
260
|
}
|
|
309
|
-
var LEGEND_VARIANTS = [
|
|
310
|
-
{ variant: "read", label: translate("Read") },
|
|
311
|
-
{ variant: "hide", label: translate("Hide") },
|
|
312
|
-
{ variant: "show", label: translate("Show") },
|
|
313
|
-
{ variant: "assign", label: translate("Assign") },
|
|
314
|
-
{ variant: "mandatory", label: translate("Required") },
|
|
315
|
-
{ variant: "warning", label: translate("Warning") },
|
|
316
|
-
{ variant: "error", label: translate("Error") },
|
|
317
|
-
{ variant: "feedback", label: translate("Feedback") }
|
|
318
|
-
];
|
|
319
261
|
function EffectLegend({ activeVariants }) {
|
|
320
262
|
const items = LEGEND_VARIANTS.filter((item) => activeVariants.has(item.variant));
|
|
321
263
|
if (!items.length) {
|
|
322
264
|
return null;
|
|
323
265
|
}
|
|
324
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) => {
|
|
325
|
-
const Icon = EFFECT_ICONS[item.variant];
|
|
267
|
+
const Icon = exports.EFFECT_ICONS[item.variant];
|
|
326
268
|
const tagProps = getEffectTagRenderPropsForVariant(item.variant);
|
|
327
269
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
328
270
|
ui.Tag,
|
|
@@ -336,115 +278,846 @@ function EffectLegend({ activeVariants }) {
|
|
|
336
278
|
);
|
|
337
279
|
}) });
|
|
338
280
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
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
|
+
];
|
|
349
296
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
// src/traceEntry.ts
|
|
300
|
+
function resolveGraphTraceEntry(entries, selectedEntryId) {
|
|
301
|
+
if (!entries.length) {
|
|
302
|
+
return null;
|
|
353
303
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
return translate("{{hours}}h ago", { hours });
|
|
304
|
+
if (selectedEntryId) {
|
|
305
|
+
return entries.find((entry) => entry.id === selectedEntryId) ?? entries[entries.length - 1];
|
|
357
306
|
}
|
|
358
|
-
return
|
|
307
|
+
return entries[entries.length - 1];
|
|
359
308
|
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
const resolved = labelLookup.resolveFieldName(id);
|
|
365
|
-
if (resolved !== id) {
|
|
366
|
-
return resolved;
|
|
367
|
-
}
|
|
309
|
+
function getActiveRuleIds(entries) {
|
|
310
|
+
const latest = resolveGraphTraceEntry(entries);
|
|
311
|
+
if (!latest) {
|
|
312
|
+
return /* @__PURE__ */ new Set();
|
|
368
313
|
}
|
|
369
|
-
return
|
|
314
|
+
return new Set(latest.ruleResults.map((result) => result.ruleId));
|
|
370
315
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
const resolved = labelLookup.resolveSectionName(id);
|
|
374
|
-
if (resolved !== id) {
|
|
375
|
-
return resolved;
|
|
376
|
-
}
|
|
316
|
+
var init_traceEntry = __esm({
|
|
317
|
+
"src/traceEntry.ts"() {
|
|
377
318
|
}
|
|
378
|
-
|
|
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];
|
|
379
326
|
}
|
|
380
|
-
function
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
if (action.dataElement?.id) {
|
|
384
|
-
targetLabel = resolveFieldLabel(
|
|
385
|
-
action.dataElement.id,
|
|
386
|
-
action.dataElement.displayName,
|
|
387
|
-
labelLookup
|
|
388
|
-
);
|
|
389
|
-
} else if (action.trackedEntityAttribute?.id) {
|
|
390
|
-
targetLabel = resolveFieldLabel(
|
|
391
|
-
action.trackedEntityAttribute.id,
|
|
392
|
-
action.trackedEntityAttribute.displayName,
|
|
393
|
-
labelLookup
|
|
394
|
-
);
|
|
395
|
-
} else if (action.programStageSection?.id) {
|
|
396
|
-
targetLabel = resolveSectionLabel(
|
|
397
|
-
action.programStageSection.id,
|
|
398
|
-
action.programStageSection.displayName,
|
|
399
|
-
labelLookup
|
|
400
|
-
);
|
|
401
|
-
} else if (action.programSection?.id) {
|
|
402
|
-
targetLabel = resolveSectionLabel(
|
|
403
|
-
action.programSection.id,
|
|
404
|
-
action.programSection.displayName,
|
|
405
|
-
labelLookup
|
|
406
|
-
);
|
|
407
|
-
} else if (type === "DISPLAYTEXT" || type === "DISPLAYKEYVALUEPAIR") {
|
|
408
|
-
targetLabel = action.location ?? "feedback";
|
|
409
|
-
}
|
|
410
|
-
let detail;
|
|
411
|
-
if (action.data) {
|
|
412
|
-
detail = action.data;
|
|
413
|
-
}
|
|
414
|
-
if (action.content && (type === "DISPLAYTEXT" || type === "DISPLAYKEYVALUEPAIR")) {
|
|
415
|
-
detail = action.content;
|
|
327
|
+
function staggerOffset(indexInBucket) {
|
|
328
|
+
if (indexInBucket === 0) {
|
|
329
|
+
return 0;
|
|
416
330
|
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
// src/resolveProgramRulesList.ts
|
|
421
|
-
function sortByPriority(rules) {
|
|
422
|
-
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;
|
|
423
333
|
}
|
|
424
|
-
function
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
priority: rule.priority,
|
|
430
|
-
programRuleActions: [...rule.programRuleActions ?? []],
|
|
431
|
-
programStageId: rule.programStageId
|
|
432
|
-
};
|
|
334
|
+
function columnFor(node) {
|
|
335
|
+
if (node.role === "target" && node.kind === "feedback") {
|
|
336
|
+
return FEEDBACK_COLUMN;
|
|
337
|
+
}
|
|
338
|
+
return ROLE_COLUMNS[node.role];
|
|
433
339
|
}
|
|
434
|
-
function
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
)
|
|
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
|
+
}
|
|
349
|
+
}
|
|
350
|
+
const flowNodes = [];
|
|
351
|
+
const endpointByGraphId = /* @__PURE__ */ new Map();
|
|
352
|
+
for (const node of graph.nodes) {
|
|
353
|
+
if (node.kind === "rule") {
|
|
354
|
+
flowNodes.push({
|
|
355
|
+
id: node.id,
|
|
356
|
+
graphNodeId: node.id,
|
|
357
|
+
kind: node.kind,
|
|
358
|
+
label: node.label,
|
|
359
|
+
role: "rule"
|
|
360
|
+
});
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
const isSource = readSources.has(node.id);
|
|
364
|
+
const isTarget = effectTargets.has(node.id);
|
|
365
|
+
const endpoints = {};
|
|
366
|
+
if (isSource) {
|
|
367
|
+
const flowId = isTarget ? `${node.id}@src` : node.id;
|
|
368
|
+
endpoints.source = flowId;
|
|
369
|
+
flowNodes.push({
|
|
370
|
+
id: flowId,
|
|
371
|
+
graphNodeId: node.id,
|
|
372
|
+
kind: node.kind,
|
|
373
|
+
label: node.label,
|
|
374
|
+
role: "source"
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
if (isTarget) {
|
|
378
|
+
const flowId = isSource ? `${node.id}@tgt` : node.id;
|
|
379
|
+
endpoints.target = flowId;
|
|
380
|
+
flowNodes.push({
|
|
381
|
+
id: flowId,
|
|
382
|
+
graphNodeId: node.id,
|
|
383
|
+
kind: node.kind,
|
|
384
|
+
label: node.label,
|
|
385
|
+
role: "target"
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
endpointByGraphId.set(node.id, endpoints);
|
|
389
|
+
}
|
|
390
|
+
const flowEdges = graph.edges.map((edge) => {
|
|
391
|
+
if (edge.effectType === "read") {
|
|
392
|
+
const endpoints2 = endpointByGraphId.get(edge.source);
|
|
393
|
+
return { ...edge, source: endpoints2?.source ?? edge.source };
|
|
394
|
+
}
|
|
395
|
+
const endpoints = endpointByGraphId.get(edge.target);
|
|
396
|
+
return { ...edge, target: endpoints?.target ?? edge.target };
|
|
397
|
+
});
|
|
398
|
+
return { nodes: flowNodes, edges: flowEdges };
|
|
399
|
+
}
|
|
400
|
+
function computeGraphLayout(flowNodes, flowEdges) {
|
|
401
|
+
const positions = /* @__PURE__ */ new Map();
|
|
402
|
+
const ruleLane = /* @__PURE__ */ new Map();
|
|
403
|
+
flowNodes.filter((node) => node.role === "rule").forEach((node, index) => {
|
|
404
|
+
ruleLane.set(node.id, index);
|
|
405
|
+
positions.set(node.id, { x: ROLE_COLUMNS.rule, y: index * LANE_HEIGHT });
|
|
406
|
+
});
|
|
407
|
+
const connectedRuleLanes = /* @__PURE__ */ new Map();
|
|
408
|
+
const addLane = (nodeId, lane) => {
|
|
409
|
+
const lanes = connectedRuleLanes.get(nodeId) ?? /* @__PURE__ */ new Set();
|
|
410
|
+
lanes.add(lane);
|
|
411
|
+
connectedRuleLanes.set(nodeId, lanes);
|
|
412
|
+
};
|
|
413
|
+
for (const edge of flowEdges) {
|
|
414
|
+
const sourceLane = ruleLane.get(edge.source);
|
|
415
|
+
const targetLane = ruleLane.get(edge.target);
|
|
416
|
+
if (targetLane !== void 0 && sourceLane === void 0) {
|
|
417
|
+
addLane(edge.source, targetLane);
|
|
418
|
+
}
|
|
419
|
+
if (sourceLane !== void 0 && targetLane === void 0) {
|
|
420
|
+
addLane(edge.target, sourceLane);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
const bucketCounts = /* @__PURE__ */ new Map();
|
|
424
|
+
for (const node of flowNodes) {
|
|
425
|
+
if (node.role === "rule") {
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
const lanes = connectedRuleLanes.get(node.id);
|
|
429
|
+
const lane = lanes && lanes.size > 0 ? median([...lanes]) : 0;
|
|
430
|
+
const column = columnFor(node);
|
|
431
|
+
const bucketKey = `${String(column)}:${String(lane)}`;
|
|
432
|
+
const indexInBucket = bucketCounts.get(bucketKey) ?? 0;
|
|
433
|
+
bucketCounts.set(bucketKey, indexInBucket + 1);
|
|
434
|
+
positions.set(node.id, {
|
|
435
|
+
x: column,
|
|
436
|
+
y: lane * LANE_HEIGHT + staggerOffset(indexInBucket)
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
return positions;
|
|
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
|
+
});
|
|
460
|
+
|
|
461
|
+
// src/graphNodeStyles.ts
|
|
462
|
+
function getGraphNodeClassName(kind, highlighted) {
|
|
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"}`;
|
|
464
|
+
}
|
|
465
|
+
function getLegendSwatchClassName(kind) {
|
|
466
|
+
return `size-2.5 shrink-0 rounded-sm ${LEGEND_SWATCH_CLASSES[kind]}`;
|
|
467
|
+
}
|
|
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
|
+
};
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
// src/RuleGraphView.tsx
|
|
487
|
+
var RuleGraphView_exports = {};
|
|
488
|
+
__export(RuleGraphView_exports, {
|
|
489
|
+
RuleGraphView: () => RuleGraphView,
|
|
490
|
+
toFlowGraph: () => toFlowGraph
|
|
491
|
+
});
|
|
492
|
+
function RuleGraphNode({ data }) {
|
|
493
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: getGraphNodeClassName(data.kind, data.highlighted), children: [
|
|
494
|
+
/* @__PURE__ */ jsxRuntime.jsx(react$1.Handle, { type: "target", position: react$1.Position.Left }),
|
|
495
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-dp4 block text-[0.625rem] font-semibold uppercase leading-none tracking-wide text-dhis2-grey-600", children: KIND_LABELS[data.kind] }),
|
|
496
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "break-words font-semibold leading-[1.3]", children: data.label }),
|
|
497
|
+
data.value !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-dp4 border-t border-dhis2-grey-200 pt-dp4 font-mono text-[0.6875rem] break-all text-dhis2-grey-700", children: data.value }) : null,
|
|
498
|
+
/* @__PURE__ */ jsxRuntime.jsx(react$1.Handle, { type: "source", position: react$1.Position.Right })
|
|
499
|
+
] });
|
|
500
|
+
}
|
|
501
|
+
function RuleGraphEdge({
|
|
502
|
+
id,
|
|
503
|
+
sourceX,
|
|
504
|
+
sourceY,
|
|
505
|
+
sourcePosition,
|
|
506
|
+
targetX,
|
|
507
|
+
targetY,
|
|
508
|
+
targetPosition,
|
|
509
|
+
markerEnd,
|
|
510
|
+
style,
|
|
511
|
+
data
|
|
512
|
+
}) {
|
|
513
|
+
const [hovered, setHovered] = react.useState(false);
|
|
514
|
+
const [edgePath, labelX, labelY] = react$1.getSmoothStepPath({
|
|
515
|
+
sourceX,
|
|
516
|
+
sourceY,
|
|
517
|
+
sourcePosition,
|
|
518
|
+
targetX,
|
|
519
|
+
targetY,
|
|
520
|
+
targetPosition,
|
|
521
|
+
offset: data?.offset ?? 20
|
|
522
|
+
});
|
|
523
|
+
const showLabel = data ? data.showLabel || hovered : false;
|
|
524
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
525
|
+
/* @__PURE__ */ jsxRuntime.jsx(react$1.BaseEdge, { id, path: edgePath, markerEnd, style }),
|
|
526
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
527
|
+
"path",
|
|
528
|
+
{
|
|
529
|
+
d: edgePath,
|
|
530
|
+
fill: "none",
|
|
531
|
+
stroke: "transparent",
|
|
532
|
+
strokeWidth: 16,
|
|
533
|
+
onMouseEnter: () => {
|
|
534
|
+
setHovered(true);
|
|
535
|
+
},
|
|
536
|
+
onMouseLeave: () => {
|
|
537
|
+
setHovered(false);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
),
|
|
541
|
+
showLabel && data ? /* @__PURE__ */ jsxRuntime.jsx(react$1.EdgeLabelRenderer, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
542
|
+
"div",
|
|
543
|
+
{
|
|
544
|
+
className: "nodrag nopan absolute rounded-sm px-dp4 text-[10px] font-semibold",
|
|
545
|
+
style: {
|
|
546
|
+
transform: `translate(-50%, -50%) translate(${String(labelX)}px, ${String(labelY)}px)`,
|
|
547
|
+
background: "rgba(255,255,255,0.92)",
|
|
548
|
+
color: data.stroke,
|
|
549
|
+
pointerEvents: "all"
|
|
550
|
+
},
|
|
551
|
+
onMouseEnter: () => {
|
|
552
|
+
setHovered(true);
|
|
553
|
+
},
|
|
554
|
+
onMouseLeave: () => {
|
|
555
|
+
setHovered(false);
|
|
556
|
+
},
|
|
557
|
+
children: data.label
|
|
558
|
+
}
|
|
559
|
+
) }) : null
|
|
560
|
+
] });
|
|
561
|
+
}
|
|
562
|
+
function effectNodeKey(effect) {
|
|
563
|
+
const kind = effect.type === "HIDESECTION" ? "section" : effect.type === "DISPLAYTEXT" || effect.type === "DISPLAYKEYVALUEPAIR" ? "feedback" : "field";
|
|
564
|
+
return `${kind}:${effect.targetId}`;
|
|
565
|
+
}
|
|
566
|
+
function entryGraphKeys(entry) {
|
|
567
|
+
const keys = /* @__PURE__ */ new Set();
|
|
568
|
+
for (const fieldId of entry.changedFields) {
|
|
569
|
+
keys.add(`field:${fieldId}`);
|
|
570
|
+
}
|
|
571
|
+
for (const result of entry.ruleResults) {
|
|
572
|
+
keys.add(`rule:${result.ruleId}`);
|
|
573
|
+
for (const effect of result.effects) {
|
|
574
|
+
keys.add(effectNodeKey(effect));
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
return keys;
|
|
578
|
+
}
|
|
579
|
+
function ruleGraphKeys(entry, ruleId) {
|
|
580
|
+
const keys = /* @__PURE__ */ new Set([`rule:${ruleId}`]);
|
|
581
|
+
for (const result of entry.ruleResults) {
|
|
582
|
+
if (result.ruleId !== ruleId) {
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
for (const effect of result.effects) {
|
|
586
|
+
keys.add(effectNodeKey(effect));
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
return keys;
|
|
590
|
+
}
|
|
591
|
+
function toFlowGraph(graph, fieldState, formValues, highlightedKeys) {
|
|
592
|
+
const { nodes: flowNodes, edges: flowEdges } = prepareFlowGraph(graph);
|
|
593
|
+
const positions = computeGraphLayout(flowNodes, flowEdges);
|
|
594
|
+
const graphNodeIdByFlowId = new Map(flowNodes.map((node) => [node.id, node.graphNodeId]));
|
|
595
|
+
const nodes = flowNodes.map((node) => {
|
|
596
|
+
const position = positions.get(node.id) ?? { x: 0, y: 0 };
|
|
597
|
+
const rawFieldId = node.kind === "field" ? node.graphNodeId.slice("field:".length) : void 0;
|
|
598
|
+
const assignedValue = rawFieldId && rawFieldId in fieldState ? fieldState[rawFieldId].assignedValue : void 0;
|
|
599
|
+
const formValue = rawFieldId ? formValues[rawFieldId] : void 0;
|
|
600
|
+
const displayValue = formatDisplayValue(assignedValue) ?? formatDisplayValue(formValue);
|
|
601
|
+
return {
|
|
602
|
+
id: node.id,
|
|
603
|
+
type: "ruleGraphNode",
|
|
604
|
+
position,
|
|
605
|
+
data: {
|
|
606
|
+
label: node.label,
|
|
607
|
+
kind: node.kind,
|
|
608
|
+
graphNodeId: node.graphNodeId,
|
|
609
|
+
value: displayValue,
|
|
610
|
+
highlighted: highlightedKeys ? highlightedKeys.has(node.graphNodeId) : true
|
|
611
|
+
}
|
|
612
|
+
};
|
|
613
|
+
});
|
|
614
|
+
const dense = flowEdges.length > READ_LABEL_EDGE_THRESHOLD;
|
|
615
|
+
const sourceFanOut = /* @__PURE__ */ new Map();
|
|
616
|
+
const edges = flowEdges.map((edge) => {
|
|
617
|
+
const sourceGraphId = graphNodeIdByFlowId.get(edge.source) ?? edge.source;
|
|
618
|
+
const targetGraphId = graphNodeIdByFlowId.get(edge.target) ?? edge.target;
|
|
619
|
+
const isHighlighted = highlightedKeys ? highlightedKeys.has(sourceGraphId) && highlightedKeys.has(targetGraphId) : true;
|
|
620
|
+
const effectType = edge.effectType ?? "default";
|
|
621
|
+
const isRead = effectType === "read";
|
|
622
|
+
const stroke = getEffectEdgeStroke(effectType, isHighlighted);
|
|
623
|
+
const label = getEffectShortLabel(effectType);
|
|
624
|
+
const showLabel = !(isRead && dense);
|
|
625
|
+
const fanIndex = sourceFanOut.get(edge.source) ?? 0;
|
|
626
|
+
sourceFanOut.set(edge.source, fanIndex + 1);
|
|
627
|
+
const offset = 20 + fanIndex * 14;
|
|
628
|
+
return {
|
|
629
|
+
id: edge.id,
|
|
630
|
+
source: edge.source,
|
|
631
|
+
target: edge.target,
|
|
632
|
+
type: "ruleGraphEdge",
|
|
633
|
+
animated: isHighlighted && !isRead,
|
|
634
|
+
zIndex: isHighlighted ? 1 : 0,
|
|
635
|
+
style: {
|
|
636
|
+
stroke,
|
|
637
|
+
strokeWidth: isRead ? 1 : Math.min(1 + edge.fireCount, 4),
|
|
638
|
+
opacity: isHighlighted ? isRead ? 0.5 : 1 : 0.35
|
|
639
|
+
},
|
|
640
|
+
markerEnd: {
|
|
641
|
+
type: react$1.MarkerType.ArrowClosed,
|
|
642
|
+
color: stroke
|
|
643
|
+
},
|
|
644
|
+
data: {
|
|
645
|
+
label,
|
|
646
|
+
stroke,
|
|
647
|
+
showLabel,
|
|
648
|
+
offset
|
|
649
|
+
}
|
|
650
|
+
};
|
|
651
|
+
});
|
|
652
|
+
return { nodes, edges };
|
|
653
|
+
}
|
|
654
|
+
function GraphToolbar({
|
|
655
|
+
nodeCount,
|
|
656
|
+
edgeCount,
|
|
657
|
+
headerActions,
|
|
658
|
+
activeEffectVariants
|
|
659
|
+
}) {
|
|
660
|
+
const items = [
|
|
661
|
+
{ kind: "field", label: translate("Field") },
|
|
662
|
+
{ kind: "rule", label: translate("Rule") },
|
|
663
|
+
{ kind: "section", label: translate("Section") },
|
|
664
|
+
{ kind: "feedback", label: translate("Feedback") }
|
|
665
|
+
];
|
|
666
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shrink-0 border-b border-dhis2-grey-200 bg-white", children: [
|
|
667
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-dp8 px-dp12 py-dp8", children: [
|
|
668
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
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
|
+
);
|
|
448
1121
|
}
|
|
449
1122
|
return sortByPriority(
|
|
450
1123
|
metadata.metadata.programRules.filter((rule) => Boolean(rule.id)).map(
|
|
@@ -524,6 +1197,10 @@ function useRuleTraceStore() {
|
|
|
524
1197
|
return store;
|
|
525
1198
|
}
|
|
526
1199
|
|
|
1200
|
+
// src/RuleDetailsModal.tsx
|
|
1201
|
+
init_effectStyles();
|
|
1202
|
+
init_i18n();
|
|
1203
|
+
|
|
527
1204
|
// src/programRuleDetailQuery.ts
|
|
528
1205
|
var PROGRAM_RULE_ACTION_DETAIL_FIELDS = [
|
|
529
1206
|
"id",
|
|
@@ -704,860 +1381,300 @@ function BasicDetailCell({ label, value, mono }) {
|
|
|
704
1381
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
|
|
705
1382
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "m-0 text-xs text-dhis2-grey-600", children: label }),
|
|
706
1383
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: `m-0 mt-[2px] text-sm text-dhis2-grey-900 ${mono ? "font-mono" : ""}`, children: value })
|
|
707
|
-
] });
|
|
708
|
-
}
|
|
709
|
-
var FEEDBACK_ACTION_TYPES = /* @__PURE__ */ new Set(["DISPLAYTEXT", "DISPLAYKEYVALUEPAIR"]);
|
|
710
|
-
function resolveDataRow(action) {
|
|
711
|
-
return action.data ? { label: translate("Data (expression)"), value: action.data, mono: true } : null;
|
|
712
|
-
}
|
|
713
|
-
function resolveContentRow(action) {
|
|
714
|
-
const showContent = Boolean(action.content) && FEEDBACK_ACTION_TYPES.has(action.programRuleActionType);
|
|
715
|
-
return showContent ? { label: translate("Content (static text)"), value: action.content ?? "" } : null;
|
|
716
|
-
}
|
|
717
|
-
function resolveLocationRow(action) {
|
|
718
|
-
return action.location ? { label: translate("Location"), value: action.location, mono: true } : null;
|
|
719
|
-
}
|
|
720
|
-
function resolveActionRows(action) {
|
|
721
|
-
const rows = [
|
|
722
|
-
resolveActionTarget(action),
|
|
723
|
-
resolveDataRow(action),
|
|
724
|
-
resolveContentRow(action),
|
|
725
|
-
resolveLocationRow(action)
|
|
726
|
-
];
|
|
727
|
-
return rows.filter((row) => row !== null);
|
|
728
|
-
}
|
|
729
|
-
function ActionCard({ action, index }) {
|
|
730
|
-
const type = action.programRuleActionType;
|
|
731
|
-
const visual = getEffectVisual(type);
|
|
732
|
-
const Icon = EFFECT_ICONS[visual.variant];
|
|
733
|
-
const rows = resolveActionRows(action);
|
|
734
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
735
|
-
"div",
|
|
736
|
-
{
|
|
737
|
-
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%)]",
|
|
738
|
-
style: { borderInlineStartWidth: 3, borderInlineStartColor: visual.edgeStroke },
|
|
739
|
-
children: [
|
|
740
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-dp12 flex items-center justify-between gap-dp8", children: [
|
|
741
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
742
|
-
"span",
|
|
743
|
-
{
|
|
744
|
-
className: `inline-flex items-center gap-[4px] rounded-[4px] px-dp8 py-[2px] text-xs font-semibold ${visual.tagClassName}`,
|
|
745
|
-
children: [
|
|
746
|
-
/* @__PURE__ */ jsxRuntime.jsx(Icon, {}),
|
|
747
|
-
type
|
|
748
|
-
]
|
|
749
|
-
}
|
|
750
|
-
),
|
|
751
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] uppercase tracking-wide text-dhis2-grey-600", children: translate("Action {{n}}", { n: index + 1 }) })
|
|
752
|
-
] }),
|
|
753
|
-
/* @__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: [
|
|
754
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-dhis2-grey-600", children: row.label }),
|
|
755
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
756
|
-
"span",
|
|
757
|
-
{
|
|
758
|
-
className: `min-w-0 break-words text-sm ${row.mono ? "font-mono text-[12px]" : ""}`,
|
|
759
|
-
children: row.value
|
|
760
|
-
}
|
|
761
|
-
)
|
|
762
|
-
] }, row.label)) })
|
|
763
|
-
]
|
|
764
|
-
}
|
|
765
|
-
);
|
|
766
|
-
}
|
|
767
|
-
function orDash(value) {
|
|
768
|
-
return value ?? EM_DASH;
|
|
769
|
-
}
|
|
770
|
-
function resolveDetailDisplayName(rule) {
|
|
771
|
-
return rule?.displayName;
|
|
772
|
-
}
|
|
773
|
-
function resolveDetailName(rule) {
|
|
774
|
-
return rule?.name;
|
|
775
|
-
}
|
|
776
|
-
function resolveRuleDisplayName(rule, fallback) {
|
|
777
|
-
const displayName = resolveDetailDisplayName(rule) ?? resolveDetailName(rule);
|
|
778
|
-
return displayName ?? fallback;
|
|
779
|
-
}
|
|
780
|
-
function resolveRuleStageName(rule) {
|
|
781
|
-
return rule?.programStage?.displayName;
|
|
782
|
-
}
|
|
783
|
-
function resolveStageName(rule, programStageName) {
|
|
784
|
-
if (programStageName === null) {
|
|
785
|
-
return translate("All stages (registration)");
|
|
786
|
-
}
|
|
787
|
-
return orDash(resolveRuleStageName(rule) ?? programStageName);
|
|
788
|
-
}
|
|
789
|
-
function resolveLastUpdatedByName(rule) {
|
|
790
|
-
return rule?.lastUpdatedBy?.displayName;
|
|
791
|
-
}
|
|
792
|
-
function resolveLastUpdatedValue(rule) {
|
|
793
|
-
const label = formatTimestamp(rule?.lastUpdated);
|
|
794
|
-
if (!label) {
|
|
795
|
-
return EM_DASH;
|
|
796
|
-
}
|
|
797
|
-
const by = resolveLastUpdatedByName(rule);
|
|
798
|
-
return by ? `${label} \xB7 ${by}` : label;
|
|
799
|
-
}
|
|
800
|
-
function resolvePriorityCellValue(rule) {
|
|
801
|
-
return rule?.priority != null ? String(rule.priority) : EM_DASH;
|
|
802
|
-
}
|
|
803
|
-
function resolveActionsCellValue(rule) {
|
|
804
|
-
return translate("{{n}} action(s)", { n: rule?.programRuleActions?.length ?? 0 });
|
|
805
|
-
}
|
|
806
|
-
function resolveBasicDetailCells(rule, ruleName, programStageName) {
|
|
807
|
-
return [
|
|
808
|
-
{ label: translate("Name"), value: resolveRuleDisplayName(rule, ruleName) },
|
|
809
|
-
{ label: translate("Code"), value: orDash(rule?.code), mono: true },
|
|
810
|
-
{ label: translate("Identifier"), value: orDash(rule?.id), mono: true },
|
|
811
|
-
{ label: translate("Program"), value: orDash(rule?.program?.displayName) },
|
|
812
|
-
{ label: translate("Program stage"), value: resolveStageName(rule, programStageName) },
|
|
813
|
-
{ label: translate("Priority"), value: resolvePriorityCellValue(rule) },
|
|
814
|
-
{ label: translate("Actions"), value: resolveActionsCellValue(rule) },
|
|
815
|
-
{ label: translate("Last updated"), value: resolveLastUpdatedValue(rule) }
|
|
816
|
-
];
|
|
817
|
-
}
|
|
818
|
-
function BasicDetails({
|
|
819
|
-
rule,
|
|
820
|
-
ruleName,
|
|
821
|
-
programStageName
|
|
822
|
-
}) {
|
|
823
|
-
const cells = resolveBasicDetailCells(rule, ruleName, programStageName);
|
|
824
|
-
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)) });
|
|
825
|
-
}
|
|
826
|
-
var SECTION_HEADING_CLASS = "m-0 mb-dp8 text-[13px] font-bold uppercase tracking-wide text-dhis2-grey-700";
|
|
827
|
-
function RuleDetailsHeader({
|
|
828
|
-
title,
|
|
829
|
-
chip,
|
|
830
|
-
description
|
|
831
|
-
}) {
|
|
832
|
-
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: [
|
|
833
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "m-0 text-[11px] font-bold uppercase tracking-[.09em] text-dhis2-grey-600", children: translate("Program rule") }),
|
|
834
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-[6px] flex flex-wrap items-center gap-dp8", children: [
|
|
835
|
-
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "m-0 text-xl font-medium leading-[1.3] text-dhis2-grey-900", children: title }),
|
|
836
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
837
|
-
"span",
|
|
838
|
-
{
|
|
839
|
-
className: `rounded-[4px] px-dp8 py-[2px] text-xs font-semibold ${chip.className}`,
|
|
840
|
-
children: chip.label
|
|
841
|
-
}
|
|
842
|
-
)
|
|
843
|
-
] }),
|
|
844
|
-
description ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
845
|
-
"p",
|
|
846
|
-
{
|
|
847
|
-
className: "m-0 mt-[6px] text-sm text-dhis2-grey-700",
|
|
848
|
-
style: { textWrap: "pretty" },
|
|
849
|
-
children: description
|
|
850
|
-
}
|
|
851
|
-
) : null
|
|
852
|
-
] });
|
|
853
|
-
}
|
|
854
|
-
function ConditionSection({
|
|
855
|
-
condition,
|
|
856
|
-
variables
|
|
857
|
-
}) {
|
|
858
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("section", { children: [
|
|
859
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: SECTION_HEADING_CLASS, children: translate("Condition") }),
|
|
860
|
-
/* @__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 }),
|
|
861
|
-
variables.length ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-dp12", children: [
|
|
862
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "m-0 mb-dp8 text-xs text-dhis2-grey-600", children: translate("Variables referenced") }),
|
|
863
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-dp8", children: variables.map((variable) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
864
|
-
"span",
|
|
865
|
-
{
|
|
866
|
-
className: `inline-flex items-center gap-[4px] rounded-full px-dp8 py-[2px] text-xs ${variable.className}`,
|
|
867
|
-
children: [
|
|
868
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono font-medium", children: variable.label }),
|
|
869
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-70", children: translate(variable.kind) })
|
|
870
|
-
]
|
|
871
|
-
},
|
|
872
|
-
variable.token
|
|
873
|
-
)) })
|
|
874
|
-
] }) : null
|
|
875
|
-
] });
|
|
876
|
-
}
|
|
877
|
-
function ActionsSection({ actions }) {
|
|
878
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("section", { children: [
|
|
879
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: SECTION_HEADING_CLASS, children: translate("Actions") }),
|
|
880
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-dp12", children: actions.map((action, index) => /* @__PURE__ */ jsxRuntime.jsx(ActionCard, { action, index }, action.id)) })
|
|
881
|
-
] });
|
|
882
|
-
}
|
|
883
|
-
function RuleDetailsContent({
|
|
884
|
-
detail,
|
|
885
|
-
ruleName,
|
|
886
|
-
programStageName,
|
|
887
|
-
variables
|
|
888
|
-
}) {
|
|
889
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-dp24 pt-dp16", children: [
|
|
890
|
-
/* @__PURE__ */ jsxRuntime.jsxs("section", { children: [
|
|
891
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: SECTION_HEADING_CLASS, children: translate("Basic details") }),
|
|
892
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
893
|
-
BasicDetails,
|
|
894
|
-
{
|
|
895
|
-
rule: detail,
|
|
896
|
-
ruleName,
|
|
897
|
-
programStageName
|
|
898
|
-
}
|
|
899
|
-
)
|
|
900
|
-
] }),
|
|
901
|
-
/* @__PURE__ */ jsxRuntime.jsx(ConditionSection, { condition: detail.condition, variables }),
|
|
902
|
-
/* @__PURE__ */ jsxRuntime.jsx(ActionsSection, { actions: detail.programRuleActions ?? [] })
|
|
903
|
-
] });
|
|
904
|
-
}
|
|
905
|
-
function RuleDetailsBody({
|
|
906
|
-
detail,
|
|
907
|
-
loading,
|
|
908
|
-
error,
|
|
909
|
-
ruleName,
|
|
910
|
-
programStageName,
|
|
911
|
-
variables
|
|
912
|
-
}) {
|
|
913
|
-
if (loading) {
|
|
914
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[280px] items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.CircularLoader, { small: true }) });
|
|
915
|
-
}
|
|
916
|
-
if (error) {
|
|
917
|
-
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 }) });
|
|
918
|
-
}
|
|
919
|
-
if (!detail) {
|
|
920
|
-
return null;
|
|
921
|
-
}
|
|
922
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
923
|
-
RuleDetailsContent,
|
|
924
|
-
{
|
|
925
|
-
detail,
|
|
926
|
-
ruleName,
|
|
927
|
-
programStageName,
|
|
928
|
-
variables
|
|
929
|
-
}
|
|
930
|
-
);
|
|
1384
|
+
] });
|
|
931
1385
|
}
|
|
932
|
-
|
|
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);
|
|
933
1411
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
934
1412
|
"div",
|
|
935
1413
|
{
|
|
936
|
-
|
|
937
|
-
|
|
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 },
|
|
938
1416
|
children: [
|
|
939
|
-
/* @__PURE__ */ jsxRuntime.
|
|
940
|
-
|
|
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)) })
|
|
941
1440
|
]
|
|
942
1441
|
}
|
|
943
1442
|
);
|
|
944
1443
|
}
|
|
945
|
-
function
|
|
946
|
-
return
|
|
947
|
-
}
|
|
948
|
-
function shouldRenderModal(open, ruleId) {
|
|
949
|
-
return open && ruleId != null;
|
|
950
|
-
}
|
|
951
|
-
function RuleDetailsModal({
|
|
952
|
-
open,
|
|
953
|
-
onClose,
|
|
954
|
-
ruleId,
|
|
955
|
-
ruleName,
|
|
956
|
-
status,
|
|
957
|
-
programStageName,
|
|
958
|
-
programRuleVariables
|
|
959
|
-
}) {
|
|
960
|
-
const { detail, loading, error } = useProgramRuleDetail(resolveActiveRuleId(open, ruleId));
|
|
961
|
-
if (!shouldRenderModal(open, ruleId)) {
|
|
962
|
-
return null;
|
|
963
|
-
}
|
|
964
|
-
const chip = resolveStatusChip(status);
|
|
965
|
-
const variables = parseConditionVariables(detail?.condition, programRuleVariables);
|
|
966
|
-
const title = resolveRuleDisplayName(detail, ruleName);
|
|
967
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Modal, { position: "middle", onClose, children: [
|
|
968
|
-
/* @__PURE__ */ jsxRuntime.jsx(RuleDetailsHeader, { title, chip, description: detail?.description }),
|
|
969
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.ModalContent, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
970
|
-
RuleDetailsBody,
|
|
971
|
-
{
|
|
972
|
-
detail,
|
|
973
|
-
loading,
|
|
974
|
-
error,
|
|
975
|
-
ruleName,
|
|
976
|
-
programStageName,
|
|
977
|
-
variables
|
|
978
|
-
}
|
|
979
|
-
) }),
|
|
980
|
-
/* @__PURE__ */ jsxRuntime.jsx(RuleDetailsFooter, { onClose })
|
|
981
|
-
] });
|
|
1444
|
+
function orDash(value) {
|
|
1445
|
+
return value ?? EM_DASH;
|
|
982
1446
|
}
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
var ROLE_COLUMNS = {
|
|
986
|
-
source: 0,
|
|
987
|
-
rule: 280,
|
|
988
|
-
target: 560
|
|
989
|
-
};
|
|
990
|
-
var FEEDBACK_COLUMN = 720;
|
|
991
|
-
var LANE_HEIGHT = 104;
|
|
992
|
-
var STAGGER = 24;
|
|
993
|
-
function median(values) {
|
|
994
|
-
const sorted = [...values].sort((a, b) => a - b);
|
|
995
|
-
const mid = Math.floor(sorted.length / 2);
|
|
996
|
-
return sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
|
|
1447
|
+
function resolveDetailDisplayName(rule) {
|
|
1448
|
+
return rule?.displayName;
|
|
997
1449
|
}
|
|
998
|
-
function
|
|
999
|
-
|
|
1000
|
-
return 0;
|
|
1001
|
-
}
|
|
1002
|
-
const magnitude = Math.ceil(indexInBucket / 2) * STAGGER;
|
|
1003
|
-
return indexInBucket % 2 === 1 ? magnitude : -magnitude;
|
|
1450
|
+
function resolveDetailName(rule) {
|
|
1451
|
+
return rule?.name;
|
|
1004
1452
|
}
|
|
1005
|
-
function
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
}
|
|
1009
|
-
return ROLE_COLUMNS[node.role];
|
|
1453
|
+
function resolveRuleDisplayName(rule, fallback) {
|
|
1454
|
+
const displayName = resolveDetailDisplayName(rule) ?? resolveDetailName(rule);
|
|
1455
|
+
return displayName ?? fallback;
|
|
1010
1456
|
}
|
|
1011
|
-
function
|
|
1012
|
-
|
|
1013
|
-
const effectTargets = /* @__PURE__ */ new Set();
|
|
1014
|
-
for (const edge of graph.edges) {
|
|
1015
|
-
if (edge.effectType === "read") {
|
|
1016
|
-
readSources.add(edge.source);
|
|
1017
|
-
} else {
|
|
1018
|
-
effectTargets.add(edge.target);
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
const flowNodes = [];
|
|
1022
|
-
const endpointByGraphId = /* @__PURE__ */ new Map();
|
|
1023
|
-
for (const node of graph.nodes) {
|
|
1024
|
-
if (node.kind === "rule") {
|
|
1025
|
-
flowNodes.push({
|
|
1026
|
-
id: node.id,
|
|
1027
|
-
graphNodeId: node.id,
|
|
1028
|
-
kind: node.kind,
|
|
1029
|
-
label: node.label,
|
|
1030
|
-
role: "rule"
|
|
1031
|
-
});
|
|
1032
|
-
continue;
|
|
1033
|
-
}
|
|
1034
|
-
const isSource = readSources.has(node.id);
|
|
1035
|
-
const isTarget = effectTargets.has(node.id);
|
|
1036
|
-
const endpoints = {};
|
|
1037
|
-
if (isSource) {
|
|
1038
|
-
const flowId = isTarget ? `${node.id}@src` : node.id;
|
|
1039
|
-
endpoints.source = flowId;
|
|
1040
|
-
flowNodes.push({
|
|
1041
|
-
id: flowId,
|
|
1042
|
-
graphNodeId: node.id,
|
|
1043
|
-
kind: node.kind,
|
|
1044
|
-
label: node.label,
|
|
1045
|
-
role: "source"
|
|
1046
|
-
});
|
|
1047
|
-
}
|
|
1048
|
-
if (isTarget) {
|
|
1049
|
-
const flowId = isSource ? `${node.id}@tgt` : node.id;
|
|
1050
|
-
endpoints.target = flowId;
|
|
1051
|
-
flowNodes.push({
|
|
1052
|
-
id: flowId,
|
|
1053
|
-
graphNodeId: node.id,
|
|
1054
|
-
kind: node.kind,
|
|
1055
|
-
label: node.label,
|
|
1056
|
-
role: "target"
|
|
1057
|
-
});
|
|
1058
|
-
}
|
|
1059
|
-
endpointByGraphId.set(node.id, endpoints);
|
|
1060
|
-
}
|
|
1061
|
-
const flowEdges = graph.edges.map((edge) => {
|
|
1062
|
-
if (edge.effectType === "read") {
|
|
1063
|
-
const endpoints2 = endpointByGraphId.get(edge.source);
|
|
1064
|
-
return { ...edge, source: endpoints2?.source ?? edge.source };
|
|
1065
|
-
}
|
|
1066
|
-
const endpoints = endpointByGraphId.get(edge.target);
|
|
1067
|
-
return { ...edge, target: endpoints?.target ?? edge.target };
|
|
1068
|
-
});
|
|
1069
|
-
return { nodes: flowNodes, edges: flowEdges };
|
|
1457
|
+
function resolveRuleStageName(rule) {
|
|
1458
|
+
return rule?.programStage?.displayName;
|
|
1070
1459
|
}
|
|
1071
|
-
function
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
flowNodes.filter((node) => node.role === "rule").forEach((node, index) => {
|
|
1075
|
-
ruleLane.set(node.id, index);
|
|
1076
|
-
positions.set(node.id, { x: ROLE_COLUMNS.rule, y: index * LANE_HEIGHT });
|
|
1077
|
-
});
|
|
1078
|
-
const connectedRuleLanes = /* @__PURE__ */ new Map();
|
|
1079
|
-
const addLane = (nodeId, lane) => {
|
|
1080
|
-
const lanes = connectedRuleLanes.get(nodeId) ?? /* @__PURE__ */ new Set();
|
|
1081
|
-
lanes.add(lane);
|
|
1082
|
-
connectedRuleLanes.set(nodeId, lanes);
|
|
1083
|
-
};
|
|
1084
|
-
for (const edge of flowEdges) {
|
|
1085
|
-
const sourceLane = ruleLane.get(edge.source);
|
|
1086
|
-
const targetLane = ruleLane.get(edge.target);
|
|
1087
|
-
if (targetLane !== void 0 && sourceLane === void 0) {
|
|
1088
|
-
addLane(edge.source, targetLane);
|
|
1089
|
-
}
|
|
1090
|
-
if (sourceLane !== void 0 && targetLane === void 0) {
|
|
1091
|
-
addLane(edge.target, sourceLane);
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
const bucketCounts = /* @__PURE__ */ new Map();
|
|
1095
|
-
for (const node of flowNodes) {
|
|
1096
|
-
if (node.role === "rule") {
|
|
1097
|
-
continue;
|
|
1098
|
-
}
|
|
1099
|
-
const lanes = connectedRuleLanes.get(node.id);
|
|
1100
|
-
const lane = lanes && lanes.size > 0 ? median([...lanes]) : 0;
|
|
1101
|
-
const column = columnFor(node);
|
|
1102
|
-
const bucketKey = `${String(column)}:${String(lane)}`;
|
|
1103
|
-
const indexInBucket = bucketCounts.get(bucketKey) ?? 0;
|
|
1104
|
-
bucketCounts.set(bucketKey, indexInBucket + 1);
|
|
1105
|
-
positions.set(node.id, {
|
|
1106
|
-
x: column,
|
|
1107
|
-
y: lane * LANE_HEIGHT + staggerOffset(indexInBucket)
|
|
1108
|
-
});
|
|
1460
|
+
function resolveStageName(rule, programStageName) {
|
|
1461
|
+
if (programStageName === null) {
|
|
1462
|
+
return translate("All stages (registration)");
|
|
1109
1463
|
}
|
|
1110
|
-
return
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1113
|
-
// src/graphNodeStyles.ts
|
|
1114
|
-
var GRAPH_NODE_CLASSES = {
|
|
1115
|
-
field: "border-dhis2-blue-500",
|
|
1116
|
-
rule: "border-dhis2-teal-600 bg-dhis2-teal-050",
|
|
1117
|
-
section: "border-dhis2-grey-600",
|
|
1118
|
-
feedback: "border-dhis2-purple-500"
|
|
1119
|
-
};
|
|
1120
|
-
var LEGEND_SWATCH_CLASSES = {
|
|
1121
|
-
field: "bg-dhis2-blue-500",
|
|
1122
|
-
rule: "bg-dhis2-teal-600",
|
|
1123
|
-
section: "bg-dhis2-grey-600",
|
|
1124
|
-
feedback: "bg-dhis2-purple-500"
|
|
1125
|
-
};
|
|
1126
|
-
function getGraphNodeClassName(kind, highlighted) {
|
|
1127
|
-
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"}`;
|
|
1464
|
+
return orDash(resolveRuleStageName(rule) ?? programStageName);
|
|
1128
1465
|
}
|
|
1129
|
-
function
|
|
1130
|
-
return
|
|
1466
|
+
function resolveLastUpdatedByName(rule) {
|
|
1467
|
+
return rule?.lastUpdatedBy?.displayName;
|
|
1131
1468
|
}
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
return null;
|
|
1137
|
-
}
|
|
1138
|
-
if (selectedEntryId) {
|
|
1139
|
-
return entries.find((entry) => entry.id === selectedEntryId) ?? entries[entries.length - 1];
|
|
1469
|
+
function resolveLastUpdatedValue(rule) {
|
|
1470
|
+
const label = formatTimestamp(rule?.lastUpdated);
|
|
1471
|
+
if (!label) {
|
|
1472
|
+
return EM_DASH;
|
|
1140
1473
|
}
|
|
1141
|
-
|
|
1474
|
+
const by = resolveLastUpdatedByName(rule);
|
|
1475
|
+
return by ? `${label} \xB7 ${by}` : label;
|
|
1142
1476
|
}
|
|
1143
|
-
function
|
|
1144
|
-
|
|
1145
|
-
if (!latest) {
|
|
1146
|
-
return /* @__PURE__ */ new Set();
|
|
1147
|
-
}
|
|
1148
|
-
return new Set(latest.ruleResults.map((result) => result.ruleId));
|
|
1477
|
+
function resolvePriorityCellValue(rule) {
|
|
1478
|
+
return rule?.priority != null ? String(rule.priority) : EM_DASH;
|
|
1149
1479
|
}
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
rule: "Rule",
|
|
1153
|
-
section: "Section",
|
|
1154
|
-
feedback: "Feedback"
|
|
1155
|
-
};
|
|
1156
|
-
var READ_LABEL_EDGE_THRESHOLD = 4;
|
|
1157
|
-
var DEFAULT_EDGE_OPTIONS = {
|
|
1158
|
-
type: "ruleGraphEdge",
|
|
1159
|
-
style: { stroke: getEffectEdgeStroke("default") },
|
|
1160
|
-
markerEnd: {
|
|
1161
|
-
type: react$1.MarkerType.ArrowClosed,
|
|
1162
|
-
color: getEffectEdgeStroke("default")
|
|
1163
|
-
}
|
|
1164
|
-
};
|
|
1165
|
-
function RuleGraphNode({ data }) {
|
|
1166
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: getGraphNodeClassName(data.kind, data.highlighted), children: [
|
|
1167
|
-
/* @__PURE__ */ jsxRuntime.jsx(react$1.Handle, { type: "target", position: react$1.Position.Left }),
|
|
1168
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mb-dp4 block text-[0.625rem] font-semibold uppercase leading-none tracking-wide text-dhis2-grey-600", children: KIND_LABELS[data.kind] }),
|
|
1169
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "break-words font-semibold leading-[1.3]", children: data.label }),
|
|
1170
|
-
data.value !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-dp4 border-t border-dhis2-grey-200 pt-dp4 font-mono text-[0.6875rem] break-all text-dhis2-grey-700", children: data.value }) : null,
|
|
1171
|
-
/* @__PURE__ */ jsxRuntime.jsx(react$1.Handle, { type: "source", position: react$1.Position.Right })
|
|
1172
|
-
] });
|
|
1480
|
+
function resolveActionsCellValue(rule) {
|
|
1481
|
+
return translate("{{n}} action(s)", { n: rule?.programRuleActions?.length ?? 0 });
|
|
1173
1482
|
}
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
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
|
|
1188
1499
|
}) {
|
|
1189
|
-
const
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
stroke: "transparent",
|
|
1208
|
-
strokeWidth: 16,
|
|
1209
|
-
onMouseEnter: () => {
|
|
1210
|
-
setHovered(true);
|
|
1211
|
-
},
|
|
1212
|
-
onMouseLeave: () => {
|
|
1213
|
-
setHovered(false);
|
|
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",
|
|
1515
|
+
{
|
|
1516
|
+
className: `rounded-[4px] px-dp8 py-[2px] text-xs font-semibold ${chip.className}`,
|
|
1517
|
+
children: chip.label
|
|
1214
1518
|
}
|
|
1215
|
-
|
|
1216
|
-
),
|
|
1217
|
-
|
|
1218
|
-
"
|
|
1519
|
+
)
|
|
1520
|
+
] }),
|
|
1521
|
+
description ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1522
|
+
"p",
|
|
1219
1523
|
{
|
|
1220
|
-
className: "
|
|
1221
|
-
style: {
|
|
1222
|
-
|
|
1223
|
-
background: "rgba(255,255,255,0.92)",
|
|
1224
|
-
color: data.stroke,
|
|
1225
|
-
pointerEvents: "all"
|
|
1226
|
-
},
|
|
1227
|
-
onMouseEnter: () => {
|
|
1228
|
-
setHovered(true);
|
|
1229
|
-
},
|
|
1230
|
-
onMouseLeave: () => {
|
|
1231
|
-
setHovered(false);
|
|
1232
|
-
},
|
|
1233
|
-
children: data.label
|
|
1524
|
+
className: "m-0 mt-[6px] text-sm text-dhis2-grey-700",
|
|
1525
|
+
style: { textWrap: "pretty" },
|
|
1526
|
+
children: description
|
|
1234
1527
|
}
|
|
1235
|
-
)
|
|
1528
|
+
) : null
|
|
1236
1529
|
] });
|
|
1237
1530
|
}
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
}
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
for (const result of entry.ruleResults) {
|
|
1261
|
-
if (result.ruleId !== ruleId) {
|
|
1262
|
-
continue;
|
|
1263
|
-
}
|
|
1264
|
-
for (const effect of result.effects) {
|
|
1265
|
-
keys.add(effectNodeKey(effect));
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1268
|
-
return keys;
|
|
1531
|
+
function ConditionSection({
|
|
1532
|
+
condition,
|
|
1533
|
+
variables
|
|
1534
|
+
}) {
|
|
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
|
+
] });
|
|
1269
1553
|
}
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
return String(value);
|
|
1276
|
-
}
|
|
1277
|
-
return JSON.stringify(value);
|
|
1278
|
-
};
|
|
1279
|
-
function toFlowGraph(graph, fieldState, formValues, highlightedKeys) {
|
|
1280
|
-
const { nodes: flowNodes, edges: flowEdges } = prepareFlowGraph(graph);
|
|
1281
|
-
const positions = computeGraphLayout(flowNodes, flowEdges);
|
|
1282
|
-
const graphNodeIdByFlowId = new Map(flowNodes.map((node) => [node.id, node.graphNodeId]));
|
|
1283
|
-
const nodes = flowNodes.map((node) => {
|
|
1284
|
-
const position = positions.get(node.id) ?? { x: 0, y: 0 };
|
|
1285
|
-
const rawFieldId = node.kind === "field" ? node.graphNodeId.slice("field:".length) : void 0;
|
|
1286
|
-
const assignedValue = rawFieldId && rawFieldId in fieldState ? fieldState[rawFieldId].assignedValue : void 0;
|
|
1287
|
-
const formValue = rawFieldId ? formValues[rawFieldId] : void 0;
|
|
1288
|
-
const displayValue = formatDisplayValue(assignedValue) ?? formatDisplayValue(formValue);
|
|
1289
|
-
return {
|
|
1290
|
-
id: node.id,
|
|
1291
|
-
type: "ruleGraphNode",
|
|
1292
|
-
position,
|
|
1293
|
-
data: {
|
|
1294
|
-
label: node.label,
|
|
1295
|
-
kind: node.kind,
|
|
1296
|
-
graphNodeId: node.graphNodeId,
|
|
1297
|
-
value: displayValue,
|
|
1298
|
-
highlighted: highlightedKeys ? highlightedKeys.has(node.graphNodeId) : true
|
|
1299
|
-
}
|
|
1300
|
-
};
|
|
1301
|
-
});
|
|
1302
|
-
const dense = flowEdges.length > READ_LABEL_EDGE_THRESHOLD;
|
|
1303
|
-
const sourceFanOut = /* @__PURE__ */ new Map();
|
|
1304
|
-
const edges = flowEdges.map((edge) => {
|
|
1305
|
-
const sourceGraphId = graphNodeIdByFlowId.get(edge.source) ?? edge.source;
|
|
1306
|
-
const targetGraphId = graphNodeIdByFlowId.get(edge.target) ?? edge.target;
|
|
1307
|
-
const isHighlighted = highlightedKeys ? highlightedKeys.has(sourceGraphId) && highlightedKeys.has(targetGraphId) : true;
|
|
1308
|
-
const effectType = edge.effectType ?? "default";
|
|
1309
|
-
const isRead = effectType === "read";
|
|
1310
|
-
const stroke = getEffectEdgeStroke(effectType, isHighlighted);
|
|
1311
|
-
const label = getEffectShortLabel(effectType);
|
|
1312
|
-
const showLabel = !(isRead && dense);
|
|
1313
|
-
const fanIndex = sourceFanOut.get(edge.source) ?? 0;
|
|
1314
|
-
sourceFanOut.set(edge.source, fanIndex + 1);
|
|
1315
|
-
const offset = 20 + fanIndex * 14;
|
|
1316
|
-
return {
|
|
1317
|
-
id: edge.id,
|
|
1318
|
-
source: edge.source,
|
|
1319
|
-
target: edge.target,
|
|
1320
|
-
type: "ruleGraphEdge",
|
|
1321
|
-
animated: isHighlighted && !isRead,
|
|
1322
|
-
zIndex: isHighlighted ? 1 : 0,
|
|
1323
|
-
style: {
|
|
1324
|
-
stroke,
|
|
1325
|
-
strokeWidth: isRead ? 1 : Math.min(1 + edge.fireCount, 4),
|
|
1326
|
-
opacity: isHighlighted ? isRead ? 0.5 : 1 : 0.35
|
|
1327
|
-
},
|
|
1328
|
-
markerEnd: {
|
|
1329
|
-
type: react$1.MarkerType.ArrowClosed,
|
|
1330
|
-
color: stroke
|
|
1331
|
-
},
|
|
1332
|
-
data: {
|
|
1333
|
-
label,
|
|
1334
|
-
stroke,
|
|
1335
|
-
showLabel,
|
|
1336
|
-
offset
|
|
1337
|
-
}
|
|
1338
|
-
};
|
|
1339
|
-
});
|
|
1340
|
-
return { nodes, edges };
|
|
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
|
+
] });
|
|
1341
1559
|
}
|
|
1342
|
-
function
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1560
|
+
function RuleDetailsContent({
|
|
1561
|
+
detail,
|
|
1562
|
+
ruleName,
|
|
1563
|
+
programStageName,
|
|
1564
|
+
variables
|
|
1347
1565
|
}) {
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
{ kind: "section", label: translate("Section") },
|
|
1352
|
-
{ kind: "feedback", label: translate("Feedback") }
|
|
1353
|
-
];
|
|
1354
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shrink-0 border-b border-dhis2-grey-200 bg-white", children: [
|
|
1355
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-dp8 px-dp12 py-dp8", children: [
|
|
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") }),
|
|
1356
1569
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1357
|
-
|
|
1570
|
+
BasicDetails,
|
|
1358
1571
|
{
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
"span",
|
|
1363
|
-
{
|
|
1364
|
-
className: "inline-flex items-center gap-dp8 text-xs text-dhis2-grey-800",
|
|
1365
|
-
children: [
|
|
1366
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: getLegendSwatchClassName(item.kind) }),
|
|
1367
|
-
item.label
|
|
1368
|
-
]
|
|
1369
|
-
},
|
|
1370
|
-
item.kind
|
|
1371
|
-
))
|
|
1572
|
+
rule: detail,
|
|
1573
|
+
ruleName,
|
|
1574
|
+
programStageName
|
|
1372
1575
|
}
|
|
1373
|
-
)
|
|
1374
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-dp8", children: [
|
|
1375
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs tabular-nums text-dhis2-grey-600", children: translate("{{nodes}} nodes \xB7 {{edges}} edges", {
|
|
1376
|
-
nodes: nodeCount,
|
|
1377
|
-
edges: edgeCount
|
|
1378
|
-
}) }),
|
|
1379
|
-
headerActions
|
|
1380
|
-
] })
|
|
1576
|
+
)
|
|
1381
1577
|
] }),
|
|
1382
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1578
|
+
/* @__PURE__ */ jsxRuntime.jsx(ConditionSection, { condition: detail.condition, variables }),
|
|
1579
|
+
/* @__PURE__ */ jsxRuntime.jsx(ActionsSection, { actions: detail.programRuleActions ?? [] })
|
|
1383
1580
|
] });
|
|
1384
1581
|
}
|
|
1385
|
-
function
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
const frameId = requestAnimationFrame(() => {
|
|
1393
|
-
void fitView({ padding: 0.2, duration: 150 });
|
|
1394
|
-
});
|
|
1395
|
-
return () => {
|
|
1396
|
-
cancelAnimationFrame(frameId);
|
|
1397
|
-
};
|
|
1398
|
-
}, [edgeCount, fitView, layoutKey, nodeCount]);
|
|
1399
|
-
return null;
|
|
1400
|
-
}
|
|
1401
|
-
function mergeNodePositions(next, current) {
|
|
1402
|
-
const currentById = new Map(current.map((node) => [node.id, node]));
|
|
1403
|
-
return next.map((node) => {
|
|
1404
|
-
const existing = currentById.get(node.id);
|
|
1405
|
-
if (existing) {
|
|
1406
|
-
return { ...node, position: existing.position };
|
|
1407
|
-
}
|
|
1408
|
-
return node;
|
|
1409
|
-
});
|
|
1410
|
-
}
|
|
1411
|
-
function RuleGraphCanvas({
|
|
1412
|
-
nodes: layoutNodes,
|
|
1413
|
-
edges: layoutEdges,
|
|
1414
|
-
layoutKey
|
|
1582
|
+
function RuleDetailsBody({
|
|
1583
|
+
detail,
|
|
1584
|
+
loading,
|
|
1585
|
+
error,
|
|
1586
|
+
ruleName,
|
|
1587
|
+
programStageName,
|
|
1588
|
+
variables
|
|
1415
1589
|
}) {
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
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,
|
|
1426
1601
|
{
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
onNodesChange,
|
|
1432
|
-
onEdgesChange,
|
|
1433
|
-
nodesDraggable: true,
|
|
1434
|
-
defaultEdgeOptions: DEFAULT_EDGE_OPTIONS,
|
|
1435
|
-
elevateEdgesOnSelect: true,
|
|
1436
|
-
fitView: true,
|
|
1437
|
-
proOptions: { hideAttribution: true },
|
|
1438
|
-
className: "h-full w-full",
|
|
1439
|
-
children: [
|
|
1440
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1441
|
-
FitViewOnChange,
|
|
1442
|
-
{
|
|
1443
|
-
layoutKey,
|
|
1444
|
-
nodeCount: nodes.length,
|
|
1445
|
-
edgeCount: edges.length
|
|
1446
|
-
}
|
|
1447
|
-
),
|
|
1448
|
-
/* @__PURE__ */ jsxRuntime.jsx(react$1.Background, { gap: 16, size: 1 }),
|
|
1449
|
-
/* @__PURE__ */ jsxRuntime.jsx(react$1.Controls, { showInteractive: false })
|
|
1450
|
-
]
|
|
1602
|
+
detail,
|
|
1603
|
+
ruleName,
|
|
1604
|
+
programStageName,
|
|
1605
|
+
variables
|
|
1451
1606
|
}
|
|
1452
1607
|
);
|
|
1453
1608
|
}
|
|
1454
|
-
function
|
|
1455
|
-
entries,
|
|
1456
|
-
fieldState,
|
|
1457
|
-
formValues,
|
|
1458
|
-
selectedEntryId,
|
|
1459
|
-
highlightRuleId,
|
|
1460
|
-
labelLookup,
|
|
1461
|
-
className,
|
|
1462
|
-
layoutKey,
|
|
1463
|
-
headerActions,
|
|
1464
|
-
minHeightClassName = "min-h-[360px]"
|
|
1465
|
-
}) {
|
|
1466
|
-
const graphEntry = react.useMemo(
|
|
1467
|
-
() => resolveGraphTraceEntry(entries, selectedEntryId),
|
|
1468
|
-
[entries, selectedEntryId]
|
|
1469
|
-
);
|
|
1470
|
-
const graph = react.useMemo(
|
|
1471
|
-
() => graphEntry ? buildGraphFromTrace([graphEntry], labelLookup) : { nodes: [], edges: [] },
|
|
1472
|
-
[graphEntry, labelLookup]
|
|
1473
|
-
);
|
|
1474
|
-
const highlightedKeys = react.useMemo(() => {
|
|
1475
|
-
if (!graphEntry || !selectedEntryId && !highlightRuleId) {
|
|
1476
|
-
return null;
|
|
1477
|
-
}
|
|
1478
|
-
const keys = /* @__PURE__ */ new Set();
|
|
1479
|
-
if (selectedEntryId) {
|
|
1480
|
-
for (const key of entryGraphKeys(graphEntry)) {
|
|
1481
|
-
keys.add(key);
|
|
1482
|
-
}
|
|
1483
|
-
}
|
|
1484
|
-
if (highlightRuleId) {
|
|
1485
|
-
for (const key of ruleGraphKeys(graphEntry, highlightRuleId)) {
|
|
1486
|
-
keys.add(key);
|
|
1487
|
-
}
|
|
1488
|
-
}
|
|
1489
|
-
return keys;
|
|
1490
|
-
}, [graphEntry, highlightRuleId, selectedEntryId]);
|
|
1491
|
-
const { nodes, edges } = react.useMemo(
|
|
1492
|
-
() => toFlowGraph(graph, fieldState, formValues, highlightedKeys),
|
|
1493
|
-
[graph, fieldState, formValues, highlightedKeys]
|
|
1494
|
-
);
|
|
1495
|
-
const activeEffectVariants = react.useMemo(() => {
|
|
1496
|
-
const variants = /* @__PURE__ */ new Set();
|
|
1497
|
-
for (const edge of graph.edges) {
|
|
1498
|
-
if (edge.effectType) {
|
|
1499
|
-
variants.add(getEffectVariant(edge.effectType));
|
|
1500
|
-
}
|
|
1501
|
-
}
|
|
1502
|
-
return variants;
|
|
1503
|
-
}, [graph.edges]);
|
|
1504
|
-
if (!graph.nodes.length) {
|
|
1505
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-dp8", children: /* @__PURE__ */ jsxRuntime.jsx(ui.NoticeBox, { title: translate("No rule relationships yet"), children: translate(
|
|
1506
|
-
"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)."
|
|
1507
|
-
) }) });
|
|
1508
|
-
}
|
|
1609
|
+
function RuleDetailsFooter({ onClose }) {
|
|
1509
1610
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1510
1611
|
"div",
|
|
1511
1612
|
{
|
|
1512
|
-
|
|
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]",
|
|
1513
1615
|
children: [
|
|
1514
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1515
|
-
|
|
1516
|
-
{
|
|
1517
|
-
nodeCount: graph.nodes.length,
|
|
1518
|
-
edgeCount: graph.edges.length,
|
|
1519
|
-
headerActions,
|
|
1520
|
-
activeEffectVariants
|
|
1521
|
-
}
|
|
1522
|
-
),
|
|
1523
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1524
|
-
"div",
|
|
1525
|
-
{
|
|
1526
|
-
className: `${minHeightClassName} flex-1 bg-white`,
|
|
1527
|
-
style: { width: "100%", height: "100%" },
|
|
1528
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(react$1.ReactFlowProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(RuleGraphCanvas, { nodes, edges, layoutKey }) })
|
|
1529
|
-
}
|
|
1530
|
-
)
|
|
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") }) })
|
|
1531
1618
|
]
|
|
1532
1619
|
}
|
|
1533
1620
|
);
|
|
1534
1621
|
}
|
|
1535
|
-
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({
|
|
1536
1629
|
open,
|
|
1537
1630
|
onClose,
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1631
|
+
ruleId,
|
|
1632
|
+
ruleName,
|
|
1633
|
+
status,
|
|
1634
|
+
programStageName,
|
|
1635
|
+
programRuleVariables
|
|
1541
1636
|
}) {
|
|
1542
|
-
|
|
1637
|
+
const { detail, loading, error } = useProgramRuleDetail(resolveActiveRuleId(open, ruleId));
|
|
1638
|
+
if (!shouldRenderModal(open, ruleId)) {
|
|
1543
1639
|
return null;
|
|
1544
1640
|
}
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.ModalContent, {
|
|
1551
|
-
|
|
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,
|
|
1552
1648
|
{
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1649
|
+
detail,
|
|
1650
|
+
loading,
|
|
1651
|
+
error,
|
|
1652
|
+
ruleName,
|
|
1653
|
+
programStageName,
|
|
1654
|
+
variables
|
|
1557
1655
|
}
|
|
1558
|
-
) })
|
|
1656
|
+
) }),
|
|
1657
|
+
/* @__PURE__ */ jsxRuntime.jsx(RuleDetailsFooter, { onClose })
|
|
1559
1658
|
] });
|
|
1560
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();
|
|
1561
1678
|
function resolveLabel(id, resolver) {
|
|
1562
1679
|
const label = resolver?.(id) ?? id;
|
|
1563
1680
|
return { label, showId: label !== id };
|
|
@@ -1774,6 +1891,9 @@ function TraceTimeline({
|
|
|
1774
1891
|
) }, entry.id);
|
|
1775
1892
|
}) });
|
|
1776
1893
|
}
|
|
1894
|
+
|
|
1895
|
+
// src/RulesPanel.tsx
|
|
1896
|
+
init_traceEntry();
|
|
1777
1897
|
var REORDER_DURATION_MS = 220;
|
|
1778
1898
|
var REORDER_EASING = "ease-out";
|
|
1779
1899
|
function prefersReducedMotion() {
|
|
@@ -1820,11 +1940,20 @@ function useFlipReorder(orderedIds, containerRef) {
|
|
|
1820
1940
|
positionsRef.current = nextPositions;
|
|
1821
1941
|
}, [orderedIds.join("|")]);
|
|
1822
1942
|
}
|
|
1823
|
-
|
|
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
|
+
}
|
|
1824
1953
|
return metadata.formKind === "event" ? metadata.programStageId : null;
|
|
1825
1954
|
}
|
|
1826
1955
|
function isRuleInScope(rule, scopeStageId) {
|
|
1827
|
-
return rule.programStageId ===
|
|
1956
|
+
return rule.programStageId === scopeStageId;
|
|
1828
1957
|
}
|
|
1829
1958
|
function resolveAccentClassName(inScope, firing, selected) {
|
|
1830
1959
|
if (selected) {
|
|
@@ -1847,6 +1976,11 @@ function resolveDetailsStatus(inScope, firing) {
|
|
|
1847
1976
|
}
|
|
1848
1977
|
return firing ? "firing" : "idle";
|
|
1849
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
|
+
}
|
|
1850
1984
|
function formatActionLabel(action) {
|
|
1851
1985
|
if (action.targetLabel && action.detail) {
|
|
1852
1986
|
return `${action.type} \xB7 ${action.targetLabel} = ${action.detail}`;
|
|
@@ -1880,7 +2014,11 @@ function resolveTabLabel(key) {
|
|
|
1880
2014
|
return translate("Graph");
|
|
1881
2015
|
}
|
|
1882
2016
|
}
|
|
1883
|
-
function RulesPanel({
|
|
2017
|
+
function RulesPanel({
|
|
2018
|
+
metadata,
|
|
2019
|
+
showConditions = true,
|
|
2020
|
+
activeProgramStageId
|
|
2021
|
+
}) {
|
|
1884
2022
|
const formStore = dhis2FormUtilsHooks.useFormStore();
|
|
1885
2023
|
const { form } = dhis2FormUtilsHooks.useFormStateContext();
|
|
1886
2024
|
const traceStore = useRuleTraceStore();
|
|
@@ -1892,7 +2030,10 @@ function RulesPanel({ metadata, showConditions = true }) {
|
|
|
1892
2030
|
const [detailsRuleId, setDetailsRuleId] = react.useState(null);
|
|
1893
2031
|
const labelLookup = react.useMemo(() => createLabelLookup(metadata), [metadata]);
|
|
1894
2032
|
const catalog = react.useMemo(() => resolveProgramRulesList(metadata), [metadata]);
|
|
1895
|
-
const scopeStageId = react.useMemo(
|
|
2033
|
+
const scopeStageId = react.useMemo(
|
|
2034
|
+
() => resolveScopeStageId(metadata, activeProgramStageId),
|
|
2035
|
+
[metadata, activeProgramStageId]
|
|
2036
|
+
);
|
|
1896
2037
|
const entries = react.useSyncExternalStore(
|
|
1897
2038
|
react.useCallback((listener) => traceStore.subscribe(listener), [traceStore]),
|
|
1898
2039
|
react.useCallback(() => traceStore.getSnapshot(), [traceStore]),
|
|
@@ -2076,19 +2217,19 @@ function RulesPanel({ metadata, showConditions = true }) {
|
|
|
2076
2217
|
},
|
|
2077
2218
|
labelLookup
|
|
2078
2219
|
}
|
|
2079
|
-
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
2080
|
-
|
|
2220
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(react.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(RulesPanelGraphFallback, {}), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2221
|
+
LazyRuleGraphView,
|
|
2081
2222
|
{
|
|
2082
2223
|
...graphProps,
|
|
2083
2224
|
headerActions: graphHasNodes ? expandGraphButton : void 0
|
|
2084
2225
|
}
|
|
2085
|
-
) }),
|
|
2226
|
+
) }) }),
|
|
2086
2227
|
entries.length > 0 && tab !== "rules" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "sr-only", "aria-live": "polite", children: translate("{{evaluations}} evaluations \xB7 {{rules}} rules observed", {
|
|
2087
2228
|
evaluations: entries.length,
|
|
2088
2229
|
rules: observedRuleCount
|
|
2089
2230
|
}) }) : null,
|
|
2090
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2091
|
-
|
|
2231
|
+
graphModalOpen ? /* @__PURE__ */ jsxRuntime.jsx(react.Suspense, { fallback: null, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2232
|
+
LazyRuleGraphModal,
|
|
2092
2233
|
{
|
|
2093
2234
|
...graphProps,
|
|
2094
2235
|
open: graphModalOpen,
|
|
@@ -2096,9 +2237,9 @@ function RulesPanel({ metadata, showConditions = true }) {
|
|
|
2096
2237
|
setGraphModalOpen(false);
|
|
2097
2238
|
},
|
|
2098
2239
|
subtitle: graphSubtitle,
|
|
2099
|
-
layoutKey:
|
|
2240
|
+
layoutKey: "open"
|
|
2100
2241
|
}
|
|
2101
|
-
),
|
|
2242
|
+
) }) : null,
|
|
2102
2243
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2103
2244
|
RuleDetailsModal,
|
|
2104
2245
|
{
|
|
@@ -2234,16 +2375,16 @@ function RulesTab({
|
|
|
2234
2375
|
children: rule.condition
|
|
2235
2376
|
}
|
|
2236
2377
|
) : null,
|
|
2237
|
-
!inScope
|
|
2238
|
-
stage: labelLookup.resolveStageName(rule.programStageId)
|
|
2239
|
-
}) }) : null
|
|
2378
|
+
!inScope ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] text-dhis2-grey-600", children: resolveAppliesToCaption(rule, labelLookup) }) : null
|
|
2240
2379
|
]
|
|
2241
2380
|
}
|
|
2242
2381
|
) }, rule.id);
|
|
2243
2382
|
}) });
|
|
2244
2383
|
}
|
|
2245
2384
|
|
|
2246
|
-
|
|
2385
|
+
// src/index.ts
|
|
2386
|
+
init_effectStyles();
|
|
2387
|
+
|
|
2247
2388
|
exports.RuleDetailsModal = RuleDetailsModal;
|
|
2248
2389
|
exports.RuleDevtoolsScope = RuleDevtoolsScope;
|
|
2249
2390
|
exports.RulesPanel = RulesPanel;
|