@rytass/bpm-core-react 0.21.4 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/dist/chunks/{FormBuilderView-DbfbLZCI.js → FormBuilderView-D_bN9gzT.js} +675 -614
  3. package/dist/chunks/FormBuilderView-D_bN9gzT.js.map +1 -0
  4. package/dist/chunks/FormBuilderView-bm8P55hp.cjs +4 -0
  5. package/dist/chunks/FormBuilderView-bm8P55hp.cjs.map +1 -0
  6. package/dist/chunks/FormRendererView-BlQgJOkI.cjs +6 -0
  7. package/dist/chunks/FormRendererView-BlQgJOkI.cjs.map +1 -0
  8. package/dist/chunks/{FormRendererView-CAyPXhPq.js → FormRendererView-CemHvfAT.js} +298 -290
  9. package/dist/chunks/FormRendererView-CemHvfAT.js.map +1 -0
  10. package/dist/chunks/compose-CdKVQfjB.cjs +2 -0
  11. package/dist/chunks/compose-CdKVQfjB.cjs.map +1 -0
  12. package/dist/chunks/{compose-U1zs3Yof.js → compose-N9657E5u.js} +161 -147
  13. package/dist/chunks/compose-N9657E5u.js.map +1 -0
  14. package/dist/chunks/{designer-DPG7R_WP.js → designer-CKwG_vM5.js} +1913 -1715
  15. package/dist/chunks/designer-CKwG_vM5.js.map +1 -0
  16. package/dist/chunks/designer-JPs7EijM.cjs +69 -0
  17. package/dist/chunks/designer-JPs7EijM.cjs.map +1 -0
  18. package/dist/chunks/detail-Dqu0K9QM.cjs +5 -0
  19. package/dist/chunks/detail-Dqu0K9QM.cjs.map +1 -0
  20. package/dist/chunks/{detail-CHZDNNM6.js → detail-fW0vJudE.js} +902 -895
  21. package/dist/chunks/detail-fW0vJudE.js.map +1 -0
  22. package/dist/chunks/instance-error-message-BKt07-Bo.js +270 -0
  23. package/dist/chunks/instance-error-message-BKt07-Bo.js.map +1 -0
  24. package/dist/chunks/instance-error-message-BL_X7Af8.cjs +2 -0
  25. package/dist/chunks/instance-error-message-BL_X7Af8.cjs.map +1 -0
  26. package/dist/pages/instances/detail/index.cjs +1 -1
  27. package/dist/pages/instances/detail/index.js +1 -1
  28. package/dist/pages/templates/compose/index.cjs +1 -1
  29. package/dist/pages/templates/compose/index.js +1 -1
  30. package/dist/pages/templates/designer/index.cjs +1 -1
  31. package/dist/pages/templates/designer/index.js +1 -1
  32. package/dist/views/forms/builder/index.cjs +1 -1
  33. package/dist/views/forms/builder/index.js +1 -1
  34. package/dist/views/forms/renderer/FormRendererView.d.cts +26 -1
  35. package/dist/views/forms/renderer/FormRendererView.d.ts +26 -1
  36. package/dist/views/forms/renderer/index.cjs +1 -1
  37. package/dist/views/forms/renderer/index.js +1 -1
  38. package/dist/views/inbox/index.cjs +1 -1
  39. package/dist/views/inbox/index.cjs.map +1 -1
  40. package/dist/views/inbox/index.js +185 -94
  41. package/dist/views/inbox/index.js.map +1 -1
  42. package/dist/views/instances/approver-filled-form.d.cts +56 -20
  43. package/dist/views/instances/approver-filled-form.d.ts +56 -20
  44. package/dist/views/instances/detail/index.cjs +1 -1
  45. package/dist/views/instances/detail/index.js +1 -1
  46. package/dist/views/instances/detail/sections/InstanceFormSection.d.cts +7 -0
  47. package/dist/views/instances/detail/sections/InstanceFormSection.d.ts +7 -0
  48. package/dist/views/instances/detail/sections/InstanceTasksSection.d.cts +1 -1
  49. package/dist/views/instances/detail/sections/InstanceTasksSection.d.ts +1 -1
  50. package/dist/views/instances/new/index.cjs +1 -1
  51. package/dist/views/instances/new/index.js +2 -2
  52. package/dist/views/templates/compose/index.cjs +1 -1
  53. package/dist/views/templates/compose/index.js +1 -1
  54. package/dist/views/templates/compose/steps/ComposeReviewStep.d.cts +3 -1
  55. package/dist/views/templates/compose/steps/ComposeReviewStep.d.ts +3 -1
  56. package/dist/views/templates/compose/steps/ComposeWorkflowStep.d.cts +4 -1
  57. package/dist/views/templates/compose/steps/ComposeWorkflowStep.d.ts +4 -1
  58. package/dist/views/templates/compose/use-template-compose-wizard.d.cts +3 -1
  59. package/dist/views/templates/compose/use-template-compose-wizard.d.ts +3 -1
  60. package/dist/views/templates/designer/TemplateDesignerView.d.cts +10 -1
  61. package/dist/views/templates/designer/TemplateDesignerView.d.ts +10 -1
  62. package/dist/views/templates/designer/field-access-matrix/FieldAccessMatrixDrawer.d.cts +3 -1
  63. package/dist/views/templates/designer/field-access-matrix/FieldAccessMatrixDrawer.d.ts +3 -1
  64. package/dist/views/templates/designer/field-access-matrix/field-access-copy.d.cts +3 -2
  65. package/dist/views/templates/designer/field-access-matrix/field-access-copy.d.ts +3 -2
  66. package/dist/views/templates/designer/index.cjs +1 -1
  67. package/dist/views/templates/designer/index.js +1 -1
  68. package/dist/views/templates/designer/launch-mode.d.cts +19 -0
  69. package/dist/views/templates/designer/launch-mode.d.ts +19 -0
  70. package/dist/views/templates/designer/publish-default-value-check.d.cts +36 -0
  71. package/dist/views/templates/designer/publish-default-value-check.d.ts +36 -0
  72. package/package.json +3 -3
  73. package/dist/chunks/FormBuilderView-Bok4Y59D.cjs +0 -4
  74. package/dist/chunks/FormBuilderView-Bok4Y59D.cjs.map +0 -1
  75. package/dist/chunks/FormBuilderView-DbfbLZCI.js.map +0 -1
  76. package/dist/chunks/FormRendererView-CAyPXhPq.js.map +0 -1
  77. package/dist/chunks/FormRendererView-iLlHFIUR.cjs +0 -6
  78. package/dist/chunks/FormRendererView-iLlHFIUR.cjs.map +0 -1
  79. package/dist/chunks/compose-DP1N4twA.cjs +0 -2
  80. package/dist/chunks/compose-DP1N4twA.cjs.map +0 -1
  81. package/dist/chunks/compose-U1zs3Yof.js.map +0 -1
  82. package/dist/chunks/designer-CWL-dCQ_.cjs +0 -69
  83. package/dist/chunks/designer-CWL-dCQ_.cjs.map +0 -1
  84. package/dist/chunks/designer-DPG7R_WP.js.map +0 -1
  85. package/dist/chunks/detail-BUfsSal0.cjs +0 -5
  86. package/dist/chunks/detail-BUfsSal0.cjs.map +0 -1
  87. package/dist/chunks/detail-CHZDNNM6.js.map +0 -1
  88. package/dist/chunks/instance-error-message-BEM4O_Kj.js +0 -208
  89. package/dist/chunks/instance-error-message-BEM4O_Kj.js.map +0 -1
  90. package/dist/chunks/instance-error-message-BpCLCOjp.cjs +0 -2
  91. package/dist/chunks/instance-error-message-BpCLCOjp.cjs.map +0 -1
