@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.
- package/CHANGELOG.md +79 -0
- package/dist/chunks/{FormBuilderView-DbfbLZCI.js → FormBuilderView-D_bN9gzT.js} +675 -614
- package/dist/chunks/FormBuilderView-D_bN9gzT.js.map +1 -0
- package/dist/chunks/FormBuilderView-bm8P55hp.cjs +4 -0
- package/dist/chunks/FormBuilderView-bm8P55hp.cjs.map +1 -0
- package/dist/chunks/FormRendererView-BlQgJOkI.cjs +6 -0
- package/dist/chunks/FormRendererView-BlQgJOkI.cjs.map +1 -0
- package/dist/chunks/{FormRendererView-CAyPXhPq.js → FormRendererView-CemHvfAT.js} +298 -290
- package/dist/chunks/FormRendererView-CemHvfAT.js.map +1 -0
- package/dist/chunks/compose-CdKVQfjB.cjs +2 -0
- package/dist/chunks/compose-CdKVQfjB.cjs.map +1 -0
- package/dist/chunks/{compose-U1zs3Yof.js → compose-N9657E5u.js} +161 -147
- package/dist/chunks/compose-N9657E5u.js.map +1 -0
- package/dist/chunks/{designer-DPG7R_WP.js → designer-CKwG_vM5.js} +1913 -1715
- package/dist/chunks/designer-CKwG_vM5.js.map +1 -0
- package/dist/chunks/designer-JPs7EijM.cjs +69 -0
- package/dist/chunks/designer-JPs7EijM.cjs.map +1 -0
- package/dist/chunks/detail-Dqu0K9QM.cjs +5 -0
- package/dist/chunks/detail-Dqu0K9QM.cjs.map +1 -0
- package/dist/chunks/{detail-CHZDNNM6.js → detail-fW0vJudE.js} +902 -895
- package/dist/chunks/detail-fW0vJudE.js.map +1 -0
- package/dist/chunks/instance-error-message-BKt07-Bo.js +270 -0
- package/dist/chunks/instance-error-message-BKt07-Bo.js.map +1 -0
- package/dist/chunks/instance-error-message-BL_X7Af8.cjs +2 -0
- package/dist/chunks/instance-error-message-BL_X7Af8.cjs.map +1 -0
- package/dist/pages/instances/detail/index.cjs +1 -1
- package/dist/pages/instances/detail/index.js +1 -1
- package/dist/pages/templates/compose/index.cjs +1 -1
- package/dist/pages/templates/compose/index.js +1 -1
- package/dist/pages/templates/designer/index.cjs +1 -1
- package/dist/pages/templates/designer/index.js +1 -1
- package/dist/views/forms/builder/index.cjs +1 -1
- package/dist/views/forms/builder/index.js +1 -1
- package/dist/views/forms/renderer/FormRendererView.d.cts +26 -1
- package/dist/views/forms/renderer/FormRendererView.d.ts +26 -1
- package/dist/views/forms/renderer/index.cjs +1 -1
- package/dist/views/forms/renderer/index.js +1 -1
- package/dist/views/inbox/index.cjs +1 -1
- package/dist/views/inbox/index.cjs.map +1 -1
- package/dist/views/inbox/index.js +185 -94
- package/dist/views/inbox/index.js.map +1 -1
- package/dist/views/instances/approver-filled-form.d.cts +56 -20
- package/dist/views/instances/approver-filled-form.d.ts +56 -20
- package/dist/views/instances/detail/index.cjs +1 -1
- package/dist/views/instances/detail/index.js +1 -1
- package/dist/views/instances/detail/sections/InstanceFormSection.d.cts +7 -0
- package/dist/views/instances/detail/sections/InstanceFormSection.d.ts +7 -0
- package/dist/views/instances/detail/sections/InstanceTasksSection.d.cts +1 -1
- package/dist/views/instances/detail/sections/InstanceTasksSection.d.ts +1 -1
- package/dist/views/instances/new/index.cjs +1 -1
- package/dist/views/instances/new/index.js +2 -2
- package/dist/views/templates/compose/index.cjs +1 -1
- package/dist/views/templates/compose/index.js +1 -1
- package/dist/views/templates/compose/steps/ComposeReviewStep.d.cts +3 -1
- package/dist/views/templates/compose/steps/ComposeReviewStep.d.ts +3 -1
- package/dist/views/templates/compose/steps/ComposeWorkflowStep.d.cts +4 -1
- package/dist/views/templates/compose/steps/ComposeWorkflowStep.d.ts +4 -1
- package/dist/views/templates/compose/use-template-compose-wizard.d.cts +3 -1
- package/dist/views/templates/compose/use-template-compose-wizard.d.ts +3 -1
- package/dist/views/templates/designer/TemplateDesignerView.d.cts +10 -1
- package/dist/views/templates/designer/TemplateDesignerView.d.ts +10 -1
- package/dist/views/templates/designer/field-access-matrix/FieldAccessMatrixDrawer.d.cts +3 -1
- package/dist/views/templates/designer/field-access-matrix/FieldAccessMatrixDrawer.d.ts +3 -1
- package/dist/views/templates/designer/field-access-matrix/field-access-copy.d.cts +3 -2
- package/dist/views/templates/designer/field-access-matrix/field-access-copy.d.ts +3 -2
- package/dist/views/templates/designer/index.cjs +1 -1
- package/dist/views/templates/designer/index.js +1 -1
- package/dist/views/templates/designer/launch-mode.d.cts +19 -0
- package/dist/views/templates/designer/launch-mode.d.ts +19 -0
- package/dist/views/templates/designer/publish-default-value-check.d.cts +36 -0
- package/dist/views/templates/designer/publish-default-value-check.d.ts +36 -0
- package/package.json +3 -3
- package/dist/chunks/FormBuilderView-Bok4Y59D.cjs +0 -4
- package/dist/chunks/FormBuilderView-Bok4Y59D.cjs.map +0 -1
- package/dist/chunks/FormBuilderView-DbfbLZCI.js.map +0 -1
- package/dist/chunks/FormRendererView-CAyPXhPq.js.map +0 -1
- package/dist/chunks/FormRendererView-iLlHFIUR.cjs +0 -6
- package/dist/chunks/FormRendererView-iLlHFIUR.cjs.map +0 -1
- package/dist/chunks/compose-DP1N4twA.cjs +0 -2
- package/dist/chunks/compose-DP1N4twA.cjs.map +0 -1
- package/dist/chunks/compose-U1zs3Yof.js.map +0 -1
- package/dist/chunks/designer-CWL-dCQ_.cjs +0 -69
- package/dist/chunks/designer-CWL-dCQ_.cjs.map +0 -1
- package/dist/chunks/designer-DPG7R_WP.js.map +0 -1
- package/dist/chunks/detail-BUfsSal0.cjs +0 -5
- package/dist/chunks/detail-BUfsSal0.cjs.map +0 -1
- package/dist/chunks/detail-CHZDNNM6.js.map +0 -1
- package/dist/chunks/instance-error-message-BEM4O_Kj.js +0 -208
- package/dist/chunks/instance-error-message-BEM4O_Kj.js.map +0 -1
- package/dist/chunks/instance-error-message-BpCLCOjp.cjs +0 -2
- package/dist/chunks/instance-error-message-BpCLCOjp.cjs.map +0 -1
|
@@ -4,24 +4,55 @@ import { n as t } from "../../chunks/auth-provider-C2b-5mGt.js";
|
|
|
4
4
|
import { i as n, o as r } from "../../chunks/task-decision-3d17ZvZE.js";
|
|
5
5
|
import { r as i } from "../../chunks/router-adapter-CkxCyzf5.js";
|
|
6
6
|
import { r as a } from "../../chunks/routes-config-BcNaWJWJ.js";
|
|
7
|
-
import {
|
|
8
|
-
import { Badge as
|
|
9
|
-
import { Fragment as
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
12
|
-
import { PlusIcon as
|
|
7
|
+
import { useCallback as o, useEffect as s, useMemo as c, useRef as l, useState as u } from "react";
|
|
8
|
+
import { Badge as d, Button as f, PageHeader as p, Section as m, SectionGroup as ee, Tab as te, TabItem as h, Table as g, Typography as _ } from "@mezzanine-ui/react";
|
|
9
|
+
import { Fragment as ne, jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
10
|
+
import { listApprovalHistoryTasksPage as b, listApprovalInstancesPage as x, listInboxTasksPage as S, listTaskDecisions as C, readApprovalInstance as w, readApprovalInstanceCaseTitle as T } from "@rytass/bpm-core-client/workflow";
|
|
11
|
+
import re from "@mezzanine-ui/react/ContentHeader";
|
|
12
|
+
import { PlusIcon as ie } from "@mezzanine-ui/icons";
|
|
13
13
|
// src/views/inbox/InboxView.tsx
|
|
14
|
-
var
|
|
14
|
+
var E = 100, ae = {
|
|
15
15
|
display: "grid",
|
|
16
16
|
gap: "var(--mzn-spacing-gap-base)",
|
|
17
17
|
marginBottom: "var(--mzn-spacing-padding-vertical-spacious)"
|
|
18
|
+
}, D = [
|
|
19
|
+
10,
|
|
20
|
+
20,
|
|
21
|
+
50
|
|
22
|
+
], O = {
|
|
23
|
+
page: 1,
|
|
24
|
+
pageSize: 20
|
|
18
25
|
};
|
|
19
|
-
function
|
|
20
|
-
let n = i(), r = a(), { member:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
function k() {
|
|
27
|
+
let n = i(), r = a(), { member: o } = t(), l = o?.memberId ?? null, [x, C] = u("pending"), [w, T] = u(null), [E, D] = u(O), [k, F] = u(O), [L, R] = u(O), z = A(l, {
|
|
28
|
+
enabled: !0,
|
|
29
|
+
paging: E,
|
|
30
|
+
setPaging: D
|
|
31
|
+
}, T, async (e, t) => j(await S({
|
|
32
|
+
assigneeMemberId: e,
|
|
33
|
+
...t
|
|
34
|
+
}), oe)), B = A(l, {
|
|
35
|
+
enabled: !0,
|
|
36
|
+
paging: k,
|
|
37
|
+
setPaging: F
|
|
38
|
+
}, T, async (e, t) => j(await b({
|
|
39
|
+
assigneeMemberId: e,
|
|
40
|
+
instanceState: "RUNNING",
|
|
41
|
+
...t
|
|
42
|
+
}), (t) => I(t, e))), H = A(l, {
|
|
43
|
+
enabled: x === "history",
|
|
44
|
+
paging: L,
|
|
45
|
+
setPaging: R
|
|
46
|
+
}, T, async (e, t) => j(await b({
|
|
47
|
+
assigneeMemberId: e,
|
|
48
|
+
...t
|
|
49
|
+
}), (t) => I(t, e))), [W, K] = u([]), [q, J] = u(!1);
|
|
50
|
+
s(() => {
|
|
51
|
+
l ? P(l).then((e) => {
|
|
52
|
+
K(e?.rows ?? []), J(e?.truncated ?? !1);
|
|
53
|
+
}, (e) => T(X(e))) : (K([]), J(!1));
|
|
54
|
+
}, [l]);
|
|
55
|
+
let Y = c(() => [
|
|
25
56
|
{
|
|
26
57
|
dataIndex: "caseTitle",
|
|
27
58
|
key: "caseTitle",
|
|
@@ -42,7 +73,7 @@ function j() {
|
|
|
42
73
|
},
|
|
43
74
|
{
|
|
44
75
|
key: "fill",
|
|
45
|
-
render: (e) => e.hasFillableFields ? /* @__PURE__ */
|
|
76
|
+
render: (e) => e.hasFillableFields ? /* @__PURE__ */ v(d, {
|
|
46
77
|
size: "sub",
|
|
47
78
|
text: "需補填",
|
|
48
79
|
variant: "dot-warning"
|
|
@@ -52,7 +83,7 @@ function j() {
|
|
|
52
83
|
},
|
|
53
84
|
{
|
|
54
85
|
key: "slaDueAt",
|
|
55
|
-
render: (e) => /* @__PURE__ */
|
|
86
|
+
render: (e) => /* @__PURE__ */ v(_, {
|
|
56
87
|
color: e.slaStatusColor,
|
|
57
88
|
component: "span",
|
|
58
89
|
variant: "body",
|
|
@@ -63,7 +94,7 @@ function j() {
|
|
|
63
94
|
},
|
|
64
95
|
{
|
|
65
96
|
key: "createdAt",
|
|
66
|
-
render: (t) => /* @__PURE__ */
|
|
97
|
+
render: (t) => /* @__PURE__ */ v(_, {
|
|
67
98
|
component: "span",
|
|
68
99
|
variant: "body",
|
|
69
100
|
children: e(t.createdAt)
|
|
@@ -71,35 +102,35 @@ function j() {
|
|
|
71
102
|
title: "建立時間",
|
|
72
103
|
width: 220
|
|
73
104
|
}
|
|
74
|
-
], []),
|
|
105
|
+
], []), se = c(() => ({
|
|
75
106
|
render: (e) => [{
|
|
76
107
|
name: "處理",
|
|
77
108
|
onClick: () => n.push(r.caseDetail(e.instanceId))
|
|
78
109
|
}],
|
|
79
110
|
variant: "base-secondary",
|
|
80
111
|
width: 88
|
|
81
|
-
}), [n, r]),
|
|
112
|
+
}), [n, r]), ce = c(() => [{
|
|
82
113
|
dataIndex: "caseTitle",
|
|
83
114
|
key: "caseTitle",
|
|
84
115
|
title: "案件",
|
|
85
116
|
width: 280
|
|
86
117
|
}, {
|
|
87
118
|
key: "startedAt",
|
|
88
|
-
render: (t) => /* @__PURE__ */
|
|
119
|
+
render: (t) => /* @__PURE__ */ v(_, {
|
|
89
120
|
component: "span",
|
|
90
121
|
variant: "body",
|
|
91
122
|
children: e(t.startedAt)
|
|
92
123
|
}),
|
|
93
124
|
title: "發起時間",
|
|
94
125
|
width: 220
|
|
95
|
-
}], []),
|
|
126
|
+
}], []), le = c(() => ({
|
|
96
127
|
render: (e) => [{
|
|
97
128
|
name: "重新送出",
|
|
98
129
|
onClick: () => n.push(r.caseDetail(e.id))
|
|
99
130
|
}],
|
|
100
131
|
variant: "base-secondary",
|
|
101
132
|
width: 120
|
|
102
|
-
}), [n, r]), Z =
|
|
133
|
+
}), [n, r]), Z = c(() => [
|
|
103
134
|
{
|
|
104
135
|
dataIndex: "caseTitle",
|
|
105
136
|
key: "caseTitle",
|
|
@@ -114,7 +145,7 @@ function j() {
|
|
|
114
145
|
},
|
|
115
146
|
{
|
|
116
147
|
key: "decisionLabel",
|
|
117
|
-
render: (e) => /* @__PURE__ */
|
|
148
|
+
render: (e) => /* @__PURE__ */ v(_, {
|
|
118
149
|
color: V(e.decisionAction),
|
|
119
150
|
component: "span",
|
|
120
151
|
variant: "body",
|
|
@@ -125,7 +156,7 @@ function j() {
|
|
|
125
156
|
},
|
|
126
157
|
{
|
|
127
158
|
key: "decisionComment",
|
|
128
|
-
render: (e) => /* @__PURE__ */
|
|
159
|
+
render: (e) => /* @__PURE__ */ v(_, {
|
|
129
160
|
component: "span",
|
|
130
161
|
variant: "body",
|
|
131
162
|
children: e.decisionCommentText
|
|
@@ -135,7 +166,7 @@ function j() {
|
|
|
135
166
|
},
|
|
136
167
|
{
|
|
137
168
|
key: "instanceStateLabel",
|
|
138
|
-
render: (e) => /* @__PURE__ */
|
|
169
|
+
render: (e) => /* @__PURE__ */ v(_, {
|
|
139
170
|
color: U(e.instanceState),
|
|
140
171
|
component: "span",
|
|
141
172
|
variant: "body",
|
|
@@ -146,7 +177,7 @@ function j() {
|
|
|
146
177
|
},
|
|
147
178
|
{
|
|
148
179
|
key: "decidedAt",
|
|
149
|
-
render: (e) => /* @__PURE__ */
|
|
180
|
+
render: (e) => /* @__PURE__ */ v(_, {
|
|
150
181
|
component: "span",
|
|
151
182
|
variant: "body",
|
|
152
183
|
children: G(e.decidedAt)
|
|
@@ -154,143 +185,203 @@ function j() {
|
|
|
154
185
|
title: "簽核時間",
|
|
155
186
|
width: 220
|
|
156
187
|
}
|
|
157
|
-
], []), Q =
|
|
188
|
+
], []), Q = c(() => ({
|
|
158
189
|
render: (e) => [{
|
|
159
190
|
name: "查看",
|
|
160
191
|
onClick: () => n.push(r.caseDetail(e.instanceId))
|
|
161
192
|
}],
|
|
162
193
|
variant: "base-secondary",
|
|
163
194
|
width: 88
|
|
164
|
-
}), [n, r])
|
|
165
|
-
function
|
|
166
|
-
|
|
195
|
+
}), [n, r]);
|
|
196
|
+
function $(e) {
|
|
197
|
+
C(e === "history" || e === "pending" || e === "tracking" ? e : "pending");
|
|
167
198
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
try {
|
|
171
|
-
if (!w) {
|
|
172
|
-
H([]), L([]), K([]), J(!1);
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
let [e, t, n] = await Promise.all([
|
|
176
|
-
C(w),
|
|
177
|
-
x(w),
|
|
178
|
-
N(w)
|
|
179
|
-
]), [r, i] = await Promise.all([F(e), I(t, w)]);
|
|
180
|
-
H(r), L(i), K(n?.rows ?? []), J(n?.truncated ?? !1);
|
|
181
|
-
} catch (e) {
|
|
182
|
-
j(X(e));
|
|
183
|
-
} finally {
|
|
184
|
-
z(!1);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
return /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ y(d, { children: /* @__PURE__ */ y(D, {
|
|
188
|
-
description: `目前以 ${S?.name ?? w ?? "目前登入會員"} 查詢待處理與歷史簽核任務。`,
|
|
199
|
+
return /* @__PURE__ */ y(ne, { children: [/* @__PURE__ */ v(p, { children: /* @__PURE__ */ v(re, {
|
|
200
|
+
description: `目前以 ${o?.name ?? l ?? "目前登入會員"} 查詢待處理與歷史簽核任務。`,
|
|
189
201
|
title: "我的待簽",
|
|
190
|
-
children: /* @__PURE__ */
|
|
191
|
-
icon:
|
|
202
|
+
children: /* @__PURE__ */ v(f, {
|
|
203
|
+
icon: ie,
|
|
192
204
|
iconType: "leading",
|
|
193
205
|
onClick: () => n.push(r.caseNew()),
|
|
194
206
|
variant: "base-primary",
|
|
195
207
|
children: "發起簽核"
|
|
196
208
|
})
|
|
197
|
-
}) }), /* @__PURE__ */
|
|
198
|
-
|
|
209
|
+
}) }), /* @__PURE__ */ v(ee, { children: /* @__PURE__ */ y(m, { children: [
|
|
210
|
+
w ? /* @__PURE__ */ v(_, {
|
|
199
211
|
color: "text-error",
|
|
200
212
|
variant: "body",
|
|
201
|
-
children:
|
|
213
|
+
children: w
|
|
202
214
|
}) : null,
|
|
203
|
-
/* @__PURE__ */
|
|
204
|
-
activeKey:
|
|
205
|
-
onChange:
|
|
215
|
+
/* @__PURE__ */ y(te, {
|
|
216
|
+
activeKey: x,
|
|
217
|
+
onChange: $,
|
|
206
218
|
size: "sub",
|
|
207
219
|
children: [
|
|
208
|
-
/* @__PURE__ */
|
|
209
|
-
badgeCount:
|
|
220
|
+
/* @__PURE__ */ v(h, {
|
|
221
|
+
badgeCount: z.loading ? void 0 : z.total,
|
|
210
222
|
children: "待簽核"
|
|
211
223
|
}, "pending"),
|
|
212
|
-
/* @__PURE__ */
|
|
213
|
-
badgeCount:
|
|
224
|
+
/* @__PURE__ */ v(h, {
|
|
225
|
+
badgeCount: B.loading ? void 0 : B.total,
|
|
214
226
|
children: "已處理未結束"
|
|
215
227
|
}, "tracking"),
|
|
216
|
-
/* @__PURE__ */
|
|
228
|
+
/* @__PURE__ */ v(h, { children: "歷史簽核記錄" }, "history")
|
|
217
229
|
]
|
|
218
230
|
}),
|
|
219
|
-
|
|
231
|
+
x === "pending" && (W.length > 0 || q) ? /* @__PURE__ */ y("div", {
|
|
220
232
|
"data-returned-instances": "",
|
|
221
|
-
style:
|
|
233
|
+
style: ae,
|
|
222
234
|
children: [
|
|
223
|
-
W.length > 0 ? /* @__PURE__ */
|
|
235
|
+
W.length > 0 ? /* @__PURE__ */ v(_, {
|
|
224
236
|
component: "h2",
|
|
225
237
|
variant: "label-primary",
|
|
226
238
|
children: "被退回待重送"
|
|
227
239
|
}) : null,
|
|
228
|
-
/* @__PURE__ */
|
|
240
|
+
/* @__PURE__ */ v(_, {
|
|
229
241
|
color: "text-neutral",
|
|
230
242
|
variant: "caption",
|
|
231
|
-
children:
|
|
243
|
+
children: N(W.length > 0, q)
|
|
232
244
|
}),
|
|
233
|
-
W.length > 0 ? /* @__PURE__ */
|
|
234
|
-
actions:
|
|
235
|
-
columns:
|
|
245
|
+
W.length > 0 ? /* @__PURE__ */ v(g, {
|
|
246
|
+
actions: le,
|
|
247
|
+
columns: ce,
|
|
236
248
|
dataSource: [...W],
|
|
237
249
|
fullWidth: !0
|
|
238
250
|
}) : null
|
|
239
251
|
]
|
|
240
252
|
}) : null,
|
|
241
|
-
|
|
242
|
-
actions:
|
|
253
|
+
x === "pending" ? /* @__PURE__ */ v(g, {
|
|
254
|
+
actions: se,
|
|
243
255
|
columns: Y,
|
|
244
|
-
dataSource:
|
|
256
|
+
dataSource: [...z.rows],
|
|
245
257
|
fullWidth: !0,
|
|
246
|
-
loading:
|
|
258
|
+
loading: z.loading,
|
|
259
|
+
pagination: M(E, z.total, D)
|
|
247
260
|
}) : null,
|
|
248
|
-
|
|
261
|
+
x === "tracking" ? /* @__PURE__ */ v(g, {
|
|
249
262
|
actions: Q,
|
|
250
263
|
columns: Z,
|
|
251
|
-
dataSource:
|
|
264
|
+
dataSource: [...B.rows],
|
|
252
265
|
fullWidth: !0,
|
|
253
|
-
loading:
|
|
266
|
+
loading: B.loading,
|
|
267
|
+
pagination: M(k, B.total, F)
|
|
254
268
|
}) : null,
|
|
255
|
-
|
|
269
|
+
x === "history" ? /* @__PURE__ */ v(g, {
|
|
256
270
|
actions: Q,
|
|
257
271
|
columns: Z,
|
|
258
|
-
dataSource:
|
|
272
|
+
dataSource: [...H.rows],
|
|
259
273
|
fullWidth: !0,
|
|
260
|
-
loading:
|
|
274
|
+
loading: H.loading,
|
|
275
|
+
pagination: M(L, H.total, R)
|
|
261
276
|
}) : null
|
|
262
277
|
] }) })] });
|
|
263
278
|
}
|
|
264
|
-
function
|
|
265
|
-
|
|
279
|
+
function A(e, { enabled: t, paging: n, setPaging: r }, i, a) {
|
|
280
|
+
let [c, d] = u({
|
|
281
|
+
loading: !0,
|
|
282
|
+
rows: [],
|
|
283
|
+
total: 0
|
|
284
|
+
}), f = l(0), p = l(a);
|
|
285
|
+
p.current = a;
|
|
286
|
+
let m = o(async () => {
|
|
287
|
+
let t = f.current + 1;
|
|
288
|
+
if (f.current = t, !e) d({
|
|
289
|
+
loading: !1,
|
|
290
|
+
rows: [],
|
|
291
|
+
total: 0
|
|
292
|
+
});
|
|
293
|
+
else {
|
|
294
|
+
d((e) => ({
|
|
295
|
+
...e,
|
|
296
|
+
loading: !0
|
|
297
|
+
}));
|
|
298
|
+
try {
|
|
299
|
+
let i = await p.current(e, n);
|
|
300
|
+
if (f.current !== t) return;
|
|
301
|
+
let a = Math.max(1, Math.ceil(i.total / n.pageSize));
|
|
302
|
+
if (!i.rows.length && n.page > a) {
|
|
303
|
+
r({
|
|
304
|
+
...n,
|
|
305
|
+
page: a
|
|
306
|
+
});
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
d({
|
|
310
|
+
loading: !1,
|
|
311
|
+
...i
|
|
312
|
+
});
|
|
313
|
+
} catch (e) {
|
|
314
|
+
f.current === t && (i(X(e)), d((e) => ({
|
|
315
|
+
...e,
|
|
316
|
+
loading: !1
|
|
317
|
+
})));
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}, [
|
|
321
|
+
e,
|
|
322
|
+
i,
|
|
323
|
+
n,
|
|
324
|
+
r
|
|
325
|
+
]);
|
|
326
|
+
return s(() => {
|
|
327
|
+
t && m();
|
|
328
|
+
}, [t, m]), c;
|
|
329
|
+
}
|
|
330
|
+
async function j(e, t) {
|
|
331
|
+
return {
|
|
332
|
+
rows: await t(e.tasks),
|
|
333
|
+
total: e.totalCount
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
function M(e, t, n) {
|
|
337
|
+
return {
|
|
338
|
+
current: e.page,
|
|
339
|
+
onChange: (t) => n({
|
|
340
|
+
...e,
|
|
341
|
+
page: t
|
|
342
|
+
}),
|
|
343
|
+
onChangePageSize: (e) => n({
|
|
344
|
+
page: 1,
|
|
345
|
+
pageSize: e
|
|
346
|
+
}),
|
|
347
|
+
pageSize: e.pageSize,
|
|
348
|
+
pageSizeLabel: "每頁筆數",
|
|
349
|
+
pageSizeOptions: D,
|
|
350
|
+
renderResultSummary: (e, t, n) => `顯示 ${e}-${t} 筆,共 ${n} 筆`,
|
|
351
|
+
showPageSizeOptions: !0,
|
|
352
|
+
total: t
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
function N(e, t) {
|
|
356
|
+
return e ? t ? "你發起的案件被退回,請修改後重新送出。可能還有其他被退回的案件,請到「我發起的」查看。" : "你發起的案件被退回,請修改後重新送出。" : `前 ${E} 筆被退回的案件中沒有你發起的,可能還有其他被退回的案件,請到「我發起的」查看。`;
|
|
266
357
|
}
|
|
267
|
-
async function
|
|
358
|
+
async function P(e) {
|
|
268
359
|
try {
|
|
269
|
-
let t = await
|
|
360
|
+
let t = await x({
|
|
270
361
|
page: 1,
|
|
271
|
-
pageSize:
|
|
362
|
+
pageSize: E,
|
|
272
363
|
searchText: null,
|
|
273
364
|
state: "RETURNED",
|
|
274
365
|
templateId: null,
|
|
275
366
|
view: "SENT"
|
|
276
367
|
});
|
|
277
368
|
return {
|
|
278
|
-
rows:
|
|
369
|
+
rows: F(t.instances, e),
|
|
279
370
|
truncated: t.totalCount > t.instances.length
|
|
280
371
|
};
|
|
281
372
|
} catch {
|
|
282
373
|
return null;
|
|
283
374
|
}
|
|
284
375
|
}
|
|
285
|
-
function
|
|
376
|
+
function F(e, t) {
|
|
286
377
|
return e.filter((e) => e.initiatorMemberId === t).map((e) => ({
|
|
287
|
-
caseTitle:
|
|
378
|
+
caseTitle: T(e),
|
|
288
379
|
id: e.id,
|
|
289
380
|
key: e.id,
|
|
290
381
|
startedAt: e.startedAt
|
|
291
382
|
}));
|
|
292
383
|
}
|
|
293
|
-
async function
|
|
384
|
+
async function oe(e) {
|
|
294
385
|
let t = await L(e);
|
|
295
386
|
return e.map((e, n) => ({
|
|
296
387
|
...e,
|
|
@@ -303,7 +394,7 @@ async function F(e) {
|
|
|
303
394
|
}));
|
|
304
395
|
}
|
|
305
396
|
async function I(e, t) {
|
|
306
|
-
let [n, i] = await Promise.all([Promise.all(e.map((e) =>
|
|
397
|
+
let [n, i] = await Promise.all([Promise.all(e.map((e) => C(e.id))), L(e)]);
|
|
307
398
|
return e.map((e, a) => {
|
|
308
399
|
let o = n[a] ?? [], s = z(o, t), c = i[a] ?? null;
|
|
309
400
|
return {
|
|
@@ -324,14 +415,14 @@ async function I(e, t) {
|
|
|
324
415
|
async function L(e) {
|
|
325
416
|
return Promise.all(e.map(async (e) => {
|
|
326
417
|
try {
|
|
327
|
-
return (await
|
|
418
|
+
return (await w(e.instanceId)).instance;
|
|
328
419
|
} catch {
|
|
329
420
|
return null;
|
|
330
421
|
}
|
|
331
422
|
}));
|
|
332
423
|
}
|
|
333
424
|
function R(e, t) {
|
|
334
|
-
return t ?
|
|
425
|
+
return t ? T(t) : e.instanceId;
|
|
335
426
|
}
|
|
336
427
|
function z(e, t) {
|
|
337
428
|
return n(e.filter((e) => e.decidedByMemberId === t));
|
|
@@ -372,6 +463,6 @@ function Y(e) {
|
|
|
372
463
|
function X(e) {
|
|
373
464
|
return e instanceof Error ? e.message : "發生未知錯誤";
|
|
374
465
|
}
|
|
375
|
-
export {
|
|
466
|
+
export { k as InboxView };
|
|
376
467
|
|
|
377
468
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["_Fragment","formatDateTime","useAuth","readLatestTaskDecision","readTaskNodeText","useRouterAdapter","useBPMRoutes","Fragment$1","ContentHeader","RETURNED_INSTANCE_PAGE_SIZE","RETURNED_SECTION_STYLE","InboxView","router","routes","currentMemberId","member","activeTab","setActiveTab","useState","error","setError","historyRows","setHistoryRows","loading","setLoading","pendingRows","setPendingRows","returnedRows","setReturnedRows","returnedTruncated","setReturnedTruncated","refreshTasks","pendingColumns","useMemo","record","Badge","Typography","pendingTableActions","returnedColumns","returnedTableActions","historyColumns","readTaskDecisionColor","readInstanceStateColor","formatHistoryDateTime","historyTableActions","trackingRows","row","handleTabChange","activeKey","nextPendingTasks","nextHistoryTasks","returned","listInboxTasks","listApprovalHistoryTasks","readReturnedInstances","nextPendingRows","nextHistoryRows","readInboxTaskRows","readApprovalHistoryRows","requestError","readErrorMessage","PageHeader","Button","PlusIcon","SectionGroup","Section","Tab","TabItem","readReturnedSectionHint","Table","hasRows","truncated","page","listApprovalInstancesPage","readReturnedInstanceRows","instances","instance","readApprovalInstanceCaseTitle","readTaskInstances","tasks","task","readTaskCaseTitle","index","readSlaStatusColor","readSlaStatusText","readTaskStatusLabel","decisionLists","listTaskDecisions","decisions","decision","readLatestOwnTaskDecision","readDecisionCommentText","readTaskDecisionLabel","readInstanceStateLabel","readApprovalInstance","action","state","comment","value","dueAt","now","label","formatDuration","totalMinutes","days","hours","minutes","status"],"sources":["../../../src/views/inbox/InboxView.tsx"],"sourcesContent":["'use client';\n\nimport {\n type CSSProperties,\n type Key,\n ReactElement,\n useEffect,\n useMemo,\n useState,\n} from 'react';\nimport {\n Badge,\n Button,\n PageHeader,\n Section,\n SectionGroup,\n Tab,\n TabItem,\n Table,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { PlusIcon } from '@mezzanine-ui/icons';\nimport type { TableActions, TableColumn } from '@mezzanine-ui/core/table';\nimport {\n ApprovalInstanceRecord,\n type ApprovalInstancesPageResult,\n listApprovalHistoryTasks,\n listApprovalInstancesPage,\n listInboxTasks,\n listTaskDecisions,\n readApprovalInstance,\n readApprovalInstanceCaseTitle,\n TaskDecisionRecord,\n TaskRecord,\n} from '@rytass/bpm-core-client/workflow';\nimport { formatDateTime } from '../../lib/format-date-time';\nimport { useAuth } from '../../lib/auth-provider';\nimport { readTaskNodeText } from '../../lib/task-node-label';\nimport { readLatestTaskDecision } from '../../lib/task-decision';\nimport { useRouterAdapter } from '../../lib/router-adapter';\nimport { useBPMRoutes } from '../../lib/routes-config';\n\ntype InboxTabKey = 'history' | 'pending' | 'tracking';\n\ntype InboxTaskRow = Readonly<\n Record<string, unknown> &\n TaskRecord & {\n caseTitle: string;\n key: string;\n nodeText: string;\n slaStatusColor: 'text-error' | 'text-neutral' | 'text-success';\n slaStatusText: string;\n statusLabel: string;\n }\n>;\n\ntype ReturnedInstanceRow = Readonly<{\n caseTitle: string;\n id: string;\n key: string;\n startedAt: string | null;\n}>;\n\n/** The server caps a page at 100; more than that is left to 我發起的. */\nconst RETURNED_INSTANCE_PAGE_SIZE = 100;\n\nconst RETURNED_SECTION_STYLE: CSSProperties = {\n display: 'grid',\n gap: 'var(--mzn-spacing-gap-base)',\n marginBottom: 'var(--mzn-spacing-padding-vertical-spacious)',\n};\n\ntype ApprovalHistoryRow = Readonly<\n Record<string, unknown> &\n TaskRecord & {\n caseTitle: string;\n decisionAction: TaskDecisionRecord['action'] | null;\n decisionComment: string | null;\n decisionCommentText: string;\n decisionLabel: string;\n decidedAt: string | null;\n instanceState: ApprovalInstanceRecord['state'] | null;\n instanceStateLabel: string;\n key: string;\n nodeText: string;\n }\n>;\n\n\n/**\n * Framework-agnostic view for the BPM \"我的待簽\" inbox. Mechanical port of\n * `apps/client/src/app/inbox/page.tsx` — renders pending tasks, tracking,\n * and history tabs.\n */\nexport function InboxView(): ReactElement {\n const router = useRouterAdapter();\n const routes = useBPMRoutes();\n const { member } = useAuth();\n const currentMemberId = member?.memberId ?? null;\n const [activeTab, setActiveTab] = useState<InboxTabKey>('pending');\n const [error, setError] = useState<string | null>(null);\n const [historyRows, setHistoryRows] = useState<ApprovalHistoryRow[]>([]);\n const [loading, setLoading] = useState(true);\n const [pendingRows, setPendingRows] = useState<InboxTaskRow[]>([]);\n const [returnedRows, setReturnedRows] = useState<\n readonly ReturnedInstanceRow[]\n >([]);\n const [returnedTruncated, setReturnedTruncated] = useState(false);\n\n useEffect((): void => {\n if (!currentMemberId) {\n return;\n }\n\n void refreshTasks();\n }, [currentMemberId]);\n\n const pendingColumns = useMemo(\n (): TableColumn<InboxTaskRow>[] => [\n { dataIndex: 'caseTitle', key: 'caseTitle', title: '案件', width: 280 },\n { dataIndex: 'nodeText', key: 'nodeText', title: '節點', width: 180 },\n {\n dataIndex: 'statusLabel',\n key: 'statusLabel',\n title: '狀態',\n width: 120,\n },\n {\n key: 'fill',\n // Approving one of these needs the case page, where the fill is entered.\n render: (record: InboxTaskRow): ReactElement | null =>\n record.hasFillableFields ? (\n <Badge size=\"sub\" text=\"需補填\" variant=\"dot-warning\" />\n ) : null,\n title: '補填',\n width: 120,\n },\n {\n key: 'slaDueAt',\n render: (record: InboxTaskRow): ReactElement => (\n <Typography\n color={record.slaStatusColor}\n component=\"span\"\n variant=\"body\"\n >\n {record.slaStatusText}\n </Typography>\n ),\n title: 'SLA',\n width: 180,\n },\n {\n key: 'createdAt',\n render: (record: InboxTaskRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {formatDateTime(record.createdAt)}\n </Typography>\n ),\n title: '建立時間',\n width: 220,\n },\n ],\n [],\n );\n const pendingTableActions = useMemo(\n (): TableActions<InboxTaskRow> => ({\n render: (record): ReturnType<TableActions<InboxTaskRow>['render']> => [\n {\n name: '處理',\n onClick: (): void => router.push(routes.caseDetail(record.instanceId)),\n },\n ],\n variant: 'base-secondary',\n width: 88,\n }),\n [router, routes],\n );\n const returnedColumns = useMemo(\n (): TableColumn<ReturnedInstanceRow>[] => [\n { dataIndex: 'caseTitle', key: 'caseTitle', title: '案件', width: 280 },\n {\n key: 'startedAt',\n render: (record: ReturnedInstanceRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {formatDateTime(record.startedAt)}\n </Typography>\n ),\n title: '發起時間',\n width: 220,\n },\n ],\n [],\n );\n const returnedTableActions = useMemo(\n (): TableActions<ReturnedInstanceRow> => ({\n render: (\n record,\n ): ReturnType<TableActions<ReturnedInstanceRow>['render']> => [\n {\n name: '重新送出',\n onClick: (): void => router.push(routes.caseDetail(record.id)),\n },\n ],\n variant: 'base-secondary',\n width: 120,\n }),\n [router, routes],\n );\n const historyColumns = useMemo(\n (): TableColumn<ApprovalHistoryRow>[] => [\n { dataIndex: 'caseTitle', key: 'caseTitle', title: '案件', width: 280 },\n { dataIndex: 'nodeText', key: 'nodeText', title: '節點', width: 180 },\n {\n key: 'decisionLabel',\n render: (record: ApprovalHistoryRow): ReactElement => (\n <Typography\n color={readTaskDecisionColor(record.decisionAction)}\n component=\"span\"\n variant=\"body\"\n >\n {record.decisionLabel}\n </Typography>\n ),\n title: '決議',\n width: 120,\n },\n {\n key: 'decisionComment',\n render: (record: ApprovalHistoryRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {record.decisionCommentText}\n </Typography>\n ),\n title: '意見',\n width: 240,\n },\n {\n key: 'instanceStateLabel',\n render: (record: ApprovalHistoryRow): ReactElement => (\n <Typography\n color={readInstanceStateColor(record.instanceState)}\n component=\"span\"\n variant=\"body\"\n >\n {record.instanceStateLabel}\n </Typography>\n ),\n title: '流程狀態',\n width: 140,\n },\n {\n key: 'decidedAt',\n render: (record: ApprovalHistoryRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {formatHistoryDateTime(record.decidedAt)}\n </Typography>\n ),\n title: '簽核時間',\n width: 220,\n },\n ],\n [],\n );\n const historyTableActions = useMemo(\n (): TableActions<ApprovalHistoryRow> => ({\n render: (\n record,\n ): ReturnType<TableActions<ApprovalHistoryRow>['render']> => [\n {\n name: '查看',\n onClick: (): void => router.push(routes.caseDetail(record.instanceId)),\n },\n ],\n variant: 'base-secondary',\n width: 88,\n }),\n [router, routes],\n );\n const trackingRows = useMemo(\n (): ApprovalHistoryRow[] =>\n historyRows.filter((row) => row.instanceState === 'RUNNING'),\n [historyRows],\n );\n\n function handleTabChange(activeKey: Key): void {\n if (\n activeKey === 'history' ||\n activeKey === 'pending' ||\n activeKey === 'tracking'\n ) {\n setActiveTab(activeKey);\n return;\n }\n\n setActiveTab('pending');\n }\n\n async function refreshTasks(): Promise<void> {\n setLoading(true);\n setError(null);\n\n try {\n if (!currentMemberId) {\n setPendingRows([]);\n setHistoryRows([]);\n setReturnedRows([]);\n setReturnedTruncated(false);\n return;\n }\n\n const [nextPendingTasks, nextHistoryTasks, returned] =\n await Promise.all([\n listInboxTasks(currentMemberId),\n listApprovalHistoryTasks(currentMemberId),\n readReturnedInstances(currentMemberId),\n ]);\n const [nextPendingRows, nextHistoryRows] = await Promise.all([\n readInboxTaskRows(nextPendingTasks),\n readApprovalHistoryRows(nextHistoryTasks, currentMemberId),\n ]);\n\n setPendingRows(nextPendingRows);\n setHistoryRows(nextHistoryRows);\n setReturnedRows(returned?.rows ?? []);\n setReturnedTruncated(returned?.truncated ?? false);\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setLoading(false);\n }\n }\n\n return (\n <>\n <PageHeader>\n <ContentHeader\n description={`目前以 ${member?.name ?? currentMemberId ?? '目前登入會員'} 查詢待處理與歷史簽核任務。`}\n title=\"我的待簽\"\n >\n <Button\n icon={PlusIcon}\n iconType=\"leading\"\n onClick={(): void => router.push(routes.caseNew())}\n variant=\"base-primary\"\n >\n 發起簽核\n </Button>\n </ContentHeader>\n </PageHeader>\n\n <SectionGroup>\n <Section>\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n <Tab activeKey={activeTab} onChange={handleTabChange} size=\"sub\">\n <TabItem\n badgeCount={loading ? undefined : pendingRows.length}\n key=\"pending\"\n >\n 待簽核\n </TabItem>\n <TabItem\n badgeCount={loading ? undefined : trackingRows.length}\n key=\"tracking\"\n >\n 已處理未結束\n </TabItem>\n <TabItem key=\"history\">歷史簽核記錄</TabItem>\n </Tab>\n\n {/* A truncated page with none of the member's own cases still\n points to 我發起的: theirs may sit past the first page. */}\n {activeTab === 'pending' &&\n (returnedRows.length > 0 || returnedTruncated) ? (\n <div data-returned-instances=\"\" style={RETURNED_SECTION_STYLE}>\n {/* Without rows there is nothing here to resubmit, only the\n pointer to 我發起的. */}\n {returnedRows.length > 0 ? (\n <Typography component=\"h2\" variant=\"label-primary\">\n 被退回待重送\n </Typography>\n ) : null}\n <Typography color=\"text-neutral\" variant=\"caption\">\n {readReturnedSectionHint(\n returnedRows.length > 0,\n returnedTruncated,\n )}\n </Typography>\n {returnedRows.length > 0 ? (\n <Table\n actions={returnedTableActions}\n columns={returnedColumns}\n dataSource={[...returnedRows]}\n fullWidth\n />\n ) : null}\n </div>\n ) : null}\n {activeTab === 'pending' ? (\n <Table\n actions={pendingTableActions}\n columns={pendingColumns}\n dataSource={pendingRows}\n fullWidth\n loading={loading}\n />\n ) : null}\n {activeTab === 'tracking' ? (\n <Table\n actions={historyTableActions}\n columns={historyColumns}\n dataSource={trackingRows}\n fullWidth\n loading={loading}\n />\n ) : null}\n {activeTab === 'history' ? (\n <Table\n actions={historyTableActions}\n columns={historyColumns}\n dataSource={historyRows}\n fullWidth\n loading={loading}\n />\n ) : null}\n </Section>\n </SectionGroup>\n </>\n );\n}\n\nfunction readReturnedSectionHint(\n hasRows: boolean,\n truncated: boolean,\n): string {\n if (!hasRows) {\n return `前 ${RETURNED_INSTANCE_PAGE_SIZE} 筆被退回的案件中沒有你發起的,可能還有其他被退回的案件,請到「我發起的」查看。`;\n }\n\n return truncated\n ? '你發起的案件被退回,請修改後重新送出。可能還有其他被退回的案件,請到「我發起的」查看。'\n : '你發起的案件被退回,請修改後重新送出。';\n}\n\n/**\n * Returned cases are a side list: a failure here, in the request or in reading\n * its rows, hides that section instead of taking the pending and history tasks\n * down with it.\n */\nasync function readReturnedInstances(\n currentMemberId: string,\n): Promise<Readonly<{\n rows: readonly ReturnedInstanceRow[];\n truncated: boolean;\n}> | null> {\n try {\n // SENT also lists cases filed on someone else's behalf; only the\n // initiator may resubmit, so those are filtered out here.\n const page: ApprovalInstancesPageResult = await listApprovalInstancesPage({\n page: 1,\n pageSize: RETURNED_INSTANCE_PAGE_SIZE,\n searchText: null,\n state: 'RETURNED',\n templateId: null,\n view: 'SENT',\n });\n\n return {\n rows: readReturnedInstanceRows(page.instances, currentMemberId),\n // The total includes cases filed for someone else, so compare it with\n // what this page fetched rather than with the rows kept.\n truncated: page.totalCount > page.instances.length,\n };\n } catch {\n return null;\n }\n}\n\nfunction readReturnedInstanceRows(\n instances: readonly ApprovalInstanceRecord[],\n currentMemberId: string,\n): readonly ReturnedInstanceRow[] {\n return instances\n .filter(\n (instance): boolean => instance.initiatorMemberId === currentMemberId,\n )\n .map(\n (instance): ReturnedInstanceRow => ({\n caseTitle: readApprovalInstanceCaseTitle(instance),\n id: instance.id,\n key: instance.id,\n startedAt: instance.startedAt,\n }),\n );\n}\n\nasync function readInboxTaskRows(\n tasks: readonly TaskRecord[],\n): Promise<InboxTaskRow[]> {\n const instances = await readTaskInstances(tasks);\n\n return tasks.map(\n (task, index): InboxTaskRow => ({\n ...task,\n caseTitle: readTaskCaseTitle(task, instances[index] ?? null),\n key: task.id,\n nodeText: readTaskNodeText(task),\n slaStatusColor: readSlaStatusColor(task.slaDueAt),\n slaStatusText: readSlaStatusText(task.slaDueAt),\n statusLabel: readTaskStatusLabel(task.status),\n }),\n );\n}\n\nasync function readApprovalHistoryRows(\n tasks: readonly TaskRecord[],\n currentMemberId: string,\n): Promise<ApprovalHistoryRow[]> {\n const [decisionLists, instances] = await Promise.all([\n Promise.all(tasks.map((task) => listTaskDecisions(task.id))),\n readTaskInstances(tasks),\n ]);\n\n return tasks.map((task, index): ApprovalHistoryRow => {\n const decisions = decisionLists[index] ?? [];\n const decision = readLatestOwnTaskDecision(decisions, currentMemberId);\n const instance = instances[index] ?? null;\n\n return {\n ...task,\n caseTitle: readTaskCaseTitle(task, instance),\n decisionAction: decision?.action ?? null,\n decisionComment: decision?.comment ?? null,\n decisionCommentText: readDecisionCommentText(decision?.comment ?? null),\n decisionLabel: readTaskDecisionLabel(decision?.action ?? null),\n // completedAt is when the last signer finished, so it can stand in only\n // when the task has no decisions at all.\n decidedAt:\n decision?.decidedAt ??\n (decisions.length === 0 ? task.completedAt : null),\n instanceState: instance?.state ?? null,\n instanceStateLabel: readInstanceStateLabel(instance?.state ?? null),\n key: task.id,\n nodeText: readTaskNodeText(task),\n };\n });\n}\n\nasync function readTaskInstances(\n tasks: readonly TaskRecord[],\n): Promise<readonly (ApprovalInstanceRecord | null)[]> {\n return Promise.all(\n tasks.map(async (task): Promise<ApprovalInstanceRecord | null> => {\n try {\n return (await readApprovalInstance(task.instanceId)).instance;\n } catch {\n return null;\n }\n }),\n );\n}\n\nfunction readTaskCaseTitle(\n task: TaskRecord,\n instance: ApprovalInstanceRecord | null,\n): string {\n return instance ? readApprovalInstanceCaseTitle(instance) : task.instanceId;\n}\n\n// A sequential or countersign task holds decisions from several signers, so\n// the newest one on the task can be somebody else's. A delegate is the one\n// recorded as deciding, and a principal never gets the delegated task in this\n// list.\nfunction readLatestOwnTaskDecision(\n decisions: readonly TaskDecisionRecord[],\n currentMemberId: string,\n): TaskDecisionRecord | null {\n return readLatestTaskDecision(\n decisions.filter(\n (decision): boolean => decision.decidedByMemberId === currentMemberId,\n ),\n );\n}\n\nfunction readTaskDecisionLabel(\n action: TaskDecisionRecord['action'] | null,\n): string {\n if (action === 'APPROVED') {\n return '同意';\n }\n\n if (action === 'REJECTED') {\n return '拒絕';\n }\n\n if (action === 'RETURNED') {\n return '退回';\n }\n\n if (action === 'TRANSFERRED') {\n return '轉派';\n }\n\n return '未記錄';\n}\n\nfunction readTaskDecisionColor(\n action: TaskDecisionRecord['action'] | null,\n): 'text-error' | 'text-neutral' | 'text-success' {\n if (action === 'APPROVED') {\n return 'text-success';\n }\n\n if (action === 'REJECTED' || action === 'RETURNED') {\n return 'text-error';\n }\n\n return 'text-neutral';\n}\n\nfunction readInstanceStateLabel(\n state: ApprovalInstanceRecord['state'] | null,\n): string {\n if (state === 'RUNNING') {\n return '進行中';\n }\n\n if (state === 'APPROVED') {\n return '已通過';\n }\n\n if (state === 'REJECTED') {\n return '已拒絕';\n }\n\n if (state === 'RETURNED') {\n return '已退回';\n }\n\n if (state === 'CANCELLED') {\n return '已取消';\n }\n\n if (state === 'EXPIRED') {\n return '已逾期';\n }\n\n if (state === 'DRAFT') {\n return '草稿';\n }\n\n return '-';\n}\n\nfunction readInstanceStateColor(\n state: ApprovalInstanceRecord['state'] | null,\n): 'text-error' | 'text-neutral' | 'text-success' {\n if (state === 'APPROVED') {\n return 'text-success';\n }\n\n if (state === 'REJECTED' || state === 'CANCELLED' || state === 'EXPIRED') {\n return 'text-error';\n }\n\n return 'text-neutral';\n}\n\nfunction readDecisionCommentText(comment: string | null): string {\n const trimmedComment = comment?.trim() ?? '';\n\n return trimmedComment || '-';\n}\n\nfunction formatHistoryDateTime(value: string | null): string {\n return formatDateTime(value);\n}\n\nfunction readSlaStatusText(value: string | null): string {\n if (!value) {\n return '-';\n }\n\n const dueAt = new Date(value).getTime();\n const now = Date.now();\n const diffMs = Math.abs(dueAt - now);\n const label = formatDuration(diffMs);\n\n return dueAt < now ? `已逾期 ${label}` : `剩餘 ${label}`;\n}\n\nfunction readSlaStatusColor(\n value: string | null,\n): 'text-error' | 'text-neutral' | 'text-success' {\n if (!value) {\n return 'text-neutral';\n }\n\n return new Date(value).getTime() < Date.now() ? 'text-error' : 'text-success';\n}\n\nfunction formatDuration(value: number): string {\n const totalMinutes = Math.max(1, Math.ceil(value / 60_000));\n const days = Math.floor(totalMinutes / 1440);\n const hours = Math.floor((totalMinutes % 1440) / 60);\n const minutes = totalMinutes % 60;\n\n if (days > 0) {\n return `${days}天 ${hours}小時`;\n }\n\n if (hours > 0) {\n return `${hours}小時 ${minutes}分鐘`;\n }\n\n return `${minutes}分鐘`;\n}\n\nfunction readTaskStatusLabel(status: TaskRecord['status']): string {\n if (status === 'PENDING') {\n return '待處理';\n }\n\n if (status === 'IN_PROGRESS') {\n return '處理中';\n }\n\n if (status === 'COMPLETED') {\n return '已完成';\n }\n\n if (status === 'TRANSFERRED') {\n return '已轉派';\n }\n\n if (status === 'CANCELLED') {\n return '已取消';\n }\n\n return status;\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '發生未知錯誤';\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["_Fragment","formatDateTime","useAuth","readLatestTaskDecision","readTaskNodeText","useRouterAdapter","useBPMRoutes","Fragment$1","ContentHeader","RETURNED_INSTANCE_PAGE_SIZE","RETURNED_SECTION_STYLE","PAGE_SIZE_OPTIONS","FIRST_PAGE","InboxView","router","routes","currentMemberId","member","activeTab","setActiveTab","useState","error","setError","pendingPaging","setPendingPaging","trackingPaging","setTrackingPaging","historyPaging","setHistoryPaging","pending","useTabPage","readRows","listInboxTasksPage","memberId","paging","readInboxTaskRows","tracking","listApprovalHistoryTasksPage","readApprovalHistoryRows","tasks","history","returnedRows","setReturnedRows","returnedTruncated","setReturnedTruncated","readReturnedInstances","returned","readErrorMessage","requestError","pendingColumns","useMemo","record","Badge","Typography","pendingTableActions","returnedColumns","returnedTableActions","historyColumns","readTaskDecisionColor","readInstanceStateColor","formatHistoryDateTime","historyTableActions","handleTabChange","activeKey","PageHeader","Button","PlusIcon","SectionGroup","Section","Tab","TabItem","readReturnedSectionHint","Table","readTablePagination","state","setState","latestRequestRef","useRef","loadRef","load","refresh","useCallback","request","previous","loaded","lastPage","onError","setPaging","useEffect","enabled","toRows","page","from","to","count","hasRows","truncated","listApprovalInstancesPage","readReturnedInstanceRows","instances","instance","readApprovalInstanceCaseTitle","readTaskInstances","task","readTaskCaseTitle","index","readSlaStatusColor","readSlaStatusText","readTaskStatusLabel","decisionLists","listTaskDecisions","decisions","decision","readLatestOwnTaskDecision","readDecisionCommentText","readTaskDecisionLabel","readInstanceStateLabel","readApprovalInstance","action","comment","value","dueAt","now","label","formatDuration","totalMinutes","days","hours","minutes","status"],"sources":["../../../src/views/inbox/InboxView.tsx"],"sourcesContent":["'use client';\n\nimport {\n type CSSProperties,\n type Key,\n ReactElement,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport {\n Badge,\n Button,\n PageHeader,\n Section,\n SectionGroup,\n Tab,\n TabItem,\n Table,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { PlusIcon } from '@mezzanine-ui/icons';\nimport type { TableActions, TableColumn } from '@mezzanine-ui/core/table';\nimport {\n ApprovalInstanceRecord,\n type ApprovalInstancesPageResult,\n listApprovalHistoryTasksPage,\n listApprovalInstancesPage,\n listInboxTasksPage,\n type MemberTaskPage,\n listTaskDecisions,\n readApprovalInstance,\n readApprovalInstanceCaseTitle,\n TaskDecisionRecord,\n TaskRecord,\n} from '@rytass/bpm-core-client/workflow';\nimport { formatDateTime } from '../../lib/format-date-time';\nimport { useAuth } from '../../lib/auth-provider';\nimport { readTaskNodeText } from '../../lib/task-node-label';\nimport { readLatestTaskDecision } from '../../lib/task-decision';\nimport { useRouterAdapter } from '../../lib/router-adapter';\nimport { useBPMRoutes } from '../../lib/routes-config';\n\ntype InboxTabKey = 'history' | 'pending' | 'tracking';\n\ntype InboxTaskRow = Readonly<\n Record<string, unknown> &\n TaskRecord & {\n caseTitle: string;\n key: string;\n nodeText: string;\n slaStatusColor: 'text-error' | 'text-neutral' | 'text-success';\n slaStatusText: string;\n statusLabel: string;\n }\n>;\n\ntype ReturnedInstanceRow = Readonly<{\n caseTitle: string;\n id: string;\n key: string;\n startedAt: string | null;\n}>;\n\n/** The server caps a page at 100; more than that is left to 我發起的. */\nconst RETURNED_INSTANCE_PAGE_SIZE = 100;\n\nconst RETURNED_SECTION_STYLE: CSSProperties = {\n display: 'grid',\n gap: 'var(--mzn-spacing-gap-base)',\n marginBottom: 'var(--mzn-spacing-padding-vertical-spacious)',\n};\n\ntype ApprovalHistoryRow = Readonly<\n Record<string, unknown> &\n TaskRecord & {\n caseTitle: string;\n decisionAction: TaskDecisionRecord['action'] | null;\n decisionComment: string | null;\n decisionCommentText: string;\n decisionLabel: string;\n decidedAt: string | null;\n instanceState: ApprovalInstanceRecord['state'] | null;\n instanceStateLabel: string;\n key: string;\n nodeText: string;\n }\n>;\n\ntype TabPaging = Readonly<{ page: number; pageSize: number }>;\n\ntype TabPage<TRow> = Readonly<{\n loading: boolean;\n rows: readonly TRow[];\n total: number;\n}>;\n\nconst PAGE_SIZE_OPTIONS = [10, 20, 50];\n\nconst FIRST_PAGE: TabPaging = { page: 1, pageSize: 20 };\n\n/**\n * Framework-agnostic view for the BPM \"我的待簽\" inbox. Mechanical port of\n * `apps/client/src/app/inbox/page.tsx` — renders pending tasks, tracking,\n * and history tabs.\n */\nexport function InboxView(): ReactElement {\n const router = useRouterAdapter();\n const routes = useBPMRoutes();\n const { member } = useAuth();\n const currentMemberId = member?.memberId ?? null;\n const [activeTab, setActiveTab] = useState<InboxTabKey>('pending');\n const [error, setError] = useState<string | null>(null);\n const [pendingPaging, setPendingPaging] = useState<TabPaging>(FIRST_PAGE);\n const [trackingPaging, setTrackingPaging] =\n useState<TabPaging>(FIRST_PAGE);\n const [historyPaging, setHistoryPaging] = useState<TabPaging>(FIRST_PAGE);\n const pending = useTabPage<InboxTaskRow>(\n currentMemberId,\n { enabled: true, paging: pendingPaging, setPaging: setPendingPaging },\n setError,\n async (memberId, paging) => {\n const page = await listInboxTasksPage({\n assigneeMemberId: memberId,\n ...paging,\n });\n\n return readRows(page, readInboxTaskRows);\n },\n );\n // Tasks of instances still under way: the same history, narrowed on the\n // server so the badge and the pages count every such task, not one page.\n const tracking = useTabPage<ApprovalHistoryRow>(\n currentMemberId,\n { enabled: true, paging: trackingPaging, setPaging: setTrackingPaging },\n setError,\n async (memberId, paging) => {\n const page = await listApprovalHistoryTasksPage({\n assigneeMemberId: memberId,\n instanceState: 'RUNNING',\n ...paging,\n });\n\n return readRows(page, (tasks) => readApprovalHistoryRows(tasks, memberId));\n },\n );\n // History has no badge, so it is read only once its tab is opened.\n const history = useTabPage<ApprovalHistoryRow>(\n currentMemberId,\n {\n enabled: activeTab === 'history',\n paging: historyPaging,\n setPaging: setHistoryPaging,\n },\n setError,\n async (memberId, paging) => {\n const page = await listApprovalHistoryTasksPage({\n assigneeMemberId: memberId,\n ...paging,\n });\n\n return readRows(page, (tasks) => readApprovalHistoryRows(tasks, memberId));\n },\n );\n const [returnedRows, setReturnedRows] = useState<\n readonly ReturnedInstanceRow[]\n >([]);\n const [returnedTruncated, setReturnedTruncated] = useState(false);\n\n useEffect((): void => {\n if (!currentMemberId) {\n setReturnedRows([]);\n setReturnedTruncated(false);\n\n return;\n }\n\n void readReturnedInstances(currentMemberId).then(\n (returned): void => {\n setReturnedRows(returned?.rows ?? []);\n setReturnedTruncated(returned?.truncated ?? false);\n },\n (requestError: unknown): void => setError(readErrorMessage(requestError)),\n );\n }, [currentMemberId]);\n\n const pendingColumns = useMemo(\n (): TableColumn<InboxTaskRow>[] => [\n { dataIndex: 'caseTitle', key: 'caseTitle', title: '案件', width: 280 },\n { dataIndex: 'nodeText', key: 'nodeText', title: '節點', width: 180 },\n {\n dataIndex: 'statusLabel',\n key: 'statusLabel',\n title: '狀態',\n width: 120,\n },\n {\n key: 'fill',\n // Approving one of these needs the case page, where the fill is entered.\n render: (record: InboxTaskRow): ReactElement | null =>\n record.hasFillableFields ? (\n <Badge size=\"sub\" text=\"需補填\" variant=\"dot-warning\" />\n ) : null,\n title: '補填',\n width: 120,\n },\n {\n key: 'slaDueAt',\n render: (record: InboxTaskRow): ReactElement => (\n <Typography\n color={record.slaStatusColor}\n component=\"span\"\n variant=\"body\"\n >\n {record.slaStatusText}\n </Typography>\n ),\n title: 'SLA',\n width: 180,\n },\n {\n key: 'createdAt',\n render: (record: InboxTaskRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {formatDateTime(record.createdAt)}\n </Typography>\n ),\n title: '建立時間',\n width: 220,\n },\n ],\n [],\n );\n const pendingTableActions = useMemo(\n (): TableActions<InboxTaskRow> => ({\n render: (record): ReturnType<TableActions<InboxTaskRow>['render']> => [\n {\n name: '處理',\n onClick: (): void => router.push(routes.caseDetail(record.instanceId)),\n },\n ],\n variant: 'base-secondary',\n width: 88,\n }),\n [router, routes],\n );\n const returnedColumns = useMemo(\n (): TableColumn<ReturnedInstanceRow>[] => [\n { dataIndex: 'caseTitle', key: 'caseTitle', title: '案件', width: 280 },\n {\n key: 'startedAt',\n render: (record: ReturnedInstanceRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {formatDateTime(record.startedAt)}\n </Typography>\n ),\n title: '發起時間',\n width: 220,\n },\n ],\n [],\n );\n const returnedTableActions = useMemo(\n (): TableActions<ReturnedInstanceRow> => ({\n render: (\n record,\n ): ReturnType<TableActions<ReturnedInstanceRow>['render']> => [\n {\n name: '重新送出',\n onClick: (): void => router.push(routes.caseDetail(record.id)),\n },\n ],\n variant: 'base-secondary',\n width: 120,\n }),\n [router, routes],\n );\n const historyColumns = useMemo(\n (): TableColumn<ApprovalHistoryRow>[] => [\n { dataIndex: 'caseTitle', key: 'caseTitle', title: '案件', width: 280 },\n { dataIndex: 'nodeText', key: 'nodeText', title: '節點', width: 180 },\n {\n key: 'decisionLabel',\n render: (record: ApprovalHistoryRow): ReactElement => (\n <Typography\n color={readTaskDecisionColor(record.decisionAction)}\n component=\"span\"\n variant=\"body\"\n >\n {record.decisionLabel}\n </Typography>\n ),\n title: '決議',\n width: 120,\n },\n {\n key: 'decisionComment',\n render: (record: ApprovalHistoryRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {record.decisionCommentText}\n </Typography>\n ),\n title: '意見',\n width: 240,\n },\n {\n key: 'instanceStateLabel',\n render: (record: ApprovalHistoryRow): ReactElement => (\n <Typography\n color={readInstanceStateColor(record.instanceState)}\n component=\"span\"\n variant=\"body\"\n >\n {record.instanceStateLabel}\n </Typography>\n ),\n title: '流程狀態',\n width: 140,\n },\n {\n key: 'decidedAt',\n render: (record: ApprovalHistoryRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {formatHistoryDateTime(record.decidedAt)}\n </Typography>\n ),\n title: '簽核時間',\n width: 220,\n },\n ],\n [],\n );\n const historyTableActions = useMemo(\n (): TableActions<ApprovalHistoryRow> => ({\n render: (\n record,\n ): ReturnType<TableActions<ApprovalHistoryRow>['render']> => [\n {\n name: '查看',\n onClick: (): void => router.push(routes.caseDetail(record.instanceId)),\n },\n ],\n variant: 'base-secondary',\n width: 88,\n }),\n [router, routes],\n );\n\n function handleTabChange(activeKey: Key): void {\n if (\n activeKey === 'history' ||\n activeKey === 'pending' ||\n activeKey === 'tracking'\n ) {\n setActiveTab(activeKey);\n return;\n }\n\n setActiveTab('pending');\n }\n\n return (\n <>\n <PageHeader>\n <ContentHeader\n description={`目前以 ${member?.name ?? currentMemberId ?? '目前登入會員'} 查詢待處理與歷史簽核任務。`}\n title=\"我的待簽\"\n >\n <Button\n icon={PlusIcon}\n iconType=\"leading\"\n onClick={(): void => router.push(routes.caseNew())}\n variant=\"base-primary\"\n >\n 發起簽核\n </Button>\n </ContentHeader>\n </PageHeader>\n\n <SectionGroup>\n <Section>\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n <Tab activeKey={activeTab} onChange={handleTabChange} size=\"sub\">\n <TabItem\n badgeCount={pending.loading ? undefined : pending.total}\n key=\"pending\"\n >\n 待簽核\n </TabItem>\n <TabItem\n badgeCount={tracking.loading ? undefined : tracking.total}\n key=\"tracking\"\n >\n 已處理未結束\n </TabItem>\n <TabItem key=\"history\">歷史簽核記錄</TabItem>\n </Tab>\n\n {/* A truncated page with none of the member's own cases still\n points to 我發起的: theirs may sit past the first page. */}\n {activeTab === 'pending' &&\n (returnedRows.length > 0 || returnedTruncated) ? (\n <div data-returned-instances=\"\" style={RETURNED_SECTION_STYLE}>\n {/* Without rows there is nothing here to resubmit, only the\n pointer to 我發起的. */}\n {returnedRows.length > 0 ? (\n <Typography component=\"h2\" variant=\"label-primary\">\n 被退回待重送\n </Typography>\n ) : null}\n <Typography color=\"text-neutral\" variant=\"caption\">\n {readReturnedSectionHint(\n returnedRows.length > 0,\n returnedTruncated,\n )}\n </Typography>\n {returnedRows.length > 0 ? (\n <Table\n actions={returnedTableActions}\n columns={returnedColumns}\n dataSource={[...returnedRows]}\n fullWidth\n />\n ) : null}\n </div>\n ) : null}\n {activeTab === 'pending' ? (\n <Table\n actions={pendingTableActions}\n columns={pendingColumns}\n dataSource={[...pending.rows]}\n fullWidth\n loading={pending.loading}\n pagination={readTablePagination(\n pendingPaging,\n pending.total,\n setPendingPaging,\n )}\n />\n ) : null}\n {activeTab === 'tracking' ? (\n <Table\n actions={historyTableActions}\n columns={historyColumns}\n dataSource={[...tracking.rows]}\n fullWidth\n loading={tracking.loading}\n pagination={readTablePagination(\n trackingPaging,\n tracking.total,\n setTrackingPaging,\n )}\n />\n ) : null}\n {activeTab === 'history' ? (\n <Table\n actions={historyTableActions}\n columns={historyColumns}\n dataSource={[...history.rows]}\n fullWidth\n loading={history.loading}\n pagination={readTablePagination(\n historyPaging,\n history.total,\n setHistoryPaging,\n )}\n />\n ) : null}\n </Section>\n </SectionGroup>\n </>\n );\n}\n\n/**\n * One tab's rows for the current member and paging. Only the latest request\n * may write, so paging quickly cannot let an earlier page's late answer\n * replace the one asked for last. Nothing is read while `enabled` is false.\n * A page past the end (the list shrank since it was chosen) moves to the last\n * page instead of showing an empty table.\n */\nfunction useTabPage<TRow>(\n memberId: string | null,\n {\n enabled,\n paging,\n setPaging,\n }: Readonly<{\n enabled: boolean;\n paging: TabPaging;\n setPaging: (paging: TabPaging) => void;\n }>,\n onError: (message: string | null) => void,\n load: (\n memberId: string,\n paging: TabPaging,\n ) => Promise<Readonly<{ rows: readonly TRow[]; total: number }>>,\n): TabPage<TRow> {\n const [state, setState] = useState<TabPage<TRow>>({\n loading: true,\n rows: [],\n total: 0,\n });\n const latestRequestRef = useRef(0);\n const loadRef = useRef(load);\n\n loadRef.current = load;\n\n const refresh = useCallback(async (): Promise<void> => {\n const request = latestRequestRef.current + 1;\n\n latestRequestRef.current = request;\n\n if (!memberId) {\n setState({ loading: false, rows: [], total: 0 });\n\n return;\n }\n\n setState((previous) => ({ ...previous, loading: true }));\n\n try {\n const loaded = await loadRef.current(memberId, paging);\n\n if (latestRequestRef.current !== request) {\n return;\n }\n\n const lastPage = Math.max(1, Math.ceil(loaded.total / paging.pageSize));\n\n if (!loaded.rows.length && paging.page > lastPage) {\n setPaging({ ...paging, page: lastPage });\n\n return;\n }\n\n setState({ loading: false, ...loaded });\n } catch (requestError: unknown) {\n if (latestRequestRef.current === request) {\n onError(readErrorMessage(requestError));\n setState((previous) => ({ ...previous, loading: false }));\n }\n }\n }, [memberId, onError, paging, setPaging]);\n\n useEffect((): void => {\n if (enabled) {\n void refresh();\n }\n }, [enabled, refresh]);\n\n return state;\n}\n\nasync function readRows<TRow>(\n page: MemberTaskPage,\n toRows: (tasks: readonly TaskRecord[]) => Promise<TRow[]>,\n): Promise<Readonly<{ rows: readonly TRow[]; total: number }>> {\n return { rows: await toRows(page.tasks), total: page.totalCount };\n}\n\nfunction readTablePagination(\n paging: TabPaging,\n total: number,\n setPaging: (paging: TabPaging) => void,\n): Readonly<{\n current: number;\n onChange: (page: number) => void;\n onChangePageSize: (pageSize: number) => void;\n pageSize: number;\n pageSizeLabel: string;\n pageSizeOptions: number[];\n renderResultSummary: (from: number, to: number, total: number) => string;\n showPageSizeOptions: boolean;\n total: number;\n}> {\n return {\n current: paging.page,\n onChange: (page: number): void => setPaging({ ...paging, page }),\n onChangePageSize: (pageSize: number): void =>\n setPaging({ page: 1, pageSize }),\n pageSize: paging.pageSize,\n pageSizeLabel: '每頁筆數',\n pageSizeOptions: PAGE_SIZE_OPTIONS,\n renderResultSummary: (from: number, to: number, count: number): string =>\n `顯示 ${from}-${to} 筆,共 ${count} 筆`,\n showPageSizeOptions: true,\n total,\n };\n}\n\nfunction readReturnedSectionHint(\n hasRows: boolean,\n truncated: boolean,\n): string {\n if (!hasRows) {\n return `前 ${RETURNED_INSTANCE_PAGE_SIZE} 筆被退回的案件中沒有你發起的,可能還有其他被退回的案件,請到「我發起的」查看。`;\n }\n\n return truncated\n ? '你發起的案件被退回,請修改後重新送出。可能還有其他被退回的案件,請到「我發起的」查看。'\n : '你發起的案件被退回,請修改後重新送出。';\n}\n\n/**\n * Returned cases are a side list: a failure here, in the request or in reading\n * its rows, hides that section instead of taking the pending and history tasks\n * down with it.\n */\nasync function readReturnedInstances(\n currentMemberId: string,\n): Promise<Readonly<{\n rows: readonly ReturnedInstanceRow[];\n truncated: boolean;\n}> | null> {\n try {\n // SENT also lists cases filed on someone else's behalf; only the\n // initiator may resubmit, so those are filtered out here.\n const page: ApprovalInstancesPageResult = await listApprovalInstancesPage({\n page: 1,\n pageSize: RETURNED_INSTANCE_PAGE_SIZE,\n searchText: null,\n state: 'RETURNED',\n templateId: null,\n view: 'SENT',\n });\n\n return {\n rows: readReturnedInstanceRows(page.instances, currentMemberId),\n // The total includes cases filed for someone else, so compare it with\n // what this page fetched rather than with the rows kept.\n truncated: page.totalCount > page.instances.length,\n };\n } catch {\n return null;\n }\n}\n\nfunction readReturnedInstanceRows(\n instances: readonly ApprovalInstanceRecord[],\n currentMemberId: string,\n): readonly ReturnedInstanceRow[] {\n return instances\n .filter(\n (instance): boolean => instance.initiatorMemberId === currentMemberId,\n )\n .map(\n (instance): ReturnedInstanceRow => ({\n caseTitle: readApprovalInstanceCaseTitle(instance),\n id: instance.id,\n key: instance.id,\n startedAt: instance.startedAt,\n }),\n );\n}\n\nasync function readInboxTaskRows(\n tasks: readonly TaskRecord[],\n): Promise<InboxTaskRow[]> {\n const instances = await readTaskInstances(tasks);\n\n return tasks.map(\n (task, index): InboxTaskRow => ({\n ...task,\n caseTitle: readTaskCaseTitle(task, instances[index] ?? null),\n key: task.id,\n nodeText: readTaskNodeText(task),\n slaStatusColor: readSlaStatusColor(task.slaDueAt),\n slaStatusText: readSlaStatusText(task.slaDueAt),\n statusLabel: readTaskStatusLabel(task.status),\n }),\n );\n}\n\nasync function readApprovalHistoryRows(\n tasks: readonly TaskRecord[],\n currentMemberId: string,\n): Promise<ApprovalHistoryRow[]> {\n const [decisionLists, instances] = await Promise.all([\n Promise.all(tasks.map((task) => listTaskDecisions(task.id))),\n readTaskInstances(tasks),\n ]);\n\n return tasks.map((task, index): ApprovalHistoryRow => {\n const decisions = decisionLists[index] ?? [];\n const decision = readLatestOwnTaskDecision(decisions, currentMemberId);\n const instance = instances[index] ?? null;\n\n return {\n ...task,\n caseTitle: readTaskCaseTitle(task, instance),\n decisionAction: decision?.action ?? null,\n decisionComment: decision?.comment ?? null,\n decisionCommentText: readDecisionCommentText(decision?.comment ?? null),\n decisionLabel: readTaskDecisionLabel(decision?.action ?? null),\n // completedAt is when the last signer finished, so it can stand in only\n // when the task has no decisions at all.\n decidedAt:\n decision?.decidedAt ??\n (decisions.length === 0 ? task.completedAt : null),\n instanceState: instance?.state ?? null,\n instanceStateLabel: readInstanceStateLabel(instance?.state ?? null),\n key: task.id,\n nodeText: readTaskNodeText(task),\n };\n });\n}\n\nasync function readTaskInstances(\n tasks: readonly TaskRecord[],\n): Promise<readonly (ApprovalInstanceRecord | null)[]> {\n return Promise.all(\n tasks.map(async (task): Promise<ApprovalInstanceRecord | null> => {\n try {\n return (await readApprovalInstance(task.instanceId)).instance;\n } catch {\n return null;\n }\n }),\n );\n}\n\nfunction readTaskCaseTitle(\n task: TaskRecord,\n instance: ApprovalInstanceRecord | null,\n): string {\n return instance ? readApprovalInstanceCaseTitle(instance) : task.instanceId;\n}\n\n// A sequential or countersign task holds decisions from several signers, so\n// the newest one on the task can be somebody else's. A delegate is the one\n// recorded as deciding, and a principal never gets the delegated task in this\n// list.\nfunction readLatestOwnTaskDecision(\n decisions: readonly TaskDecisionRecord[],\n currentMemberId: string,\n): TaskDecisionRecord | null {\n return readLatestTaskDecision(\n decisions.filter(\n (decision): boolean => decision.decidedByMemberId === currentMemberId,\n ),\n );\n}\n\nfunction readTaskDecisionLabel(\n action: TaskDecisionRecord['action'] | null,\n): string {\n if (action === 'APPROVED') {\n return '同意';\n }\n\n if (action === 'REJECTED') {\n return '拒絕';\n }\n\n if (action === 'RETURNED') {\n return '退回';\n }\n\n if (action === 'TRANSFERRED') {\n return '轉派';\n }\n\n return '未記錄';\n}\n\nfunction readTaskDecisionColor(\n action: TaskDecisionRecord['action'] | null,\n): 'text-error' | 'text-neutral' | 'text-success' {\n if (action === 'APPROVED') {\n return 'text-success';\n }\n\n if (action === 'REJECTED' || action === 'RETURNED') {\n return 'text-error';\n }\n\n return 'text-neutral';\n}\n\nfunction readInstanceStateLabel(\n state: ApprovalInstanceRecord['state'] | null,\n): string {\n if (state === 'RUNNING') {\n return '進行中';\n }\n\n if (state === 'APPROVED') {\n return '已通過';\n }\n\n if (state === 'REJECTED') {\n return '已拒絕';\n }\n\n if (state === 'RETURNED') {\n return '已退回';\n }\n\n if (state === 'CANCELLED') {\n return '已取消';\n }\n\n if (state === 'EXPIRED') {\n return '已逾期';\n }\n\n if (state === 'DRAFT') {\n return '草稿';\n }\n\n return '-';\n}\n\nfunction readInstanceStateColor(\n state: ApprovalInstanceRecord['state'] | null,\n): 'text-error' | 'text-neutral' | 'text-success' {\n if (state === 'APPROVED') {\n return 'text-success';\n }\n\n if (state === 'REJECTED' || state === 'CANCELLED' || state === 'EXPIRED') {\n return 'text-error';\n }\n\n return 'text-neutral';\n}\n\nfunction readDecisionCommentText(comment: string | null): string {\n const trimmedComment = comment?.trim() ?? '';\n\n return trimmedComment || '-';\n}\n\nfunction formatHistoryDateTime(value: string | null): string {\n return formatDateTime(value);\n}\n\nfunction readSlaStatusText(value: string | null): string {\n if (!value) {\n return '-';\n }\n\n const dueAt = new Date(value).getTime();\n const now = Date.now();\n const diffMs = Math.abs(dueAt - now);\n const label = formatDuration(diffMs);\n\n return dueAt < now ? `已逾期 ${label}` : `剩餘 ${label}`;\n}\n\nfunction readSlaStatusColor(\n value: string | null,\n): 'text-error' | 'text-neutral' | 'text-success' {\n if (!value) {\n return 'text-neutral';\n }\n\n return new Date(value).getTime() < Date.now() ? 'text-error' : 'text-success';\n}\n\nfunction formatDuration(value: number): string {\n const totalMinutes = Math.max(1, Math.ceil(value / 60_000));\n const days = Math.floor(totalMinutes / 1440);\n const hours = Math.floor((totalMinutes % 1440) / 60);\n const minutes = totalMinutes % 60;\n\n if (days > 0) {\n return `${days}天 ${hours}小時`;\n }\n\n if (hours > 0) {\n return `${hours}小時 ${minutes}分鐘`;\n }\n\n return `${minutes}分鐘`;\n}\n\nfunction readTaskStatusLabel(status: TaskRecord['status']): string {\n if (status === 'PENDING') {\n return '待處理';\n }\n\n if (status === 'IN_PROGRESS') {\n return '處理中';\n }\n\n if (status === 'COMPLETED') {\n return '已完成';\n }\n\n if (status === 'TRANSFERRED') {\n return '已轉派';\n }\n\n if (status === 'CANCELLED') {\n return '已取消';\n }\n\n return status;\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '發生未知錯誤';\n}\n"],"mappings":"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"}
|