@@ -0,0 +1,270 @@
1
+ import { isFormDataValueEmpty as e, omitFormDataKeys as t, readNodeFillGrantFieldKeys as n, readNodeFillUnits as r, readNodeFillableFieldKeys as i, readNodeRequiredFieldKeys as a } from "@rytass/bpm-core-shared";
2
+ import { readFormDataSourceErrorMessage as o, readFormTableRows as s } from "@rytass/bpm-core-client/form";
3
+ import { ATTACHMENT_NOT_BINDABLE_ERROR_CODE as c, isFormStaticOptionFieldDefinition as l, isOrgUnitFieldDefinition as u, isTableFieldDefinition as d } from "@rytass/bpm-core-shared/form";
4
+ // src/views/instances/approver-filled-form.ts
5
+ var f = (e, t) => Object.prototype.hasOwnProperty.call(e, t);
6
+ function p(e) {
7
+ return new Set(C(e).flatMap((e) => [...i(e)]));
8
+ }
9
+ function m(e) {
10
+ return C(e).flatMap((e) => r(e)).flatMap((e) => e.columnKey === null ? [] : [[e.fieldKey, e.columnKey]]).reduce((e, [t, n]) => new Map([...e, [t, /* @__PURE__ */ new Set([...e.get(t) ?? [], n])]]), /* @__PURE__ */ new Map());
11
+ }
12
+ function h(e, t) {
13
+ let n = p(t), r = m(t);
14
+ return {
15
+ ...e,
16
+ fields: e.fields.flatMap((e) => {
17
+ if (n.has(e.fieldKey)) return [];
18
+ let t = r.get(e.fieldKey);
19
+ if (t === void 0) return [e];
20
+ if (!d(e)) return [];
21
+ let i = e.columns.filter((e) => !t.has(e.fieldKey));
22
+ return i.length === 0 ? [] : [{
23
+ ...e,
24
+ columns: i
25
+ }];
26
+ })
27
+ };
28
+ }
29
+ function g(e, n) {
30
+ let r = p(n), i = m(n);
31
+ return Object.fromEntries(Object.entries(t(e, r)).flatMap(([e, n]) => {
32
+ let r = i.get(e);
33
+ if (r === void 0) return [[e, n]];
34
+ let a = s(n).map((e) => t(e, r));
35
+ return a.length === 0 || a.every((e) => Object.keys(e).length === 0) ? [] : [[e, a]];
36
+ }));
37
+ }
38
+ function _(e) {
39
+ let t = i(e);
40
+ return new Set(r(e).filter((e) => e.columnKey !== null && !t.has(e.fieldKey)).map((e) => e.fieldKey));
41
+ }
42
+ function v(e, t) {
43
+ return new Map(r(e).flatMap((e) => e.fieldKey === t && e.columnKey !== null ? [[e.columnKey, e.fill]] : []));
44
+ }
45
+ function y(e, t, n) {
46
+ if (!d(e)) return null;
47
+ let r = v(t, e.fieldKey), i = e.columns.filter((e) => n || r.has(e.fieldKey)).map((e) => ({
48
+ ...e,
49
+ required: r.get(e.fieldKey) === "REQUIRED",
50
+ requiredWhen: void 0
51
+ }));
52
+ return i.length === 0 ? null : {
53
+ ...e,
54
+ columns: i,
55
+ maxRows: void 0,
56
+ minRows: void 0,
57
+ required: !1,
58
+ requiredWhen: void 0
59
+ };
60
+ }
61
+ function b(e, t) {
62
+ return i(t).has(e.fieldKey) ? e : _(t).has(e.fieldKey) ? y(e, t, !1) : null;
63
+ }
64
+ function x(e, t) {
65
+ let n = S(e, t);
66
+ return {
67
+ ...n,
68
+ fields: n.fields.flatMap((e) => {
69
+ let n = b(e, t);
70
+ return n === null ? [] : [n];
71
+ })
72
+ };
73
+ }
74
+ function S(e, t) {
75
+ let n = i(t), r = a(t), o = _(t);
76
+ return {
77
+ ...e,
78
+ fields: e.fields.map((e) => n.has(e.fieldKey) ? {
79
+ ...e,
80
+ required: r.has(e.fieldKey),
81
+ requiredWhen: void 0
82
+ } : o.has(e.fieldKey) ? y(e, t, !0) ?? e : e)
83
+ };
84
+ }
85
+ var C = (e) => e.nodes.filter((e) => e.type === "userTask");
86
+ function w(e, t) {
87
+ let r = t === null ? /* @__PURE__ */ new Set() : n(t);
88
+ return C(e).reduce((e, t) => ({
89
+ ...Object.fromEntries([...n(t)].map((e) => [e, r.has(e) ? "本關補填" : `${t.data.label || t.id}補填`])),
90
+ ...e
91
+ }), {});
92
+ }
93
+ function T(e, t, n = {}) {
94
+ let r = _(t), a = [...i(t)].filter((t) => f(e, t)).map((t) => [t, e[t] ?? null]), o = [...r].filter((t) => f(e, t)).map((r) => [r, E(s(n[r]), s(e[r]), [...v(t, r).keys()])]);
95
+ return Object.fromEntries([...a, ...o]);
96
+ }
97
+ function E(e, t, n) {
98
+ return e.length === 0 ? null : e.map((e, r) => {
99
+ let i = t[r] ?? {};
100
+ return {
101
+ ...e,
102
+ ...Object.fromEntries(n.map((e) => [e, i[e] ?? null]))
103
+ };
104
+ });
105
+ }
106
+ function D({ draft: e, node: t, schema: n, stored: r, values: o }) {
107
+ let s = i(t), c = n.fields.filter((e) => {
108
+ let t = o[e.fieldKey];
109
+ return d(e) && s.has(e.fieldKey) && !Object.prototype.hasOwnProperty.call(r, e.fieldKey) && Array.isArray(t) && t.length > 0;
110
+ }), l = [...a(t)].filter((e) => s.has(e) && typeof o[e] == "boolean");
111
+ return {
112
+ ...Object.fromEntries(c.map((e) => [e.fieldKey, o[e.fieldKey]])),
113
+ ...Object.fromEntries(l.map((e) => [e, o[e]])),
114
+ ...e
115
+ };
116
+ }
117
+ function O(e) {
118
+ return e.includes("form data changed; refresh and retry");
119
+ }
120
+ var k = "FORM_FIELD_ACCESS_TABLE_ROWS_LOCKED", A = /^欄位「(.+?)」(.+)$/, j = /^「(.+?)」第 (\d+) 列(?:的「(.+?)」)?(.+)$/, M = "缺少必填欄位:";
121
+ function N(e, t, n) {
122
+ let r = n.settings?.label || n.settings?.fieldKey || "";
123
+ switch (e) {
124
+ case "REQUIRED": return n.topLevel && r !== "" ? `${r}為必填欄位。` : "此欄為必填。";
125
+ case "MIN_LENGTH": return `至少需要 ${t ?? 0} 個字。`;
126
+ case "MAX_LENGTH": return `最多 ${t ?? 0} 個字。`;
127
+ case "MIN_ROWS": return `至少需要 ${t ?? 0} 列。`;
128
+ case "MAX_ROWS": return `最多 ${t ?? 0} 列。`;
129
+ case "MAX_FILES": return `最多 ${t ?? 0} 個檔案。`;
130
+ case "PATTERN": return (n.settings && (n.settings.type === "text" || n.settings.type === "textarea") ? n.settings.patternErrorMessage?.trim() : void 0) || "格式不正確。";
131
+ case "PATTERN_TIMEOUT": return "格式檢查逾時,請縮短內容後再試,或請表單設計者簡化格式。";
132
+ default: return "此欄內容不正確。";
133
+ }
134
+ }
135
+ function P(e) {
136
+ let t = /^至少需要 (\d+) 列$/.exec(e), n = /^最多 (\d+) 列$/.exec(e), r = /^至少需要 (\d+) 個字$/.exec(e), i = /^最多 (\d+) 個字$/.exec(e), a = /^最多 (\d+) 個檔案$/.exec(e);
137
+ return e === "為必填" ? { reason: "REQUIRED" } : t ? {
138
+ limit: Number(t[1]),
139
+ reason: "MIN_ROWS"
140
+ } : n ? {
141
+ limit: Number(n[1]),
142
+ reason: "MAX_ROWS"
143
+ } : r ? {
144
+ limit: Number(r[1]),
145
+ reason: "MIN_LENGTH"
146
+ } : i ? {
147
+ limit: Number(i[1]),
148
+ reason: "MAX_LENGTH"
149
+ } : a ? {
150
+ limit: Number(a[1]),
151
+ reason: "MAX_FILES"
152
+ } : e === "格式不正確" ? { reason: "PATTERN" } : e === "無法在時限內完成格式檢查" ? { reason: "PATTERN_TIMEOUT" } : { reason: "TYPE" };
153
+ }
154
+ function F(e, t) {
155
+ let n = t.find((t) => e === t.fieldKey || d(t) && e.startsWith(`${t.fieldKey}[`));
156
+ if (!n) return null;
157
+ if (e === n.fieldKey) return {
158
+ key: e,
159
+ settings: n,
160
+ topLevel: !0
161
+ };
162
+ if (!d(n)) return null;
163
+ let r = /^\[(\d+)\](?:\.(.+))?$/u.exec(e.slice(n.fieldKey.length));
164
+ if (!r) return null;
165
+ let i = `${n.fieldKey}[${r[1]}]`, a = r[2];
166
+ if (a === void 0) return {
167
+ key: i,
168
+ settings: void 0,
169
+ topLevel: !1
170
+ };
171
+ let o = n.columns.find((e) => e.fieldKey === a);
172
+ return o ? {
173
+ key: `${i}.${o.fieldKey}`,
174
+ settings: o,
175
+ topLevel: !1
176
+ } : null;
177
+ }
178
+ function I(e, t) {
179
+ let n = e.map((e) => {
180
+ let n = F(e.path, t);
181
+ return n === null ? null : [n.key, N(e.reason, e.limit, n)];
182
+ });
183
+ return n.length === 0 || n.some((e) => e === null) ? null : Object.fromEntries(n.filter((e) => e !== null));
184
+ }
185
+ function L(e, t) {
186
+ let n = A.exec(e);
187
+ if (n) {
188
+ let e = t.find((e) => (e.label || e.fieldKey) === n[1]);
189
+ return e ? {
190
+ reason: n[2],
191
+ target: {
192
+ key: e.fieldKey,
193
+ settings: e,
194
+ topLevel: !0
195
+ }
196
+ } : null;
197
+ }
198
+ let r = j.exec(e);
199
+ if (!r) return null;
200
+ let i = t.find((e) => d(e) && (e.label || e.fieldKey) === r[1]);
201
+ if (!i || !d(i)) return null;
202
+ let a = `${i.fieldKey}[${Number(r[2]) - 1}]`, o = r[3];
203
+ if (o === void 0) return {
204
+ reason: r[4],
205
+ target: {
206
+ key: a,
207
+ settings: void 0,
208
+ topLevel: !1
209
+ }
210
+ };
211
+ let s = i.columns.find((e) => (e.label || e.fieldKey) === o);
212
+ return s ? {
213
+ reason: r[4],
214
+ target: {
215
+ key: `${a}.${s.fieldKey}`,
216
+ settings: s,
217
+ topLevel: !1
218
+ }
219
+ } : null;
220
+ }
221
+ function R(e, t, n, r = []) {
222
+ let i = x(t, n).fields;
223
+ if (r.length > 0) return I(r, i);
224
+ if (e.startsWith(M)) {
225
+ let t = `, ${e.slice(7)}, `, n = i.filter((e) => t.includes(`, ${e.label || e.fieldKey}, `));
226
+ return n.length > 0 ? Object.fromEntries(n.map((e) => [e.fieldKey, `${e.label || e.fieldKey}為必填欄位。`])) : null;
227
+ }
228
+ let a = e.split(";").map((e) => L(e, i));
229
+ return a.length === 0 || a.some((e) => e === null) ? null : Object.fromEntries(a.flatMap((e) => {
230
+ if (e === null) return [];
231
+ let { limit: t, reason: n } = P(e.reason);
232
+ return [[e.target.key, N(n, t, e.target)]];
233
+ }));
234
+ }
235
+ var z = "未知組織";
236
+ function B(e, t) {
237
+ return [...new Set(e.fields.filter(u).map((e) => t[e.fieldKey]).filter((e) => typeof e == "string" && e.trim().length > 0))];
238
+ }
239
+ function V(t, n, r, i) {
240
+ if (d(t)) {
241
+ let e = s(n), r = t.columns.filter((e) => e.type === "number" || e.type === "money").map((t) => ({
242
+ label: t.label || t.fieldKey,
243
+ total: e.reduce((e, n) => typeof n[t.fieldKey] == "number" && Number.isFinite(n[t.fieldKey]) ? e + n[t.fieldKey] : e, 0)
244
+ }));
245
+ return [`${e.length} 列`, ...e.length > 0 ? r.map(({ label: e, total: t }) => `${e}合計 ${t.toLocaleString("zh-TW")}`) : []].join(",");
246
+ }
247
+ if (e(n)) return "未填寫";
248
+ if (typeof n == "boolean") return n ? "是" : "否";
249
+ if (u(t)) return typeof n == "string" ? i.get(n) ?? "未知組織" : z;
250
+ let a = Array.isArray(n) ? n : [n], o = l(t) ? new Map(t.options.map((e) => [e.value, e.label])) : t.type === "member" ? r : /* @__PURE__ */ new Map();
251
+ return a.map((e) => typeof e == "string" ? o.get(e) ?? e : String(e)).join("、");
252
+ }
253
+ function H(e, t, n, r = /* @__PURE__ */ new Map(), i = /* @__PURE__ */ new Map()) {
254
+ return x(e, t).fields.flatMap((e) => f(n, e.fieldKey) ? [{
255
+ fieldKey: e.fieldKey,
256
+ label: e.label || e.fieldKey,
257
+ text: V(e, n[e.fieldKey], r, i)
258
+ }] : []);
259
+ }
260
+ // src/views/instances/instance-error-message.ts
261
+ var U = "表單中有附件無法附加到這個案件(只能附加申請人為本案上傳的檔案),請移除後重新上傳再送出。";
262
+ function W(e) {
263
+ let t = o(e);
264
+ if (t) return t;
265
+ let n = e instanceof Error ? e.message : null;
266
+ return n?.includes(c) ? U : n ?? "發生未知錯誤";
267
+ }
268
+ export { g as a, S as c, B as d, T as f, x as h, h as i, w as l, H as m, k as n, D as o, R as p, O as r, _ as s, W as t, v as u };
269
+
270
+ //# sourceMappingURL=instance-error-message-BKt07-Bo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instance-error-message-BKt07-Bo.js","names":["hasOwn","target","key","readApproverWholeFilledKeys","userTaskNodes","definition","readNodeFillableFieldKeys","node","readApproverOpenColumnKeys","readNodeFillUnits","unit","fieldKey","columnKey","grouped","readApplicantFormSchema","wholeKeys","openColumns","schema","field","open","isTableFieldDefinition","columns","column","readApplicantSubmission","omitFormDataKeys","values","value","rows","readFormTableRows","row","readFillColumnModeFieldKeys","readFillOpenColumns","readFillColumnOnlyTable","keepClosedColumns","readFillApproverField","readFillValidationSchema","displaySchema","readFillDisplaySchema","approverField","fillableKeys","requiredKeys","readNodeRequiredFieldKeys","columnModeKeys","readFillLabelMarkers","viewerKeys","viewerFillNode","readNodeFillGrantFieldKeys","markers","readFillPayload","wholeEntries","columnEntries","readFillColumnModeRows","stored","storedRows","draftRow","draftRows","index","storedRow","openColumnKeys","readFillChanges","seededTables","seeded","requiredBooleanKeys","draft","isFillConflictMessage","message","FILL_TABLE_ROWS_LOCKED_CODE","TOP_LEVEL_REF_PATTERN","TABLE_REF_PATTERN","MISSING_FIELDS_PREFIX","readFillIssueText","name","reason","limit","readFillReasonFromSentence","atLeastRows","sentence","atMostRows","shortest","longest","mostFiles","readFillIssueTarget","fields","path","candidate","rowPath","rowKey","readFillErrorsFromIssues","entries","issues","issue","entry","readFillErrorTarget","topLevel","part","tableRef","table","columnLabel","readFillServerErrors","fillableFields","formDataIssues","listed","missing","parts","UNKNOWN_FILL_ORG_UNIT_TEXT","readFillOrgUnitIds","isOrgUnitFieldDefinition","readFillValueText","totals","sum","label","total","isFormDataValueEmpty","orgUnitNames","items","optionLabels","isFormStaticOptionFieldDefinition","option","memberNames","item","readFillSummary","ATTACHMENT_NOT_BINDABLE_MESSAGE","readErrorMessage","dataSourceMessage","readFormDataSourceErrorMessage","error","ATTACHMENT_NOT_BINDABLE_ERROR_CODE"],"sources":["../../src/views/instances/approver-filled-form.ts","../../src/views/instances/instance-error-message.ts"],"sourcesContent":["import {\n isFormDataValueEmpty,\n omitFormDataKeys,\n readNodeFillableFieldKeys,\n readNodeFillGrantFieldKeys,\n readNodeFillUnits,\n readNodeRequiredFieldKeys,\n type FieldAccessFillUnit,\n type UserTaskNode,\n type WorkflowDefinition,\n} from '@rytass/bpm-core-shared';\nimport {\n isFormStaticOptionFieldDefinition,\n isOrgUnitFieldDefinition,\n isTableFieldDefinition,\n type FormDefinitionSchema,\n type FormFieldDefinition,\n type FormFieldValue,\n type FormDataIssue,\n type FormDataIssueReason,\n type FormTableCellValue,\n type FormTableRowValue,\n type TableColumnDefinition,\n} from '@rytass/bpm-core-shared/form';\nimport { readFormTableRows } from '@rytass/bpm-core-client/form';\nimport type { WorkflowFormData } from '@rytass/bpm-core-client/workflow';\n\nconst hasOwn = (target: object, key: string): boolean =>\n Object.prototype.hasOwnProperty.call(target, key);\n\n/** A fill grant on one column: the open cell's fill. */\ntype ColumnFill = FieldAccessFillUnit['fill'];\n\n/**\n * Every top-level field the approver fills as a whole, across all user tasks.\n * A field granted per column only is not here; see `readApproverOpenColumnKeys`.\n */\nfunction readApproverWholeFilledKeys(\n definition: WorkflowDefinition,\n): ReadonlySet<string> {\n return new Set(\n userTaskNodes(definition).flatMap((node) => [\n ...readNodeFillableFieldKeys(node),\n ]),\n );\n}\n\n/**\n * The columns each table opens to some approver, across all user tasks: a\n * table's `fieldKey` to the `columnKey`s an approver fills. Only column units\n * count; whole grants are `readApproverWholeFilledKeys`.\n */\nfunction readApproverOpenColumnKeys(\n definition: WorkflowDefinition,\n): ReadonlyMap<string, ReadonlySet<string>> {\n return userTaskNodes(definition)\n .flatMap((node) => readNodeFillUnits(node))\n .flatMap((unit): readonly (readonly [string, string])[] =>\n unit.columnKey === null ? [] : [[unit.fieldKey, unit.columnKey]],\n )\n .reduce<ReadonlyMap<string, ReadonlySet<string>>>(\n (grouped, [fieldKey, columnKey]) =>\n new Map([\n ...grouped,\n [fieldKey, new Set([...(grouped.get(fieldKey) ?? []), columnKey])],\n ]),\n new Map(),\n );\n}\n\n/**\n * The form as the applicant sees it: without the fields some approver fills in\n * as a whole, and without the columns some approver fills per column. A table\n * keeps its other columns, and is dropped when none remain. The server drops\n * those keys from a submission, so rendering one would swallow what the\n * applicant types, and a `minRows` or `required` on it would block a launch\n * nobody can complete. Never mutates its input.\n */\nexport function readApplicantFormSchema(\n schema: FormDefinitionSchema,\n definition: WorkflowDefinition,\n): FormDefinitionSchema {\n const wholeKeys = readApproverWholeFilledKeys(definition);\n const openColumns = readApproverOpenColumnKeys(definition);\n\n return {\n ...schema,\n fields: schema.fields.flatMap((field): readonly FormFieldDefinition[] => {\n if (wholeKeys.has(field.fieldKey)) {\n return [];\n }\n\n const open = openColumns.get(field.fieldKey);\n\n if (open === undefined) {\n return [field];\n }\n\n if (!isTableFieldDefinition(field)) {\n return [];\n }\n\n const columns = field.columns.filter(\n (column) => !open.has(column.fieldKey),\n );\n\n return columns.length === 0 ? [] : [{ ...field, columns }];\n }),\n };\n}\n\n/**\n * The payload the applicant may send: `values` without the keys an approver\n * fills as a whole, and without the open cells of the tables an approver fills\n * per column. A table left with no applicant cell at all is omitted, so the\n * server keeps the approver's cells as they are.\n *\n * Known limit: the schema is not passed in, so a table whose every column is\n * approver-filled is recognised by its rows, not by its columns. A table that\n * had no rows is omitted for the same reason.\n */\nexport function readApplicantSubmission(\n values: WorkflowFormData,\n definition: WorkflowDefinition,\n): WorkflowFormData {\n const wholeKeys = readApproverWholeFilledKeys(definition);\n const openColumns = readApproverOpenColumnKeys(definition);\n\n return Object.fromEntries(\n Object.entries(omitFormDataKeys(values, wholeKeys)).flatMap(\n ([fieldKey, value]): readonly (readonly [string, unknown])[] => {\n const open = openColumns.get(fieldKey);\n\n if (open === undefined) {\n return [[fieldKey, value]];\n }\n\n // A non-table value reads as no rows, so an approver's non-table\n // per-column field is dropped with the table case.\n const rows = readFormTableRows(value as FormFieldValue).map((row) =>\n omitFormDataKeys(row, open),\n );\n\n return rows.length === 0 ||\n rows.every((row) => Object.keys(row).length === 0)\n ? []\n : [[fieldKey, rows]];\n },\n ),\n ) as WorkflowFormData;\n}\n\n/**\n * Top-level fields this node lets its approver fill only per column (ADR 27\n * §4(a)). A field the node also grants whole is not in this set.\n */\nexport function readFillColumnModeFieldKeys(\n node: UserTaskNode,\n): ReadonlySet<string> {\n const wholeKeys = readNodeFillableFieldKeys(node);\n\n return new Set(\n readNodeFillUnits(node)\n .filter(\n (unit) => unit.columnKey !== null && !wholeKeys.has(unit.fieldKey),\n )\n .map((unit) => unit.fieldKey),\n );\n}\n\n/**\n * The columns of `fieldKey` this node opens to its approver, each with its\n * fill. Empty when the node opens none of them.\n */\nexport function readFillOpenColumns(\n node: UserTaskNode,\n fieldKey: string,\n): ReadonlyMap<string, ColumnFill> {\n return new Map(\n readNodeFillUnits(node).flatMap(\n (unit): readonly (readonly [string, ColumnFill])[] =>\n unit.fieldKey === fieldKey && unit.columnKey !== null\n ? [[unit.columnKey, unit.fill]]\n : [],\n ),\n );\n}\n\n/**\n * The approver's view of a column-only table: its open columns, each required\n * exactly when the node says `REQUIRED`. The row bounds go, since the applicant\n * owns the row count; the closed columns go too, since the approver may not\n * fill them. Other fields, and tables the node opens whole, come back as is.\n */\nfunction readFillColumnOnlyTable(\n field: FormFieldDefinition,\n node: UserTaskNode,\n keepClosedColumns: boolean,\n): FormFieldDefinition | null {\n if (!isTableFieldDefinition(field)) {\n return null;\n }\n\n const open = readFillOpenColumns(node, field.fieldKey);\n const columns = field.columns\n .filter((column) => keepClosedColumns || open.has(column.fieldKey))\n .map((column): TableColumnDefinition => ({\n ...column,\n // The node alone decides; the form's rule for the column is ignored.\n required: open.get(column.fieldKey) === 'REQUIRED',\n requiredWhen: undefined,\n }));\n\n return columns.length === 0\n ? null\n : {\n ...field,\n columns,\n maxRows: undefined,\n minRows: undefined,\n required: false,\n requiredWhen: undefined,\n };\n}\n\n/**\n * The part of `field` the node's approver fills in, or `null` when they fill\n * none of it: a whole fillable field as it is, a column-only table as its open\n * columns. Never mutates its input.\n */\nfunction readFillApproverField(\n field: FormFieldDefinition,\n node: UserTaskNode,\n): FormFieldDefinition | null {\n if (readNodeFillableFieldKeys(node).has(field.fieldKey)) {\n return field;\n }\n\n return readFillColumnModeFieldKeys(node).has(field.fieldKey)\n ? readFillColumnOnlyTable(field, node, false)\n : null;\n}\n\n/**\n * The form an approver's fill is validated against: only what `node` lets them\n * fill in, each whole field required exactly when the node says `REQUIRED`,\n * and each column-only table reduced to its open columns. A required table\n * needs at least one row, the rule `validateFormRendererValues` already\n * applies, so the result agrees with the server's fill check. Never mutates its\n * input.\n */\nexport function readFillValidationSchema(\n schema: FormDefinitionSchema,\n node: UserTaskNode,\n): FormDefinitionSchema {\n const displaySchema = readFillDisplaySchema(schema, node);\n\n // Derived from the display schema so the check and the asterisk read the\n // same rule.\n return {\n ...displaySchema,\n fields: displaySchema.fields.flatMap((field) => {\n const approverField = readFillApproverField(field, node);\n\n return approverField === null ? [] : [approverField];\n }),\n };\n}\n\n/**\n * The schema the fill node's approver sees: every field as the form defines\n * it, except that the node's fillable fields are required exactly when the\n * node says `REQUIRED`, so the asterisk matches what approving will check. A\n * column-only table keeps every column, so the closed ones still show; their\n * requirement is cleared, and the open ones follow the node.\n */\nexport function readFillDisplaySchema(\n schema: FormDefinitionSchema,\n node: UserTaskNode,\n): FormDefinitionSchema {\n const fillableKeys = readNodeFillableFieldKeys(node);\n const requiredKeys = readNodeRequiredFieldKeys(node);\n const columnModeKeys = readFillColumnModeFieldKeys(node);\n\n return {\n ...schema,\n fields: schema.fields.map((field) => {\n if (fillableKeys.has(field.fieldKey)) {\n return {\n ...field,\n required: requiredKeys.has(field.fieldKey),\n // The node alone decides; the server ignores the form's rule.\n requiredWhen: undefined,\n };\n }\n\n return columnModeKeys.has(field.fieldKey)\n ? (readFillColumnOnlyTable(field, node, true) ?? field)\n : field;\n }),\n };\n}\n\nconst userTaskNodes = (\n definition: WorkflowDefinition,\n): readonly UserTaskNode[] =>\n definition.nodes.filter(\n (node): node is UserTaskNode => node.type === 'userTask',\n );\n\n/**\n * The marker shown after each approver-filled field's label: 「本關補填」 on\n * the fields `viewerFillNode` lets the viewer fill, 「<node>補填」 on every\n * other approver-filled field (the first node in the definition that fills\n * it names it).\n */\nexport function readFillLabelMarkers(\n definition: WorkflowDefinition,\n viewerFillNode: UserTaskNode | null,\n): Readonly<Record<string, string>> {\n const viewerKeys =\n viewerFillNode === null\n ? new Set<string>()\n : readNodeFillGrantFieldKeys(viewerFillNode);\n\n return userTaskNodes(definition).reduce<Readonly<Record<string, string>>>(\n (markers, node) => ({\n ...Object.fromEntries(\n [...readNodeFillGrantFieldKeys(node)].map((fieldKey) => [\n fieldKey,\n viewerKeys.has(fieldKey)\n ? '本關補填'\n : `${node.data.label || node.id}補填`,\n ]),\n ),\n ...markers,\n }),\n {},\n );\n}\n\n/**\n * What approving sends as `fill.formData`: the keys of `values` that `node`\n * lets its approver fill, and nothing else (the server refuses any other key).\n * `values` is the fill changes from `readFillChanges`, not the rendered form:\n * a renderer default the approver never touched (a table's empty rows, an\n * OPTIONAL boolean's `false`) is not theirs to sign and stays out. A REQUIRED\n * boolean is the exception; its shown value is sent, see `readFillChanges`.\n */\nexport function readFillPayload(\n values: WorkflowFormData,\n node: UserTaskNode,\n stored: WorkflowFormData = {},\n): Readonly<Record<string, unknown>> {\n const columnModeKeys = readFillColumnModeFieldKeys(node);\n const wholeEntries = [...readNodeFillableFieldKeys(node)]\n .filter((fieldKey) => hasOwn(values, fieldKey))\n // A cleared field reads `undefined`, which JSON drops, and the server\n // would keep the old value; `null` clears it.\n .map((fieldKey): readonly [string, unknown] => [\n fieldKey,\n values[fieldKey] ?? null,\n ]);\n const columnEntries = [...columnModeKeys]\n .filter((fieldKey) => hasOwn(values, fieldKey))\n .map((fieldKey): readonly [string, unknown] => [\n fieldKey,\n readFillColumnModeRows(\n readFormTableRows(stored[fieldKey]),\n readFormTableRows(values[fieldKey]),\n [...readFillOpenColumns(node, fieldKey).keys()],\n ),\n ]);\n\n return Object.fromEntries([...wholeEntries, ...columnEntries]);\n}\n\n/**\n * The rows a column-mode fill sends (ADR 27 §4(a)): one per stored row, in the\n * stored order, each carrying the applicant's cells unchanged and the open\n * cells as the approver left them. `null` when nothing is stored, since the\n * server has no row to settle against. The row count and order never come\n * from the draft.\n */\nfunction readFillColumnModeRows(\n storedRows: readonly FormTableRowValue[],\n draftRows: readonly FormTableRowValue[],\n openColumnKeys: readonly string[],\n): readonly FormTableRowValue[] | null {\n if (storedRows.length === 0) {\n return null;\n }\n\n return storedRows.map((storedRow, index) => {\n const draftRow = draftRows[index] ?? {};\n\n return {\n ...storedRow,\n ...Object.fromEntries(\n openColumnKeys.map(\n (columnKey): readonly [string, FormTableCellValue] => [\n columnKey,\n draftRow[columnKey] ?? null,\n ],\n ),\n ),\n };\n });\n}\n\n/**\n * What the approver's fill amounts to: the fields they changed (`draft`), plus\n * each fill table the page seeds with `minRows` rows that the case does not\n * store yet, and each REQUIRED boolean fill field at the value the page shows.\n * The page shows those rows and values and validates them, so they are sent\n * too. Left out, the server would refuse the approval for a missing row, and\n * for a missing `false` that the approver can see on screen: a boolean that\n * is never sent reads as empty, while `false` is a value. Changed values\n * override the displayed ones.\n */\nexport function readFillChanges({\n draft,\n node,\n schema,\n stored,\n values,\n}: {\n readonly draft: WorkflowFormData;\n readonly node: UserTaskNode;\n readonly schema: FormDefinitionSchema;\n readonly stored: WorkflowFormData;\n readonly values: WorkflowFormData;\n}): WorkflowFormData {\n const fillableKeys = readNodeFillableFieldKeys(node);\n const seededTables = schema.fields.filter((field) => {\n const seeded = values[field.fieldKey];\n\n return (\n isTableFieldDefinition(field) &&\n fillableKeys.has(field.fieldKey) &&\n !Object.prototype.hasOwnProperty.call(stored, field.fieldKey) &&\n Array.isArray(seeded) &&\n seeded.length > 0\n );\n });\n\n const requiredBooleanKeys = [...readNodeRequiredFieldKeys(node)].filter(\n (fieldKey) =>\n fillableKeys.has(fieldKey) && typeof values[fieldKey] === 'boolean',\n );\n\n return {\n ...Object.fromEntries(\n seededTables.map((field) => [field.fieldKey, values[field.fieldKey]]),\n ),\n ...Object.fromEntries(\n requiredBooleanKeys.map((fieldKey) => [fieldKey, values[fieldKey]]),\n ),\n ...draft,\n };\n}\n\n/** The server message of a fill against form data someone else changed. */\nexport function isFillConflictMessage(message: string): boolean {\n return message.includes('form data changed; refresh and retry');\n}\n\n/**\n * The stable code the server answers when an approver's fill adds, removes or\n * reorders rows of a column-only table (ADR 27 §4(a)). Its message is English;\n * callers show their own copy from this code.\n */\nexport const FILL_TABLE_ROWS_LOCKED_CODE =\n 'FORM_FIELD_ACCESS_TABLE_ROWS_LOCKED';\n\n// The server names a field by its label: `欄位「標籤」`, a table row as\n// `「表格」第 N 列` (1-based), and a cell as `「表格」第 N 列的「欄位」`.\n// Each part of a message is separated by `;`. This is the fallback for a\n// response that carries no `formDataIssues`: labels are not unique, so the\n// issues, which address each value by its path, are the primary route.\nconst TOP_LEVEL_REF_PATTERN = /^欄位「(.+?)」(.+)$/;\nconst TABLE_REF_PATTERN = /^「(.+?)」第 (\\d+) 列(?:的「(.+?)」)?(.+)$/;\nconst MISSING_FIELDS_PREFIX = '缺少必填欄位:';\n\n/** The field, or table column, a server error names, with its renderer key. */\ninterface FillErrorTarget {\n readonly key: string;\n readonly settings: FormFieldDefinition | TableColumnDefinition | undefined;\n readonly topLevel: boolean;\n}\n\n/** The sentence a refused value shows, by its reason. */\nfunction readFillIssueText(\n reason: FormDataIssueReason,\n limit: number | undefined,\n target: FillErrorTarget,\n): string {\n const name = target.settings?.label || target.settings?.fieldKey || '';\n\n switch (reason) {\n case 'REQUIRED':\n return target.topLevel && name !== ''\n ? `${name}為必填欄位。`\n : '此欄為必填。';\n case 'MIN_LENGTH':\n return `至少需要 ${limit ?? 0} 個字。`;\n case 'MAX_LENGTH':\n return `最多 ${limit ?? 0} 個字。`;\n case 'MIN_ROWS':\n return `至少需要 ${limit ?? 0} 列。`;\n case 'MAX_ROWS':\n return `最多 ${limit ?? 0} 列。`;\n case 'MAX_FILES':\n return `最多 ${limit ?? 0} 個檔案。`;\n case 'PATTERN': {\n const message =\n target.settings &&\n (target.settings.type === 'text' || target.settings.type === 'textarea')\n ? target.settings.patternErrorMessage?.trim()\n : undefined;\n\n return message || '格式不正確。';\n }\n // The server ran out of time on the format check; the value may be fine.\n case 'PATTERN_TIMEOUT':\n return '格式檢查逾時,請縮短內容後再試,或請表單設計者簡化格式。';\n default:\n return '此欄內容不正確。';\n }\n}\n\n/** The reason a message sentence stands for, for the message fallback. */\nfunction readFillReasonFromSentence(sentence: string): {\n readonly limit?: number;\n readonly reason: FormDataIssueReason;\n} {\n const atLeastRows = /^至少需要 (\\d+) 列$/.exec(sentence);\n const atMostRows = /^最多 (\\d+) 列$/.exec(sentence);\n const shortest = /^至少需要 (\\d+) 個字$/.exec(sentence);\n const longest = /^最多 (\\d+) 個字$/.exec(sentence);\n const mostFiles = /^最多 (\\d+) 個檔案$/.exec(sentence);\n\n if (sentence === '為必填') {\n return { reason: 'REQUIRED' };\n }\n\n if (atLeastRows) {\n return { limit: Number(atLeastRows[1]), reason: 'MIN_ROWS' };\n }\n\n if (atMostRows) {\n return { limit: Number(atMostRows[1]), reason: 'MAX_ROWS' };\n }\n\n if (shortest) {\n return { limit: Number(shortest[1]), reason: 'MIN_LENGTH' };\n }\n\n if (longest) {\n return { limit: Number(longest[1]), reason: 'MAX_LENGTH' };\n }\n\n if (mostFiles) {\n return { limit: Number(mostFiles[1]), reason: 'MAX_FILES' };\n }\n\n if (sentence === '格式不正確') {\n return { reason: 'PATTERN' };\n }\n\n if (sentence === '無法在時限內完成格式檢查') {\n return { reason: 'PATTERN_TIMEOUT' };\n }\n\n return { reason: 'TYPE' };\n}\n\n/**\n * The renderer key of the value an issue path addresses, among the fields\n * `fields` lets the approver fill. `items` is a table, `items[1]` a row,\n * `items[1].qty` a cell. `null` when the path names nothing fillable.\n */\nfunction readFillIssueTarget(\n path: string,\n fields: readonly FormFieldDefinition[],\n): FillErrorTarget | null {\n const field = fields.find(\n (candidate) =>\n path === candidate.fieldKey ||\n (isTableFieldDefinition(candidate) &&\n path.startsWith(`${candidate.fieldKey}[`)),\n );\n\n if (!field) {\n return null;\n }\n\n if (path === field.fieldKey) {\n return { key: path, settings: field, topLevel: true };\n }\n\n if (!isTableFieldDefinition(field)) {\n return null;\n }\n\n const rowPath = /^\\[(\\d+)\\](?:\\.(.+))?$/u.exec(\n path.slice(field.fieldKey.length),\n );\n\n if (!rowPath) {\n return null;\n }\n\n const rowKey = `${field.fieldKey}[${rowPath[1]}]`;\n const columnKey = rowPath[2];\n\n if (columnKey === undefined) {\n return { key: rowKey, settings: undefined, topLevel: false };\n }\n\n const column = field.columns.find(\n (candidate) => candidate.fieldKey === columnKey,\n );\n\n return column\n ? { key: `${rowKey}.${column.fieldKey}`, settings: column, topLevel: false }\n : null;\n}\n\nfunction readFillErrorsFromIssues(\n issues: readonly FormDataIssue[],\n fields: readonly FormFieldDefinition[],\n): Readonly<Record<string, string>> | null {\n const entries = issues.map((issue): readonly [string, string] | null => {\n const target = readFillIssueTarget(issue.path, fields);\n\n return target === null\n ? null\n : [target.key, readFillIssueText(issue.reason, issue.limit, target)];\n });\n\n if (entries.length === 0 || entries.some((entry) => entry === null)) {\n return null;\n }\n\n return Object.fromEntries(\n entries.filter(\n (entry): entry is readonly [string, string] => entry !== null,\n ),\n );\n}\n\nfunction readFillErrorTarget(\n part: string,\n fields: readonly FormFieldDefinition[],\n): { readonly target: FillErrorTarget; readonly reason: string } | null {\n const topLevel = TOP_LEVEL_REF_PATTERN.exec(part);\n\n if (topLevel) {\n const field = fields.find(\n (candidate) => (candidate.label || candidate.fieldKey) === topLevel[1],\n );\n\n return field\n ? {\n reason: topLevel[2],\n target: { key: field.fieldKey, settings: field, topLevel: true },\n }\n : null;\n }\n\n const tableRef = TABLE_REF_PATTERN.exec(part);\n\n if (!tableRef) {\n return null;\n }\n\n const table = fields.find(\n (candidate) =>\n isTableFieldDefinition(candidate) &&\n (candidate.label || candidate.fieldKey) === tableRef[1],\n );\n\n if (!table || !isTableFieldDefinition(table)) {\n return null;\n }\n\n const rowKey = `${table.fieldKey}[${Number(tableRef[2]) - 1}]`;\n const columnLabel = tableRef[3];\n\n if (columnLabel === undefined) {\n return {\n reason: tableRef[4],\n target: { key: rowKey, settings: undefined, topLevel: false },\n };\n }\n\n const column = table.columns.find(\n (candidate) => (candidate.label || candidate.fieldKey) === columnLabel,\n );\n\n return column\n ? {\n reason: tableRef[4],\n target: {\n key: `${rowKey}.${column.fieldKey}`,\n settings: column,\n topLevel: false,\n },\n }\n : null;\n}\n\n/**\n * Turns the server's fill validation refusal (docs/08 §8.5, error table 2)\n * into field errors keyed the way `FormRenderer` addresses them, so a check\n * the page missed still lands on the field. `formDataIssues` is the refusal's\n * structured list and wins when it is present: each issue names its value by\n * path, so two fields sharing a label stay apart. Without it, the Chinese\n * `message` is parsed. `null` when any part does not resolve to a fillable\n * field — the caller then shows the message as it is.\n */\nexport function readFillServerErrors(\n message: string,\n schema: FormDefinitionSchema,\n node: UserTaskNode,\n formDataIssues: readonly FormDataIssue[] = [],\n): Readonly<Record<string, string>> | null {\n const fillableFields = readFillValidationSchema(schema, node).fields;\n\n if (formDataIssues.length > 0) {\n return readFillErrorsFromIssues(formDataIssues, fillableFields);\n }\n\n if (message.startsWith(MISSING_FIELDS_PREFIX)) {\n // Labels may hold commas, so match each fillable field's label (or key)\n // against the list rather than splitting it.\n const listed = `, ${message.slice(MISSING_FIELDS_PREFIX.length)}, `;\n const missing = fillableFields.filter((field) =>\n listed.includes(`, ${field.label || field.fieldKey}, `),\n );\n\n return missing.length > 0\n ? Object.fromEntries(\n missing.map((field) => [\n field.fieldKey,\n `${field.label || field.fieldKey}為必填欄位。`,\n ]),\n )\n : null;\n }\n\n const parts = message\n .split(';')\n .map((part) => readFillErrorTarget(part, fillableFields));\n\n if (parts.length === 0 || parts.some((part) => part === null)) {\n return null;\n }\n\n return Object.fromEntries(\n parts.flatMap((part) => {\n if (part === null) {\n return [];\n }\n\n const { limit, reason } = readFillReasonFromSentence(part.reason);\n\n return [[part.target.key, readFillIssueText(reason, limit, part.target)]];\n }),\n );\n}\n\nexport interface FillSummaryItem {\n readonly fieldKey: string;\n readonly label: string;\n readonly text: string;\n}\n\nexport const UNKNOWN_FILL_ORG_UNIT_TEXT = '未知組織';\n\n/**\n * The org unit ids among `values`, once each and in form order, so the caller\n * can look up their names before building the summary.\n */\nexport function readFillOrgUnitIds(\n schema: FormDefinitionSchema,\n values: WorkflowFormData,\n): readonly string[] {\n return [\n ...new Set(\n schema.fields\n .filter(isOrgUnitFieldDefinition)\n .map((field) => values[field.fieldKey])\n .filter(\n (value): value is string =>\n typeof value === 'string' && value.trim().length > 0,\n ),\n ),\n ];\n}\n\nfunction readFillValueText(\n field: FormFieldDefinition,\n value: FormFieldValue | undefined,\n memberNames: ReadonlyMap<string, string>,\n orgUnitNames: ReadonlyMap<string, string>,\n): string {\n if (isTableFieldDefinition(field)) {\n const rows = readFormTableRows(value);\n const totals = field.columns\n .filter((column) => column.type === 'number' || column.type === 'money')\n .map((column) => ({\n label: column.label || column.fieldKey,\n total: rows.reduce(\n (sum, row) =>\n typeof row[column.fieldKey] === 'number' &&\n Number.isFinite(row[column.fieldKey])\n ? sum + (row[column.fieldKey] as number)\n : sum,\n 0,\n ),\n }));\n\n return [\n `${rows.length} 列`,\n ...(rows.length > 0\n ? totals.map(\n ({ label, total }) =>\n `${label}合計 ${total.toLocaleString('zh-TW')}`,\n )\n : []),\n ].join(',');\n }\n\n if (isFormDataValueEmpty(value)) {\n return '未填寫';\n }\n\n if (typeof value === 'boolean') {\n return value ? '是' : '否';\n }\n\n if (isOrgUnitFieldDefinition(field)) {\n // `orgUnitNames` (`useOrgUnitNames`) maps an id still being looked up to\n // a loading text; an id the lookup did not name is 「未知組織」. Never the\n // raw id.\n return typeof value === 'string'\n ? (orgUnitNames.get(value) ?? UNKNOWN_FILL_ORG_UNIT_TEXT)\n : UNKNOWN_FILL_ORG_UNIT_TEXT;\n }\n\n const items = Array.isArray(value) ? value : [value];\n const optionLabels = isFormStaticOptionFieldDefinition(field)\n ? new Map(field.options.map((option) => [option.value, option.label]))\n : field.type === 'member'\n ? memberNames\n : new Map<string, string>();\n\n return items\n .map((item) =>\n typeof item === 'string'\n ? (optionLabels.get(item) ?? item)\n : String(item),\n )\n .join('、');\n}\n\n/**\n * One line per field `node` lets its approver fill, in form order, for the\n * approve dialog: what will be sent along with the approval. A member field\n * names each member found in `memberNames` and shows the id otherwise; an org\n * unit field names its unit from `orgUnitNames` and shows 「未知組織」 otherwise.\n */\nexport function readFillSummary(\n schema: FormDefinitionSchema,\n node: UserTaskNode,\n values: WorkflowFormData,\n memberNames: ReadonlyMap<string, string> = new Map(),\n orgUnitNames: ReadonlyMap<string, string> = new Map(),\n): readonly FillSummaryItem[] {\n return readFillValidationSchema(schema, node).fields.flatMap(\n (field): readonly FillSummaryItem[] =>\n hasOwn(values, field.fieldKey)\n ? [\n {\n fieldKey: field.fieldKey,\n label: field.label || field.fieldKey,\n text: readFillValueText(\n field,\n values[field.fieldKey],\n memberNames,\n orgUnitNames,\n ),\n },\n ]\n : [],\n );\n}\n","import { readFormDataSourceErrorMessage } from '@rytass/bpm-core-client/form';\nimport { ATTACHMENT_NOT_BINDABLE_ERROR_CODE } from '@rytass/bpm-core-shared/form';\n\nconst ATTACHMENT_NOT_BINDABLE_MESSAGE =\n '表單中有附件無法附加到這個案件(只能附加申請人為本案上傳的檔案),請移除後重新上傳再送出。';\n\n/**\n * The message a case page shows for a failed request: a stable server code\n * translated into words, otherwise the server's own message.\n */\nexport function readErrorMessage(error: unknown): string {\n // DataSource failures arrive as stable codes so the backend never leaks\n // upstream transport detail; translate them instead of showing the raw code.\n const dataSourceMessage = readFormDataSourceErrorMessage(error);\n\n if (dataSourceMessage) {\n return dataSourceMessage;\n }\n\n const message = error instanceof Error ? error.message : null;\n\n // Names the problem, not the attachment ids the server lists.\n if (message?.includes(ATTACHMENT_NOT_BINDABLE_ERROR_CODE)) {\n return ATTACHMENT_NOT_BINDABLE_MESSAGE;\n }\n\n return message ?? '發生未知錯誤';\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ let e=require("@rytass/bpm-core-shared"),t=require("@rytass/bpm-core-client/form"),n=require("@rytass/bpm-core-shared/form");var r=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);function i(t){return new Set(m(t).flatMap(t=>[...(0,e.readNodeFillableFieldKeys)(t)]))}function a(t){return m(t).flatMap(t=>(0,e.readNodeFillUnits)(t)).flatMap(e=>e.columnKey===null?[]:[[e.fieldKey,e.columnKey]]).reduce((e,[t,n])=>new Map([...e,[t,new Set([...e.get(t)??[],n])]]),new Map)}function o(e,t){let r=i(t),o=a(t);return{...e,fields:e.fields.flatMap(e=>{if(r.has(e.fieldKey))return[];let t=o.get(e.fieldKey);if(t===void 0)return[e];if(!(0,n.isTableFieldDefinition)(e))return[];let i=e.columns.filter(e=>!t.has(e.fieldKey));return i.length===0?[]:[{...e,columns:i}]})}}function s(n,r){let o=i(r),s=a(r);return Object.fromEntries(Object.entries((0,e.omitFormDataKeys)(n,o)).flatMap(([n,r])=>{let i=s.get(n);if(i===void 0)return[[n,r]];let a=(0,t.readFormTableRows)(r).map(t=>(0,e.omitFormDataKeys)(t,i));return a.length===0||a.every(e=>Object.keys(e).length===0)?[]:[[n,a]]}))}function c(t){let n=(0,e.readNodeFillableFieldKeys)(t);return new Set((0,e.readNodeFillUnits)(t).filter(e=>e.columnKey!==null&&!n.has(e.fieldKey)).map(e=>e.fieldKey))}function l(t,n){return new Map((0,e.readNodeFillUnits)(t).flatMap(e=>e.fieldKey===n&&e.columnKey!==null?[[e.columnKey,e.fill]]:[]))}function u(e,t,r){if(!(0,n.isTableFieldDefinition)(e))return null;let i=l(t,e.fieldKey),a=e.columns.filter(e=>r||i.has(e.fieldKey)).map(e=>({...e,required:i.get(e.fieldKey)===`REQUIRED`,requiredWhen:void 0}));return a.length===0?null:{...e,columns:a,maxRows:void 0,minRows:void 0,required:!1,requiredWhen:void 0}}function d(t,n){return(0,e.readNodeFillableFieldKeys)(n).has(t.fieldKey)?t:c(n).has(t.fieldKey)?u(t,n,!1):null}function f(e,t){let n=p(e,t);return{...n,fields:n.fields.flatMap(e=>{let n=d(e,t);return n===null?[]:[n]})}}function p(t,n){let r=(0,e.readNodeFillableFieldKeys)(n),i=(0,e.readNodeRequiredFieldKeys)(n),a=c(n);return{...t,fields:t.fields.map(e=>r.has(e.fieldKey)?{...e,required:i.has(e.fieldKey),requiredWhen:void 0}:a.has(e.fieldKey)?u(e,n,!0)??e:e)}}var m=e=>e.nodes.filter(e=>e.type===`userTask`);function h(t,n){let r=n===null?new Set:(0,e.readNodeFillGrantFieldKeys)(n);return m(t).reduce((t,n)=>({...Object.fromEntries([...(0,e.readNodeFillGrantFieldKeys)(n)].map(e=>[e,r.has(e)?`本關補填`:`${n.data.label||n.id}補填`])),...t}),{})}function g(n,i,a={}){let o=c(i),s=[...(0,e.readNodeFillableFieldKeys)(i)].filter(e=>r(n,e)).map(e=>[e,n[e]??null]),u=[...o].filter(e=>r(n,e)).map(e=>[e,_((0,t.readFormTableRows)(a[e]),(0,t.readFormTableRows)(n[e]),[...l(i,e).keys()])]);return Object.fromEntries([...s,...u])}function _(e,t,n){return e.length===0?null:e.map((e,r)=>{let i=t[r]??{};return{...e,...Object.fromEntries(n.map(e=>[e,i[e]??null]))}})}function v({draft:t,node:r,schema:i,stored:a,values:o}){let s=(0,e.readNodeFillableFieldKeys)(r),c=i.fields.filter(e=>{let t=o[e.fieldKey];return(0,n.isTableFieldDefinition)(e)&&s.has(e.fieldKey)&&!Object.prototype.hasOwnProperty.call(a,e.fieldKey)&&Array.isArray(t)&&t.length>0}),l=[...(0,e.readNodeRequiredFieldKeys)(r)].filter(e=>s.has(e)&&typeof o[e]==`boolean`);return{...Object.fromEntries(c.map(e=>[e.fieldKey,o[e.fieldKey]])),...Object.fromEntries(l.map(e=>[e,o[e]])),...t}}function y(e){return e.includes(`form data changed; refresh and retry`)}var b=`FORM_FIELD_ACCESS_TABLE_ROWS_LOCKED`,x=/^欄位「(.+?)」(.+)$/,S=/^「(.+?)」第 (\d+) 列(?:的「(.+?)」)?(.+)$/,C=`缺少必填欄位:`;function w(e,t,n){let r=n.settings?.label||n.settings?.fieldKey||``;switch(e){case`REQUIRED`:return n.topLevel&&r!==``?`${r}為必填欄位。`:`此欄為必填。`;case`MIN_LENGTH`:return`至少需要 ${t??0} 個字。`;case`MAX_LENGTH`:return`最多 ${t??0} 個字。`;case`MIN_ROWS`:return`至少需要 ${t??0} 列。`;case`MAX_ROWS`:return`最多 ${t??0} 列。`;case`MAX_FILES`:return`最多 ${t??0} 個檔案。`;case`PATTERN`:return(n.settings&&(n.settings.type===`text`||n.settings.type===`textarea`)?n.settings.patternErrorMessage?.trim():void 0)||`格式不正確。`;case`PATTERN_TIMEOUT`:return`格式檢查逾時,請縮短內容後再試,或請表單設計者簡化格式。`;default:return`此欄內容不正確。`}}function T(e){let t=/^至少需要 (\d+) 列$/.exec(e),n=/^最多 (\d+) 列$/.exec(e),r=/^至少需要 (\d+) 個字$/.exec(e),i=/^最多 (\d+) 個字$/.exec(e),a=/^最多 (\d+) 個檔案$/.exec(e);return e===`為必填`?{reason:`REQUIRED`}:t?{limit:Number(t[1]),reason:`MIN_ROWS`}:n?{limit:Number(n[1]),reason:`MAX_ROWS`}:r?{limit:Number(r[1]),reason:`MIN_LENGTH`}:i?{limit:Number(i[1]),reason:`MAX_LENGTH`}:a?{limit:Number(a[1]),reason:`MAX_FILES`}:e===`格式不正確`?{reason:`PATTERN`}:e===`無法在時限內完成格式檢查`?{reason:`PATTERN_TIMEOUT`}:{reason:`TYPE`}}function E(e,t){let r=t.find(t=>e===t.fieldKey||(0,n.isTableFieldDefinition)(t)&&e.startsWith(`${t.fieldKey}[`));if(!r)return null;if(e===r.fieldKey)return{key:e,settings:r,topLevel:!0};if(!(0,n.isTableFieldDefinition)(r))return null;let i=/^\[(\d+)\](?:\.(.+))?$/u.exec(e.slice(r.fieldKey.length));if(!i)return null;let a=`${r.fieldKey}[${i[1]}]`,o=i[2];if(o===void 0)return{key:a,settings:void 0,topLevel:!1};let s=r.columns.find(e=>e.fieldKey===o);return s?{key:`${a}.${s.fieldKey}`,settings:s,topLevel:!1}:null}function D(e,t){let n=e.map(e=>{let n=E(e.path,t);return n===null?null:[n.key,w(e.reason,e.limit,n)]});return n.length===0||n.some(e=>e===null)?null:Object.fromEntries(n.filter(e=>e!==null))}function O(e,t){let r=x.exec(e);if(r){let e=t.find(e=>(e.label||e.fieldKey)===r[1]);return e?{reason:r[2],target:{key:e.fieldKey,settings:e,topLevel:!0}}:null}let i=S.exec(e);if(!i)return null;let a=t.find(e=>(0,n.isTableFieldDefinition)(e)&&(e.label||e.fieldKey)===i[1]);if(!a||!(0,n.isTableFieldDefinition)(a))return null;let o=`${a.fieldKey}[${Number(i[2])-1}]`,s=i[3];if(s===void 0)return{reason:i[4],target:{key:o,settings:void 0,topLevel:!1}};let c=a.columns.find(e=>(e.label||e.fieldKey)===s);return c?{reason:i[4],target:{key:`${o}.${c.fieldKey}`,settings:c,topLevel:!1}}:null}function k(e,t,n,r=[]){let i=f(t,n).fields;if(r.length>0)return D(r,i);if(e.startsWith(C)){let t=`, ${e.slice(7)}, `,n=i.filter(e=>t.includes(`, ${e.label||e.fieldKey}, `));return n.length>0?Object.fromEntries(n.map(e=>[e.fieldKey,`${e.label||e.fieldKey}為必填欄位。`])):null}let a=e.split(`;`).map(e=>O(e,i));return a.length===0||a.some(e=>e===null)?null:Object.fromEntries(a.flatMap(e=>{if(e===null)return[];let{limit:t,reason:n}=T(e.reason);return[[e.target.key,w(n,t,e.target)]]}))}var A=`未知組織`;function j(e,t){return[...new Set(e.fields.filter(n.isOrgUnitFieldDefinition).map(e=>t[e.fieldKey]).filter(e=>typeof e==`string`&&e.trim().length>0))]}function M(r,i,a,o){if((0,n.isTableFieldDefinition)(r)){let e=(0,t.readFormTableRows)(i),n=r.columns.filter(e=>e.type===`number`||e.type===`money`).map(t=>({label:t.label||t.fieldKey,total:e.reduce((e,n)=>typeof n[t.fieldKey]==`number`&&Number.isFinite(n[t.fieldKey])?e+n[t.fieldKey]:e,0)}));return[`${e.length} 列`,...e.length>0?n.map(({label:e,total:t})=>`${e}合計 ${t.toLocaleString(`zh-TW`)}`):[]].join(`,`)}if((0,e.isFormDataValueEmpty)(i))return`未填寫`;if(typeof i==`boolean`)return i?`是`:`否`;if((0,n.isOrgUnitFieldDefinition)(r))return typeof i==`string`?o.get(i)??`未知組織`:A;let s=Array.isArray(i)?i:[i],c=(0,n.isFormStaticOptionFieldDefinition)(r)?new Map(r.options.map(e=>[e.value,e.label])):r.type===`member`?a:new Map;return s.map(e=>typeof e==`string`?c.get(e)??e:String(e)).join(`、`)}function N(e,t,n,i=new Map,a=new Map){return f(e,t).fields.flatMap(e=>r(n,e.fieldKey)?[{fieldKey:e.fieldKey,label:e.label||e.fieldKey,text:M(e,n[e.fieldKey],i,a)}]:[])}var P=`表單中有附件無法附加到這個案件(只能附加申請人為本案上傳的檔案),請移除後重新上傳再送出。`;function F(e){let r=(0,t.readFormDataSourceErrorMessage)(e);if(r)return r;let i=e instanceof Error?e.message:null;return i?.includes(n.ATTACHMENT_NOT_BINDABLE_ERROR_CODE)?P:i??`發生未知錯誤`}Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,"c",{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,"d",{enumerable:!0,get:function(){return j}}),Object.defineProperty(exports,"f",{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,"h",{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,"l",{enumerable:!0,get:function(){return h}}),Object.defineProperty(exports,"m",{enumerable:!0,get:function(){return N}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return b}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return v}}),Object.defineProperty(exports,"p",{enumerable:!0,get:function(){return k}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,"s",{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return F}}),Object.defineProperty(exports,"u",{enumerable:!0,get:function(){return l}});
2
+ //# sourceMappingURL=instance-error-message-BL_X7Af8.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instance-error-message-BL_X7Af8.cjs","names":["readNodeFillableFieldKeys","readNodeFillUnits","isTableFieldDefinition","omitFormDataKeys","readFormTableRows","readNodeRequiredFieldKeys","readNodeFillGrantFieldKeys","isOrgUnitFieldDefinition","isFormDataValueEmpty","isFormStaticOptionFieldDefinition","readFormDataSourceErrorMessage","ATTACHMENT_NOT_BINDABLE_ERROR_CODE","_rytass_bpm_core_shared","_rytass_bpm_core_client_form","_rytass_bpm_core_shared_form","hasOwn","target","key","readApproverWholeFilledKeys","userTaskNodes","definition","node","readApproverOpenColumnKeys","unit","fieldKey","columnKey","grouped","readApplicantFormSchema","wholeKeys","openColumns","schema","field","open","columns","column","readApplicantSubmission","values","value","rows","row","readFillColumnModeFieldKeys","readFillOpenColumns","readFillColumnOnlyTable","keepClosedColumns","readFillApproverField","readFillValidationSchema","displaySchema","readFillDisplaySchema","approverField","fillableKeys","requiredKeys","columnModeKeys","readFillLabelMarkers","viewerKeys","viewerFillNode","markers","readFillPayload","wholeEntries","columnEntries","readFillColumnModeRows","stored","storedRows","draftRow","draftRows","index","storedRow","openColumnKeys","readFillChanges","seededTables","seeded","requiredBooleanKeys","draft","isFillConflictMessage","message","FILL_TABLE_ROWS_LOCKED_CODE","TOP_LEVEL_REF_PATTERN","TABLE_REF_PATTERN","MISSING_FIELDS_PREFIX","readFillIssueText","name","reason","limit","readFillReasonFromSentence","atLeastRows","sentence","atMostRows","shortest","longest","mostFiles","readFillIssueTarget","fields","path","candidate","rowPath","rowKey","readFillErrorsFromIssues","entries","issues","issue","entry","readFillErrorTarget","topLevel","part","tableRef","table","columnLabel","readFillServerErrors","fillableFields","formDataIssues","listed","missing","parts","UNKNOWN_FILL_ORG_UNIT_TEXT","readFillOrgUnitIds","readFillValueText","totals","sum","label","total","orgUnitNames","items","optionLabels","option","memberNames","item","readFillSummary","ATTACHMENT_NOT_BINDABLE_MESSAGE","readErrorMessage","dataSourceMessage","error"],"sources":["../../src/views/instances/approver-filled-form.ts","../../src/views/instances/instance-error-message.ts"],"sourcesContent":["import {\n isFormDataValueEmpty,\n omitFormDataKeys,\n readNodeFillableFieldKeys,\n readNodeFillGrantFieldKeys,\n readNodeFillUnits,\n readNodeRequiredFieldKeys,\n type FieldAccessFillUnit,\n type UserTaskNode,\n type WorkflowDefinition,\n} from '@rytass/bpm-core-shared';\nimport {\n isFormStaticOptionFieldDefinition,\n isOrgUnitFieldDefinition,\n isTableFieldDefinition,\n type FormDefinitionSchema,\n type FormFieldDefinition,\n type FormFieldValue,\n type FormDataIssue,\n type FormDataIssueReason,\n type FormTableCellValue,\n type FormTableRowValue,\n type TableColumnDefinition,\n} from '@rytass/bpm-core-shared/form';\nimport { readFormTableRows } from '@rytass/bpm-core-client/form';\nimport type { WorkflowFormData } from '@rytass/bpm-core-client/workflow';\n\nconst hasOwn = (target: object, key: string): boolean =>\n Object.prototype.hasOwnProperty.call(target, key);\n\n/** A fill grant on one column: the open cell's fill. */\ntype ColumnFill = FieldAccessFillUnit['fill'];\n\n/**\n * Every top-level field the approver fills as a whole, across all user tasks.\n * A field granted per column only is not here; see `readApproverOpenColumnKeys`.\n */\nfunction readApproverWholeFilledKeys(\n definition: WorkflowDefinition,\n): ReadonlySet<string> {\n return new Set(\n userTaskNodes(definition).flatMap((node) => [\n ...readNodeFillableFieldKeys(node),\n ]),\n );\n}\n\n/**\n * The columns each table opens to some approver, across all user tasks: a\n * table's `fieldKey` to the `columnKey`s an approver fills. Only column units\n * count; whole grants are `readApproverWholeFilledKeys`.\n */\nfunction readApproverOpenColumnKeys(\n definition: WorkflowDefinition,\n): ReadonlyMap<string, ReadonlySet<string>> {\n return userTaskNodes(definition)\n .flatMap((node) => readNodeFillUnits(node))\n .flatMap((unit): readonly (readonly [string, string])[] =>\n unit.columnKey === null ? [] : [[unit.fieldKey, unit.columnKey]],\n )\n .reduce<ReadonlyMap<string, ReadonlySet<string>>>(\n (grouped, [fieldKey, columnKey]) =>\n new Map([\n ...grouped,\n [fieldKey, new Set([...(grouped.get(fieldKey) ?? []), columnKey])],\n ]),\n new Map(),\n );\n}\n\n/**\n * The form as the applicant sees it: without the fields some approver fills in\n * as a whole, and without the columns some approver fills per column. A table\n * keeps its other columns, and is dropped when none remain. The server drops\n * those keys from a submission, so rendering one would swallow what the\n * applicant types, and a `minRows` or `required` on it would block a launch\n * nobody can complete. Never mutates its input.\n */\nexport function readApplicantFormSchema(\n schema: FormDefinitionSchema,\n definition: WorkflowDefinition,\n): FormDefinitionSchema {\n const wholeKeys = readApproverWholeFilledKeys(definition);\n const openColumns = readApproverOpenColumnKeys(definition);\n\n return {\n ...schema,\n fields: schema.fields.flatMap((field): readonly FormFieldDefinition[] => {\n if (wholeKeys.has(field.fieldKey)) {\n return [];\n }\n\n const open = openColumns.get(field.fieldKey);\n\n if (open === undefined) {\n return [field];\n }\n\n if (!isTableFieldDefinition(field)) {\n return [];\n }\n\n const columns = field.columns.filter(\n (column) => !open.has(column.fieldKey),\n );\n\n return columns.length === 0 ? [] : [{ ...field, columns }];\n }),\n };\n}\n\n/**\n * The payload the applicant may send: `values` without the keys an approver\n * fills as a whole, and without the open cells of the tables an approver fills\n * per column. A table left with no applicant cell at all is omitted, so the\n * server keeps the approver's cells as they are.\n *\n * Known limit: the schema is not passed in, so a table whose every column is\n * approver-filled is recognised by its rows, not by its columns. A table that\n * had no rows is omitted for the same reason.\n */\nexport function readApplicantSubmission(\n values: WorkflowFormData,\n definition: WorkflowDefinition,\n): WorkflowFormData {\n const wholeKeys = readApproverWholeFilledKeys(definition);\n const openColumns = readApproverOpenColumnKeys(definition);\n\n return Object.fromEntries(\n Object.entries(omitFormDataKeys(values, wholeKeys)).flatMap(\n ([fieldKey, value]): readonly (readonly [string, unknown])[] => {\n const open = openColumns.get(fieldKey);\n\n if (open === undefined) {\n return [[fieldKey, value]];\n }\n\n // A non-table value reads as no rows, so an approver's non-table\n // per-column field is dropped with the table case.\n const rows = readFormTableRows(value as FormFieldValue).map((row) =>\n omitFormDataKeys(row, open),\n );\n\n return rows.length === 0 ||\n rows.every((row) => Object.keys(row).length === 0)\n ? []\n : [[fieldKey, rows]];\n },\n ),\n ) as WorkflowFormData;\n}\n\n/**\n * Top-level fields this node lets its approver fill only per column (ADR 27\n * §4(a)). A field the node also grants whole is not in this set.\n */\nexport function readFillColumnModeFieldKeys(\n node: UserTaskNode,\n): ReadonlySet<string> {\n const wholeKeys = readNodeFillableFieldKeys(node);\n\n return new Set(\n readNodeFillUnits(node)\n .filter(\n (unit) => unit.columnKey !== null && !wholeKeys.has(unit.fieldKey),\n )\n .map((unit) => unit.fieldKey),\n );\n}\n\n/**\n * The columns of `fieldKey` this node opens to its approver, each with its\n * fill. Empty when the node opens none of them.\n */\nexport function readFillOpenColumns(\n node: UserTaskNode,\n fieldKey: string,\n): ReadonlyMap<string, ColumnFill> {\n return new Map(\n readNodeFillUnits(node).flatMap(\n (unit): readonly (readonly [string, ColumnFill])[] =>\n unit.fieldKey === fieldKey && unit.columnKey !== null\n ? [[unit.columnKey, unit.fill]]\n : [],\n ),\n );\n}\n\n/**\n * The approver's view of a column-only table: its open columns, each required\n * exactly when the node says `REQUIRED`. The row bounds go, since the applicant\n * owns the row count; the closed columns go too, since the approver may not\n * fill them. Other fields, and tables the node opens whole, come back as is.\n */\nfunction readFillColumnOnlyTable(\n field: FormFieldDefinition,\n node: UserTaskNode,\n keepClosedColumns: boolean,\n): FormFieldDefinition | null {\n if (!isTableFieldDefinition(field)) {\n return null;\n }\n\n const open = readFillOpenColumns(node, field.fieldKey);\n const columns = field.columns\n .filter((column) => keepClosedColumns || open.has(column.fieldKey))\n .map((column): TableColumnDefinition => ({\n ...column,\n // The node alone decides; the form's rule for the column is ignored.\n required: open.get(column.fieldKey) === 'REQUIRED',\n requiredWhen: undefined,\n }));\n\n return columns.length === 0\n ? null\n : {\n ...field,\n columns,\n maxRows: undefined,\n minRows: undefined,\n required: false,\n requiredWhen: undefined,\n };\n}\n\n/**\n * The part of `field` the node's approver fills in, or `null` when they fill\n * none of it: a whole fillable field as it is, a column-only table as its open\n * columns. Never mutates its input.\n */\nfunction readFillApproverField(\n field: FormFieldDefinition,\n node: UserTaskNode,\n): FormFieldDefinition | null {\n if (readNodeFillableFieldKeys(node).has(field.fieldKey)) {\n return field;\n }\n\n return readFillColumnModeFieldKeys(node).has(field.fieldKey)\n ? readFillColumnOnlyTable(field, node, false)\n : null;\n}\n\n/**\n * The form an approver's fill is validated against: only what `node` lets them\n * fill in, each whole field required exactly when the node says `REQUIRED`,\n * and each column-only table reduced to its open columns. A required table\n * needs at least one row, the rule `validateFormRendererValues` already\n * applies, so the result agrees with the server's fill check. Never mutates its\n * input.\n */\nexport function readFillValidationSchema(\n schema: FormDefinitionSchema,\n node: UserTaskNode,\n): FormDefinitionSchema {\n const displaySchema = readFillDisplaySchema(schema, node);\n\n // Derived from the display schema so the check and the asterisk read the\n // same rule.\n return {\n ...displaySchema,\n fields: displaySchema.fields.flatMap((field) => {\n const approverField = readFillApproverField(field, node);\n\n return approverField === null ? [] : [approverField];\n }),\n };\n}\n\n/**\n * The schema the fill node's approver sees: every field as the form defines\n * it, except that the node's fillable fields are required exactly when the\n * node says `REQUIRED`, so the asterisk matches what approving will check. A\n * column-only table keeps every column, so the closed ones still show; their\n * requirement is cleared, and the open ones follow the node.\n */\nexport function readFillDisplaySchema(\n schema: FormDefinitionSchema,\n node: UserTaskNode,\n): FormDefinitionSchema {\n const fillableKeys = readNodeFillableFieldKeys(node);\n const requiredKeys = readNodeRequiredFieldKeys(node);\n const columnModeKeys = readFillColumnModeFieldKeys(node);\n\n return {\n ...schema,\n fields: schema.fields.map((field) => {\n if (fillableKeys.has(field.fieldKey)) {\n return {\n ...field,\n required: requiredKeys.has(field.fieldKey),\n // The node alone decides; the server ignores the form's rule.\n requiredWhen: undefined,\n };\n }\n\n return columnModeKeys.has(field.fieldKey)\n ? (readFillColumnOnlyTable(field, node, true) ?? field)\n : field;\n }),\n };\n}\n\nconst userTaskNodes = (\n definition: WorkflowDefinition,\n): readonly UserTaskNode[] =>\n definition.nodes.filter(\n (node): node is UserTaskNode => node.type === 'userTask',\n );\n\n/**\n * The marker shown after each approver-filled field's label: 「本關補填」 on\n * the fields `viewerFillNode` lets the viewer fill, 「<node>補填」 on every\n * other approver-filled field (the first node in the definition that fills\n * it names it).\n */\nexport function readFillLabelMarkers(\n definition: WorkflowDefinition,\n viewerFillNode: UserTaskNode | null,\n): Readonly<Record<string, string>> {\n const viewerKeys =\n viewerFillNode === null\n ? new Set<string>()\n : readNodeFillGrantFieldKeys(viewerFillNode);\n\n return userTaskNodes(definition).reduce<Readonly<Record<string, string>>>(\n (markers, node) => ({\n ...Object.fromEntries(\n [...readNodeFillGrantFieldKeys(node)].map((fieldKey) => [\n fieldKey,\n viewerKeys.has(fieldKey)\n ? '本關補填'\n : `${node.data.label || node.id}補填`,\n ]),\n ),\n ...markers,\n }),\n {},\n );\n}\n\n/**\n * What approving sends as `fill.formData`: the keys of `values` that `node`\n * lets its approver fill, and nothing else (the server refuses any other key).\n * `values` is the fill changes from `readFillChanges`, not the rendered form:\n * a renderer default the approver never touched (a table's empty rows, an\n * OPTIONAL boolean's `false`) is not theirs to sign and stays out. A REQUIRED\n * boolean is the exception; its shown value is sent, see `readFillChanges`.\n */\nexport function readFillPayload(\n values: WorkflowFormData,\n node: UserTaskNode,\n stored: WorkflowFormData = {},\n): Readonly<Record<string, unknown>> {\n const columnModeKeys = readFillColumnModeFieldKeys(node);\n const wholeEntries = [...readNodeFillableFieldKeys(node)]\n .filter((fieldKey) => hasOwn(values, fieldKey))\n // A cleared field reads `undefined`, which JSON drops, and the server\n // would keep the old value; `null` clears it.\n .map((fieldKey): readonly [string, unknown] => [\n fieldKey,\n values[fieldKey] ?? null,\n ]);\n const columnEntries = [...columnModeKeys]\n .filter((fieldKey) => hasOwn(values, fieldKey))\n .map((fieldKey): readonly [string, unknown] => [\n fieldKey,\n readFillColumnModeRows(\n readFormTableRows(stored[fieldKey]),\n readFormTableRows(values[fieldKey]),\n [...readFillOpenColumns(node, fieldKey).keys()],\n ),\n ]);\n\n return Object.fromEntries([...wholeEntries, ...columnEntries]);\n}\n\n/**\n * The rows a column-mode fill sends (ADR 27 §4(a)): one per stored row, in the\n * stored order, each carrying the applicant's cells unchanged and the open\n * cells as the approver left them. `null` when nothing is stored, since the\n * server has no row to settle against. The row count and order never come\n * from the draft.\n */\nfunction readFillColumnModeRows(\n storedRows: readonly FormTableRowValue[],\n draftRows: readonly FormTableRowValue[],\n openColumnKeys: readonly string[],\n): readonly FormTableRowValue[] | null {\n if (storedRows.length === 0) {\n return null;\n }\n\n return storedRows.map((storedRow, index) => {\n const draftRow = draftRows[index] ?? {};\n\n return {\n ...storedRow,\n ...Object.fromEntries(\n openColumnKeys.map(\n (columnKey): readonly [string, FormTableCellValue] => [\n columnKey,\n draftRow[columnKey] ?? null,\n ],\n ),\n ),\n };\n });\n}\n\n/**\n * What the approver's fill amounts to: the fields they changed (`draft`), plus\n * each fill table the page seeds with `minRows` rows that the case does not\n * store yet, and each REQUIRED boolean fill field at the value the page shows.\n * The page shows those rows and values and validates them, so they are sent\n * too. Left out, the server would refuse the approval for a missing row, and\n * for a missing `false` that the approver can see on screen: a boolean that\n * is never sent reads as empty, while `false` is a value. Changed values\n * override the displayed ones.\n */\nexport function readFillChanges({\n draft,\n node,\n schema,\n stored,\n values,\n}: {\n readonly draft: WorkflowFormData;\n readonly node: UserTaskNode;\n readonly schema: FormDefinitionSchema;\n readonly stored: WorkflowFormData;\n readonly values: WorkflowFormData;\n}): WorkflowFormData {\n const fillableKeys = readNodeFillableFieldKeys(node);\n const seededTables = schema.fields.filter((field) => {\n const seeded = values[field.fieldKey];\n\n return (\n isTableFieldDefinition(field) &&\n fillableKeys.has(field.fieldKey) &&\n !Object.prototype.hasOwnProperty.call(stored, field.fieldKey) &&\n Array.isArray(seeded) &&\n seeded.length > 0\n );\n });\n\n const requiredBooleanKeys = [...readNodeRequiredFieldKeys(node)].filter(\n (fieldKey) =>\n fillableKeys.has(fieldKey) && typeof values[fieldKey] === 'boolean',\n );\n\n return {\n ...Object.fromEntries(\n seededTables.map((field) => [field.fieldKey, values[field.fieldKey]]),\n ),\n ...Object.fromEntries(\n requiredBooleanKeys.map((fieldKey) => [fieldKey, values[fieldKey]]),\n ),\n ...draft,\n };\n}\n\n/** The server message of a fill against form data someone else changed. */\nexport function isFillConflictMessage(message: string): boolean {\n return message.includes('form data changed; refresh and retry');\n}\n\n/**\n * The stable code the server answers when an approver's fill adds, removes or\n * reorders rows of a column-only table (ADR 27 §4(a)). Its message is English;\n * callers show their own copy from this code.\n */\nexport const FILL_TABLE_ROWS_LOCKED_CODE =\n 'FORM_FIELD_ACCESS_TABLE_ROWS_LOCKED';\n\n// The server names a field by its label: `欄位「標籤」`, a table row as\n// `「表格」第 N 列` (1-based), and a cell as `「表格」第 N 列的「欄位」`.\n// Each part of a message is separated by `;`. This is the fallback for a\n// response that carries no `formDataIssues`: labels are not unique, so the\n// issues, which address each value by its path, are the primary route.\nconst TOP_LEVEL_REF_PATTERN = /^欄位「(.+?)」(.+)$/;\nconst TABLE_REF_PATTERN = /^「(.+?)」第 (\\d+) 列(?:的「(.+?)」)?(.+)$/;\nconst MISSING_FIELDS_PREFIX = '缺少必填欄位:';\n\n/** The field, or table column, a server error names, with its renderer key. */\ninterface FillErrorTarget {\n readonly key: string;\n readonly settings: FormFieldDefinition | TableColumnDefinition | undefined;\n readonly topLevel: boolean;\n}\n\n/** The sentence a refused value shows, by its reason. */\nfunction readFillIssueText(\n reason: FormDataIssueReason,\n limit: number | undefined,\n target: FillErrorTarget,\n): string {\n const name = target.settings?.label || target.settings?.fieldKey || '';\n\n switch (reason) {\n case 'REQUIRED':\n return target.topLevel && name !== ''\n ? `${name}為必填欄位。`\n : '此欄為必填。';\n case 'MIN_LENGTH':\n return `至少需要 ${limit ?? 0} 個字。`;\n case 'MAX_LENGTH':\n return `最多 ${limit ?? 0} 個字。`;\n case 'MIN_ROWS':\n return `至少需要 ${limit ?? 0} 列。`;\n case 'MAX_ROWS':\n return `最多 ${limit ?? 0} 列。`;\n case 'MAX_FILES':\n return `最多 ${limit ?? 0} 個檔案。`;\n case 'PATTERN': {\n const message =\n target.settings &&\n (target.settings.type === 'text' || target.settings.type === 'textarea')\n ? target.settings.patternErrorMessage?.trim()\n : undefined;\n\n return message || '格式不正確。';\n }\n // The server ran out of time on the format check; the value may be fine.\n case 'PATTERN_TIMEOUT':\n return '格式檢查逾時,請縮短內容後再試,或請表單設計者簡化格式。';\n default:\n return '此欄內容不正確。';\n }\n}\n\n/** The reason a message sentence stands for, for the message fallback. */\nfunction readFillReasonFromSentence(sentence: string): {\n readonly limit?: number;\n readonly reason: FormDataIssueReason;\n} {\n const atLeastRows = /^至少需要 (\\d+) 列$/.exec(sentence);\n const atMostRows = /^最多 (\\d+) 列$/.exec(sentence);\n const shortest = /^至少需要 (\\d+) 個字$/.exec(sentence);\n const longest = /^最多 (\\d+) 個字$/.exec(sentence);\n const mostFiles = /^最多 (\\d+) 個檔案$/.exec(sentence);\n\n if (sentence === '為必填') {\n return { reason: 'REQUIRED' };\n }\n\n if (atLeastRows) {\n return { limit: Number(atLeastRows[1]), reason: 'MIN_ROWS' };\n }\n\n if (atMostRows) {\n return { limit: Number(atMostRows[1]), reason: 'MAX_ROWS' };\n }\n\n if (shortest) {\n return { limit: Number(shortest[1]), reason: 'MIN_LENGTH' };\n }\n\n if (longest) {\n return { limit: Number(longest[1]), reason: 'MAX_LENGTH' };\n }\n\n if (mostFiles) {\n return { limit: Number(mostFiles[1]), reason: 'MAX_FILES' };\n }\n\n if (sentence === '格式不正確') {\n return { reason: 'PATTERN' };\n }\n\n if (sentence === '無法在時限內完成格式檢查') {\n return { reason: 'PATTERN_TIMEOUT' };\n }\n\n return { reason: 'TYPE' };\n}\n\n/**\n * The renderer key of the value an issue path addresses, among the fields\n * `fields` lets the approver fill. `items` is a table, `items[1]` a row,\n * `items[1].qty` a cell. `null` when the path names nothing fillable.\n */\nfunction readFillIssueTarget(\n path: string,\n fields: readonly FormFieldDefinition[],\n): FillErrorTarget | null {\n const field = fields.find(\n (candidate) =>\n path === candidate.fieldKey ||\n (isTableFieldDefinition(candidate) &&\n path.startsWith(`${candidate.fieldKey}[`)),\n );\n\n if (!field) {\n return null;\n }\n\n if (path === field.fieldKey) {\n return { key: path, settings: field, topLevel: true };\n }\n\n if (!isTableFieldDefinition(field)) {\n return null;\n }\n\n const rowPath = /^\\[(\\d+)\\](?:\\.(.+))?$/u.exec(\n path.slice(field.fieldKey.length),\n );\n\n if (!rowPath) {\n return null;\n }\n\n const rowKey = `${field.fieldKey}[${rowPath[1]}]`;\n const columnKey = rowPath[2];\n\n if (columnKey === undefined) {\n return { key: rowKey, settings: undefined, topLevel: false };\n }\n\n const column = field.columns.find(\n (candidate) => candidate.fieldKey === columnKey,\n );\n\n return column\n ? { key: `${rowKey}.${column.fieldKey}`, settings: column, topLevel: false }\n : null;\n}\n\nfunction readFillErrorsFromIssues(\n issues: readonly FormDataIssue[],\n fields: readonly FormFieldDefinition[],\n): Readonly<Record<string, string>> | null {\n const entries = issues.map((issue): readonly [string, string] | null => {\n const target = readFillIssueTarget(issue.path, fields);\n\n return target === null\n ? null\n : [target.key, readFillIssueText(issue.reason, issue.limit, target)];\n });\n\n if (entries.length === 0 || entries.some((entry) => entry === null)) {\n return null;\n }\n\n return Object.fromEntries(\n entries.filter(\n (entry): entry is readonly [string, string] => entry !== null,\n ),\n );\n}\n\nfunction readFillErrorTarget(\n part: string,\n fields: readonly FormFieldDefinition[],\n): { readonly target: FillErrorTarget; readonly reason: string } | null {\n const topLevel = TOP_LEVEL_REF_PATTERN.exec(part);\n\n if (topLevel) {\n const field = fields.find(\n (candidate) => (candidate.label || candidate.fieldKey) === topLevel[1],\n );\n\n return field\n ? {\n reason: topLevel[2],\n target: { key: field.fieldKey, settings: field, topLevel: true },\n }\n : null;\n }\n\n const tableRef = TABLE_REF_PATTERN.exec(part);\n\n if (!tableRef) {\n return null;\n }\n\n const table = fields.find(\n (candidate) =>\n isTableFieldDefinition(candidate) &&\n (candidate.label || candidate.fieldKey) === tableRef[1],\n );\n\n if (!table || !isTableFieldDefinition(table)) {\n return null;\n }\n\n const rowKey = `${table.fieldKey}[${Number(tableRef[2]) - 1}]`;\n const columnLabel = tableRef[3];\n\n if (columnLabel === undefined) {\n return {\n reason: tableRef[4],\n target: { key: rowKey, settings: undefined, topLevel: false },\n };\n }\n\n const column = table.columns.find(\n (candidate) => (candidate.label || candidate.fieldKey) === columnLabel,\n );\n\n return column\n ? {\n reason: tableRef[4],\n target: {\n key: `${rowKey}.${column.fieldKey}`,\n settings: column,\n topLevel: false,\n },\n }\n : null;\n}\n\n/**\n * Turns the server's fill validation refusal (docs/08 §8.5, error table 2)\n * into field errors keyed the way `FormRenderer` addresses them, so a check\n * the page missed still lands on the field. `formDataIssues` is the refusal's\n * structured list and wins when it is present: each issue names its value by\n * path, so two fields sharing a label stay apart. Without it, the Chinese\n * `message` is parsed. `null` when any part does not resolve to a fillable\n * field — the caller then shows the message as it is.\n */\nexport function readFillServerErrors(\n message: string,\n schema: FormDefinitionSchema,\n node: UserTaskNode,\n formDataIssues: readonly FormDataIssue[] = [],\n): Readonly<Record<string, string>> | null {\n const fillableFields = readFillValidationSchema(schema, node).fields;\n\n if (formDataIssues.length > 0) {\n return readFillErrorsFromIssues(formDataIssues, fillableFields);\n }\n\n if (message.startsWith(MISSING_FIELDS_PREFIX)) {\n // Labels may hold commas, so match each fillable field's label (or key)\n // against the list rather than splitting it.\n const listed = `, ${message.slice(MISSING_FIELDS_PREFIX.length)}, `;\n const missing = fillableFields.filter((field) =>\n listed.includes(`, ${field.label || field.fieldKey}, `),\n );\n\n return missing.length > 0\n ? Object.fromEntries(\n missing.map((field) => [\n field.fieldKey,\n `${field.label || field.fieldKey}為必填欄位。`,\n ]),\n )\n : null;\n }\n\n const parts = message\n .split(';')\n .map((part) => readFillErrorTarget(part, fillableFields));\n\n if (parts.length === 0 || parts.some((part) => part === null)) {\n return null;\n }\n\n return Object.fromEntries(\n parts.flatMap((part) => {\n if (part === null) {\n return [];\n }\n\n const { limit, reason } = readFillReasonFromSentence(part.reason);\n\n return [[part.target.key, readFillIssueText(reason, limit, part.target)]];\n }),\n );\n}\n\nexport interface FillSummaryItem {\n readonly fieldKey: string;\n readonly label: string;\n readonly text: string;\n}\n\nexport const UNKNOWN_FILL_ORG_UNIT_TEXT = '未知組織';\n\n/**\n * The org unit ids among `values`, once each and in form order, so the caller\n * can look up their names before building the summary.\n */\nexport function readFillOrgUnitIds(\n schema: FormDefinitionSchema,\n values: WorkflowFormData,\n): readonly string[] {\n return [\n ...new Set(\n schema.fields\n .filter(isOrgUnitFieldDefinition)\n .map((field) => values[field.fieldKey])\n .filter(\n (value): value is string =>\n typeof value === 'string' && value.trim().length > 0,\n ),\n ),\n ];\n}\n\nfunction readFillValueText(\n field: FormFieldDefinition,\n value: FormFieldValue | undefined,\n memberNames: ReadonlyMap<string, string>,\n orgUnitNames: ReadonlyMap<string, string>,\n): string {\n if (isTableFieldDefinition(field)) {\n const rows = readFormTableRows(value);\n const totals = field.columns\n .filter((column) => column.type === 'number' || column.type === 'money')\n .map((column) => ({\n label: column.label || column.fieldKey,\n total: rows.reduce(\n (sum, row) =>\n typeof row[column.fieldKey] === 'number' &&\n Number.isFinite(row[column.fieldKey])\n ? sum + (row[column.fieldKey] as number)\n : sum,\n 0,\n ),\n }));\n\n return [\n `${rows.length} 列`,\n ...(rows.length > 0\n ? totals.map(\n ({ label, total }) =>\n `${label}合計 ${total.toLocaleString('zh-TW')}`,\n )\n : []),\n ].join(',');\n }\n\n if (isFormDataValueEmpty(value)) {\n return '未填寫';\n }\n\n if (typeof value === 'boolean') {\n return value ? '是' : '否';\n }\n\n if (isOrgUnitFieldDefinition(field)) {\n // `orgUnitNames` (`useOrgUnitNames`) maps an id still being looked up to\n // a loading text; an id the lookup did not name is 「未知組織」. Never the\n // raw id.\n return typeof value === 'string'\n ? (orgUnitNames.get(value) ?? UNKNOWN_FILL_ORG_UNIT_TEXT)\n : UNKNOWN_FILL_ORG_UNIT_TEXT;\n }\n\n const items = Array.isArray(value) ? value : [value];\n const optionLabels = isFormStaticOptionFieldDefinition(field)\n ? new Map(field.options.map((option) => [option.value, option.label]))\n : field.type === 'member'\n ? memberNames\n : new Map<string, string>();\n\n return items\n .map((item) =>\n typeof item === 'string'\n ? (optionLabels.get(item) ?? item)\n : String(item),\n )\n .join('、');\n}\n\n/**\n * One line per field `node` lets its approver fill, in form order, for the\n * approve dialog: what will be sent along with the approval. A member field\n * names each member found in `memberNames` and shows the id otherwise; an org\n * unit field names its unit from `orgUnitNames` and shows 「未知組織」 otherwise.\n */\nexport function readFillSummary(\n schema: FormDefinitionSchema,\n node: UserTaskNode,\n values: WorkflowFormData,\n memberNames: ReadonlyMap<string, string> = new Map(),\n orgUnitNames: ReadonlyMap<string, string> = new Map(),\n): readonly FillSummaryItem[] {\n return readFillValidationSchema(schema, node).fields.flatMap(\n (field): readonly FillSummaryItem[] =>\n hasOwn(values, field.fieldKey)\n ? [\n {\n fieldKey: field.fieldKey,\n label: field.label || field.fieldKey,\n text: readFillValueText(\n field,\n values[field.fieldKey],\n memberNames,\n orgUnitNames,\n ),\n },\n ]\n : [],\n );\n}\n","import { readFormDataSourceErrorMessage } from '@rytass/bpm-core-client/form';\nimport { ATTACHMENT_NOT_BINDABLE_ERROR_CODE } from '@rytass/bpm-core-shared/form';\n\nconst ATTACHMENT_NOT_BINDABLE_MESSAGE =\n '表單中有附件無法附加到這個案件(只能附加申請人為本案上傳的檔案),請移除後重新上傳再送出。';\n\n/**\n * The message a case page shows for a failed request: a stable server code\n * translated into words, otherwise the server's own message.\n */\nexport function readErrorMessage(error: unknown): string {\n // DataSource failures arrive as stable codes so the backend never leaks\n // upstream transport detail; translate them instead of showing the raw code.\n const dataSourceMessage = readFormDataSourceErrorMessage(error);\n\n if (dataSourceMessage) {\n return dataSourceMessage;\n }\n\n const message = error instanceof Error ? error.message : null;\n\n // Names the problem, not the attachment ids the server lists.\n if (message?.includes(ATTACHMENT_NOT_BINDABLE_ERROR_CODE)) {\n return ATTACHMENT_NOT_BINDABLE_MESSAGE;\n }\n\n return message ?? '發生未知錯誤';\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../../../chunks/detail-BUfsSal0.cjs");let t=require("react/jsx-runtime");var n={title:`案件詳情 | BPM Admin`};async function r({params:n}){let{id:r}=await n;return(0,t.jsx)(e.t,{instanceId:r})}exports.default=r,exports.metadata=n;
1
+ Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../../../chunks/detail-Dqu0K9QM.cjs");let t=require("react/jsx-runtime");var n={title:`案件詳情 | BPM Admin`};async function r({params:n}){let{id:r}=await n;return(0,t.jsx)(e.t,{instanceId:r})}exports.default=r,exports.metadata=n;
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1,4 +1,4 @@
1
- import { t as e } from "../../../chunks/detail-CHZDNNM6.js";
1
+ import { t as e } from "../../../chunks/detail-fW0vJudE.js";
2
2
  import { jsx as t } from "react/jsx-runtime";
3
3
  // src/pages/instances/detail/index.tsx
4
4
  var n = { title: "案件詳情 | BPM Admin" };
@@ -1,2 +1,2 @@
1
- Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../../../chunks/compose-DP1N4twA.cjs");let t=require("react/jsx-runtime"),n=require("next/server");var r={title:`建立模板(表單 + 流程) | BPM Admin`,description:`以精靈式流程一次完成 BPM 表單與簽核流程設計並發佈。`};async function i(){return await(0,n.connection)(),(0,t.jsx)(e.t,{aiAssistantAvailable:!!process.env.OPENAI_API_KEY,showAiAssistant:process.env.BPM_AI_ASSISTANT_ENABLED===`true`})}exports.default=i,exports.metadata=r;
1
+ Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../../../chunks/compose-CdKVQfjB.cjs");let t=require("react/jsx-runtime"),n=require("next/server");var r={title:`建立模板(表單 + 流程) | BPM Admin`,description:`以精靈式流程一次完成 BPM 表單與簽核流程設計並發佈。`};async function i(){return await(0,n.connection)(),(0,t.jsx)(e.t,{aiAssistantAvailable:!!process.env.OPENAI_API_KEY,showAiAssistant:process.env.BPM_AI_ASSISTANT_ENABLED===`true`})}exports.default=i,exports.metadata=r;
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1,4 +1,4 @@
1
- import { t as e } from "../../../chunks/compose-U1zs3Yof.js";
1
+ import { t as e } from "../../../chunks/compose-N9657E5u.js";
2
2
  import { jsx as t } from "react/jsx-runtime";
3
3
  import { connection as n } from "next/server";
4
4
  // src/pages/templates/compose/index.tsx
@@ -1,2 +1,2 @@
1
- Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../../../chunks/designer-CWL-dCQ_.cjs");let t=require("react/jsx-runtime");var n={title:`流程設計器 | BPM Admin`,description:`以視覺化設計器編輯 BPM 簽核流程節點、條件與發佈版本。`};async function r({params:n}){let{id:r}=await n;return(0,t.jsx)(e.t,{aiAssistantAvailable:!!process.env.OPENAI_API_KEY,showAiAssistant:process.env.BPM_AI_ASSISTANT_ENABLED===`true`,showDryRun:process.env.BPM_TEMPLATE_DRY_RUN_ENABLED!==`false`,templateId:r})}exports.default=r,exports.metadata=n;
1
+ Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../../../chunks/designer-JPs7EijM.cjs");let t=require("react/jsx-runtime");var n={title:`流程設計器 | BPM Admin`,description:`以視覺化設計器編輯 BPM 簽核流程節點、條件與發佈版本。`};async function r({params:n}){let{id:r}=await n;return(0,t.jsx)(e.t,{aiAssistantAvailable:!!process.env.OPENAI_API_KEY,showAiAssistant:process.env.BPM_AI_ASSISTANT_ENABLED===`true`,showDryRun:process.env.BPM_TEMPLATE_DRY_RUN_ENABLED!==`false`,templateId:r})}exports.default=r,exports.metadata=n;
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1,4 +1,4 @@
1
- import { t as e } from "../../../chunks/designer-DPG7R_WP.js";
1
+ import { t as e } from "../../../chunks/designer-CKwG_vM5.js";
2
2
  import { jsx as t } from "react/jsx-runtime";
3
3
  // src/pages/templates/designer/index.tsx
4
4
  var n = {
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../../../chunks/FormBuilderView-Bok4Y59D.cjs");exports.FormBuilderView=e.t;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../../../chunks/FormBuilderView-bm8P55hp.cjs");exports.FormBuilderView=e.t;
@@ -1 +1 @@
1
- export { t as FormBuilderView } from "../../../chunks/FormBuilderView-DbfbLZCI.js";
1
+ export { t as FormBuilderView } from "../../../chunks/FormBuilderView-D_bN9gzT.js";
@@ -4,6 +4,22 @@ import { FormRendererValues } from '@rytass/bpm-core-client/form';
4
4
  import { ResolvedFieldAccess } from '@rytass/bpm-core-shared';
5
5
  import { FormDataSourceFieldState, FormRendererDataSourceContext } from './form-data-source-field.cjs';
6
6
  import { FileUploadFieldState, FormRendererAttachment } from './file-upload-field.cjs';
7
+ /**
8
+ * What a viewer may do inside one table field, for approver fill in column mode
9
+ * (ADR 27 §4(a)). Keyed by the table's `fieldKey` in `FormRendererProps.tableAccess`.
10
+ */
11
+ export interface FormRendererTableAccess {
12
+ /**
13
+ * Column `fieldKey`s whose cells the viewer may edit. Every other column is
14
+ * read-only in that table.
15
+ */
16
+ readonly editableColumns: readonly string[];
17
+ /**
18
+ * When true, the row actions and the add-row button are hidden, so the viewer
19
+ * cannot add, remove or reorder rows.
20
+ */
21
+ readonly lockRows: boolean;
22
+ }
7
23
  export interface FormRendererProps {
8
24
  readonly dataSourceContext?: FormRendererDataSourceContext;
9
25
  readonly dataSourceInitialValues?: FormRendererValues;
@@ -24,6 +40,15 @@ export interface FormRendererProps {
24
40
  * map leaves out follows `readonly`; leaving the prop out changes nothing.
25
41
  */
26
42
  readonly fieldAccess?: Readonly<Record<string, ResolvedFieldAccess>>;
43
+ /**
44
+ * Per table field, which columns this viewer may edit and whether it may add
45
+ * or remove rows (ADR 27 §4(a)). A table listed here takes its cell
46
+ * editability from this map instead of `fieldAccess`, so a column-only grant
47
+ * does not lock the whole table; `fieldAccess` still decides visibility. The
48
+ * form's `readonly` prop still locks every cell. Tables left out follow
49
+ * `fieldAccess` and `readonly` as before.
50
+ */
51
+ readonly tableAccess?: Readonly<Record<string, FormRendererTableAccess>>;
27
52
  /**
28
53
  * Short text shown after a field's label (Mezzanine `labelOptionalMarker`),
29
54
  * per top-level field — e.g. 「本關補填」 on the fields an approver fills.
@@ -58,5 +83,5 @@ export interface FormRendererProps {
58
83
  readonly uiSchema: FormUiSchema;
59
84
  readonly value?: FormRendererValues;
60
85
  }
61
- export declare function FormRenderer({ attachments, dataSourceContext, dataSourceInitialValues, emptyText, errors, fieldAccess, fieldLabelMarkers, maxWidth, onChange, onDataSourceStateChange, onDownloadAttachment, onFileUploadStateChange, onUploadAttachment, optionSnapshots, readonly, schema, singleColumn, uiSchema, value, }: FormRendererProps): ReactElement;
86
+ export declare function FormRenderer({ attachments, dataSourceContext, dataSourceInitialValues, emptyText, errors, fieldAccess, fieldLabelMarkers, maxWidth, onChange, onDataSourceStateChange, onDownloadAttachment, onFileUploadStateChange, onUploadAttachment, optionSnapshots, readonly, schema, singleColumn, tableAccess, uiSchema, value, }: FormRendererProps): ReactElement;
62
87
  export { FormRenderer as FormRendererView };