@things-factory/worklist 10.0.19 → 10.1.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 (215) hide show
  1. package/dist-client/activity-cells.d.ts +21 -0
  2. package/dist-client/activity-cells.js +117 -0
  3. package/dist-client/activity-cells.js.map +1 -0
  4. package/dist-client/activity-summary-generator.js +57 -11
  5. package/dist-client/activity-summary-generator.js.map +1 -1
  6. package/dist-client/approval-progress.d.ts +18 -0
  7. package/dist-client/approval-progress.js +31 -0
  8. package/dist-client/approval-progress.js.map +1 -0
  9. package/dist-client/bootstrap.js +6 -11
  10. package/dist-client/bootstrap.js.map +1 -1
  11. package/dist-client/chip-filters.d.ts +53 -0
  12. package/dist-client/chip-filters.js +60 -0
  13. package/dist-client/chip-filters.js.map +1 -0
  14. package/dist-client/components/activity-approval-ribon.d.ts +19 -0
  15. package/dist-client/components/activity-approval-ribon.js +50 -126
  16. package/dist-client/components/activity-approval-ribon.js.map +1 -1
  17. package/dist-client/components/activity-fields.d.ts +50 -0
  18. package/dist-client/components/activity-fields.js +393 -0
  19. package/dist-client/components/activity-fields.js.map +1 -0
  20. package/dist-client/components/activity-instance-ribon.d.ts +19 -0
  21. package/dist-client/components/activity-instance-ribon.js +55 -122
  22. package/dist-client/components/activity-instance-ribon.js.map +1 -1
  23. package/dist-client/components/activity-thread-ribon.d.ts +25 -0
  24. package/dist-client/components/activity-thread-ribon.js +64 -121
  25. package/dist-client/components/activity-thread-ribon.js.map +1 -1
  26. package/dist-client/components/activity-thread-timeline.d.ts +14 -2
  27. package/dist-client/components/activity-thread-timeline.js +234 -104
  28. package/dist-client/components/activity-thread-timeline.js.map +1 -1
  29. package/dist-client/components/state-ribon.d.ts +40 -0
  30. package/dist-client/components/state-ribon.js +200 -0
  31. package/dist-client/components/state-ribon.js.map +1 -0
  32. package/dist-client/due.d.ts +16 -0
  33. package/dist-client/due.js +78 -0
  34. package/dist-client/due.js.map +1 -0
  35. package/dist-client/pages/activity/activity-list-page.js +4 -6
  36. package/dist-client/pages/activity/activity-list-page.js.map +1 -1
  37. package/dist-client/pages/activity-approval/activity-approval-list-page.js +2 -1
  38. package/dist-client/pages/activity-approval/activity-approval-list-page.js.map +1 -1
  39. package/dist-client/pages/activity-approval/activity-approval-page.d.ts +52 -5
  40. package/dist-client/pages/activity-approval/activity-approval-page.js +265 -118
  41. package/dist-client/pages/activity-approval/activity-approval-page.js.map +1 -1
  42. package/dist-client/pages/activity-instance/activity-instance-list-page.d.ts +0 -1
  43. package/dist-client/pages/activity-instance/activity-instance-list-page.js +8 -24
  44. package/dist-client/pages/activity-instance/activity-instance-list-page.js.map +1 -1
  45. package/dist-client/pages/activity-instance/activity-instance-start-page.d.ts +1 -0
  46. package/dist-client/pages/activity-instance/activity-instance-start-page.js +7 -0
  47. package/dist-client/pages/activity-instance/activity-instance-start-page.js.map +1 -1
  48. package/dist-client/pages/activity-instance/activity-instance-view.js +1 -1
  49. package/dist-client/pages/activity-instance/activity-instance-view.js.map +1 -1
  50. package/dist-client/pages/activity-stats/activity-stats-list-page.js +2 -1
  51. package/dist-client/pages/activity-stats/activity-stats-list-page.js.map +1 -1
  52. package/dist-client/pages/activity-thread/activity-thread-page.d.ts +30 -4
  53. package/dist-client/pages/activity-thread/activity-thread-page.js +142 -66
  54. package/dist-client/pages/activity-thread/activity-thread-page.js.map +1 -1
  55. package/dist-client/pages/activity-thread/activity-thread-view-page.d.ts +17 -6
  56. package/dist-client/pages/activity-thread/activity-thread-view-page.js +118 -65
  57. package/dist-client/pages/activity-thread/activity-thread-view-page.js.map +1 -1
  58. package/dist-client/pages/activity-thread/activity-thread-view.d.ts +24 -7
  59. package/dist-client/pages/activity-thread/activity-thread-view.js +84 -308
  60. package/dist-client/pages/activity-thread/activity-thread-view.js.map +1 -1
  61. package/dist-client/pages/installable-activity/installable-activity-list-page.d.ts +1 -1
  62. package/dist-client/pages/installable-activity/installable-activity-list-page.js +13 -9
  63. package/dist-client/pages/installable-activity/installable-activity-list-page.js.map +1 -1
  64. package/dist-client/pages/todo/approval-done-list-page.d.ts +1 -0
  65. package/dist-client/pages/todo/approval-done-list-page.js +111 -58
  66. package/dist-client/pages/todo/approval-done-list-page.js.map +1 -1
  67. package/dist-client/pages/todo/approval-pending-list-page.d.ts +32 -1
  68. package/dist-client/pages/todo/approval-pending-list-page.js +485 -130
  69. package/dist-client/pages/todo/approval-pending-list-page.js.map +1 -1
  70. package/dist-client/pages/todo/done-list-calendar-page.d.ts +2 -0
  71. package/dist-client/pages/todo/done-list-calendar-page.js +213 -29
  72. package/dist-client/pages/todo/done-list-calendar-page.js.map +1 -1
  73. package/dist-client/pages/todo/done-list-page.d.ts +1 -0
  74. package/dist-client/pages/todo/done-list-page.js +126 -62
  75. package/dist-client/pages/todo/done-list-page.js.map +1 -1
  76. package/dist-client/pages/todo/draft-list-page.d.ts +1 -0
  77. package/dist-client/pages/todo/draft-list-page.js +82 -34
  78. package/dist-client/pages/todo/draft-list-page.js.map +1 -1
  79. package/dist-client/pages/todo/pickable-list-page.js +6 -10
  80. package/dist-client/pages/todo/pickable-list-page.js.map +1 -1
  81. package/dist-client/pages/todo/todo-list-page.d.ts +1 -1
  82. package/dist-client/pages/todo/todo-list-page.js +102 -78
  83. package/dist-client/pages/todo/todo-list-page.js.map +1 -1
  84. package/dist-client/route.d.ts +1 -1
  85. package/dist-client/route.js +0 -6
  86. package/dist-client/route.js.map +1 -1
  87. package/dist-client/templates/activity-approval-context-template.js +60 -138
  88. package/dist-client/templates/activity-approval-context-template.js.map +1 -1
  89. package/dist-client/templates/activity-instance-context-template.js +13 -172
  90. package/dist-client/templates/activity-instance-context-template.js.map +1 -1
  91. package/dist-client/templates/activity-thread-context-template.js +39 -131
  92. package/dist-client/templates/activity-thread-context-template.js.map +1 -1
  93. package/dist-client/templates/context-panel.d.ts +26 -0
  94. package/dist-client/templates/context-panel.js +182 -0
  95. package/dist-client/templates/context-panel.js.map +1 -0
  96. package/dist-client/tsconfig.tsbuildinfo +1 -1
  97. package/dist-client/when.d.ts +4 -0
  98. package/dist-client/when.js +72 -0
  99. package/dist-client/when.js.map +1 -0
  100. package/dist-server/controllers/activity-approval/_abort.js +2 -1
  101. package/dist-server/controllers/activity-approval/_abort.js.map +1 -1
  102. package/dist-server/controllers/activity-approval/approval-line.d.ts +106 -0
  103. package/dist-server/controllers/activity-approval/approval-line.js +151 -0
  104. package/dist-server/controllers/activity-approval/approval-line.js.map +1 -0
  105. package/dist-server/controllers/activity-approval/approve.js +61 -51
  106. package/dist-server/controllers/activity-approval/approve.js.map +1 -1
  107. package/dist-server/controllers/activity-approval/claim.d.ts +42 -0
  108. package/dist-server/controllers/activity-approval/claim.js +77 -0
  109. package/dist-server/controllers/activity-approval/claim.js.map +1 -0
  110. package/dist-server/controllers/activity-approval/create-approval.d.ts +41 -0
  111. package/dist-server/controllers/activity-approval/create-approval.js +78 -0
  112. package/dist-server/controllers/activity-approval/create-approval.js.map +1 -0
  113. package/dist-server/controllers/activity-approval/delegate.d.ts +16 -0
  114. package/dist-server/controllers/activity-approval/delegate.js +43 -7
  115. package/dist-server/controllers/activity-approval/delegate.js.map +1 -1
  116. package/dist-server/controllers/activity-approval/opinion.d.ts +10 -0
  117. package/dist-server/controllers/activity-approval/opinion.js +43 -0
  118. package/dist-server/controllers/activity-approval/opinion.js.map +1 -0
  119. package/dist-server/controllers/activity-approval/reject.js +21 -1
  120. package/dist-server/controllers/activity-approval/reject.js.map +1 -1
  121. package/dist-server/controllers/activity-approval/save.js +26 -2
  122. package/dist-server/controllers/activity-approval/save.js.map +1 -1
  123. package/dist-server/controllers/activity-installation/activity-installation-controller.d.ts +26 -0
  124. package/dist-server/controllers/activity-installation/activity-installation-controller.js +45 -0
  125. package/dist-server/controllers/activity-installation/activity-installation-controller.js.map +1 -1
  126. package/dist-server/controllers/activity-instance/approval-expectation.d.ts +34 -0
  127. package/dist-server/controllers/activity-instance/approval-expectation.js +18 -0
  128. package/dist-server/controllers/activity-instance/approval-expectation.js.map +1 -0
  129. package/dist-server/controllers/activity-instance/issue.js +32 -0
  130. package/dist-server/controllers/activity-instance/issue.js.map +1 -1
  131. package/dist-server/controllers/activity-thread/restart.js +17 -1
  132. package/dist-server/controllers/activity-thread/restart.js.map +1 -1
  133. package/dist-server/controllers/activity-thread/submit.js +23 -43
  134. package/dist-server/controllers/activity-thread/submit.js.map +1 -1
  135. package/dist-server/controllers/common.js +46 -0
  136. package/dist-server/controllers/common.js.map +1 -1
  137. package/dist-server/controllers/index.d.ts +1 -0
  138. package/dist-server/controllers/index.js +8 -0
  139. package/dist-server/controllers/index.js.map +1 -1
  140. package/dist-server/service/activity/activity-mutation.js +25 -5
  141. package/dist-server/service/activity/activity-mutation.js.map +1 -1
  142. package/dist-server/service/activity/activity-type.d.ts +14 -0
  143. package/dist-server/service/activity/activity-type.js +8 -0
  144. package/dist-server/service/activity/activity-type.js.map +1 -1
  145. package/dist-server/service/activity/activity.d.ts +18 -0
  146. package/dist-server/service/activity/activity.js +5 -0
  147. package/dist-server/service/activity/activity.js.map +1 -1
  148. package/dist-server/service/activity-approval/activity-approval-mutation.js +3 -2
  149. package/dist-server/service/activity-approval/activity-approval-mutation.js.map +1 -1
  150. package/dist-server/service/activity-approval/activity-approval-query.d.ts +19 -1
  151. package/dist-server/service/activity-approval/activity-approval-query.js +186 -56
  152. package/dist-server/service/activity-approval/activity-approval-query.js.map +1 -1
  153. package/dist-server/service/activity-approval/activity-approval.d.ts +29 -1
  154. package/dist-server/service/activity-approval/activity-approval.js +19 -0
  155. package/dist-server/service/activity-approval/activity-approval.js.map +1 -1
  156. package/dist-server/service/activity-approval/my-pending-approvals.d.ts +21 -0
  157. package/dist-server/service/activity-approval/my-pending-approvals.js +40 -0
  158. package/dist-server/service/activity-approval/my-pending-approvals.js.map +1 -0
  159. package/dist-server/service/activity-instance/activity-instance-type.d.ts +20 -0
  160. package/dist-server/service/activity-instance/activity-instance-type.js +11 -0
  161. package/dist-server/service/activity-instance/activity-instance-type.js.map +1 -1
  162. package/dist-server/service/activity-instance/activity-instance.d.ts +6 -0
  163. package/dist-server/service/activity-instance/activity-instance.js +5 -0
  164. package/dist-server/service/activity-instance/activity-instance.js.map +1 -1
  165. package/dist-server/service/activity-stats/activity-stats-query.d.ts +5 -0
  166. package/dist-server/service/activity-stats/activity-stats-query.js +149 -11
  167. package/dist-server/service/activity-stats/activity-stats-query.js.map +1 -1
  168. package/dist-server/service/activity-stats/activity-stats-type.d.ts +19 -6
  169. package/dist-server/service/activity-stats/activity-stats-type.js +37 -10
  170. package/dist-server/service/activity-stats/activity-stats-type.js.map +1 -1
  171. package/dist-server/service/activity-stats/event-placement.d.ts +55 -0
  172. package/dist-server/service/activity-stats/event-placement.js +82 -0
  173. package/dist-server/service/activity-stats/event-placement.js.map +1 -0
  174. package/dist-server/service/activity-thread/activity-thread-query.js +48 -24
  175. package/dist-server/service/activity-thread/activity-thread-query.js.map +1 -1
  176. package/dist-server/service/activity-thread/my-open-threads.d.ts +15 -0
  177. package/dist-server/service/activity-thread/my-open-threads.js +40 -0
  178. package/dist-server/service/activity-thread/my-open-threads.js.map +1 -0
  179. package/dist-server/service/activity-thread/todo-order.d.ts +49 -0
  180. package/dist-server/service/activity-thread/todo-order.js +70 -0
  181. package/dist-server/service/activity-thread/todo-order.js.map +1 -0
  182. package/dist-server/service/installable-activity/installable-activity-mutation.d.ts +6 -0
  183. package/dist-server/service/installable-activity/installable-activity-mutation.js +7 -24
  184. package/dist-server/service/installable-activity/installable-activity-mutation.js.map +1 -1
  185. package/dist-server/service/nulls-last.d.ts +30 -0
  186. package/dist-server/service/nulls-last.js +37 -0
  187. package/dist-server/service/nulls-last.js.map +1 -0
  188. package/dist-server/tsconfig.tsbuildinfo +1 -1
  189. package/helps/dataset/activity-instance-search-page.ko.md +5 -0
  190. package/helps/dataset/activity-instance-search-page.md +13 -0
  191. package/helps/worklist/activity-approval.ko.md +5 -0
  192. package/helps/worklist/activity-approval.md +14 -0
  193. package/helps/worklist/activity-bank-page.ko.md +5 -0
  194. package/helps/worklist/activity-bank-page.md +13 -0
  195. package/helps/worklist/activity-reporter-list.ko.md +5 -0
  196. package/helps/worklist/activity-reporter-list.md +13 -0
  197. package/helps/worklist/activity-store.ko.md +5 -0
  198. package/helps/worklist/activity-store.md +13 -0
  199. package/helps/worklist/activity-template.ko.md +5 -0
  200. package/helps/worklist/activity-template.md +13 -0
  201. package/helps/worklist/installable-activity.ko.md +5 -0
  202. package/helps/worklist/installable-activity.md +13 -0
  203. package/package.json +13 -12
  204. package/things-factory.config.js +0 -5
  205. package/translations/en.json +129 -51
  206. package/translations/ja.json +130 -52
  207. package/translations/ko.json +82 -4
  208. package/translations/ms.json +129 -51
  209. package/translations/zh.json +132 -54
  210. package/dist-client/pages/dashboard/dashboard-home.d.ts +0 -1
  211. package/dist-client/pages/dashboard/dashboard-home.js +0 -22
  212. package/dist-client/pages/dashboard/dashboard-home.js.map +0 -1
  213. package/dist-client/pages/worklist-home.d.ts +0 -1
  214. package/dist-client/pages/worklist-home.js +0 -17
  215. package/dist-client/pages/worklist-home.js.map +0 -1
@@ -2,149 +2,73 @@ import { __decorate, __metadata } from "tslib";
2
2
  import '@operato/property-editor/ox-properties-dynamic-view.js';
3
3
  import '@things-factory/organization/dist-client/component/approval-line-brief.js';
4
4
  import '../templates/activity-approval-context-template.js';
5
- import { css, html, LitElement } from 'lit';
5
+ import { html, LitElement } from 'lit';
6
6
  import { customElement, property } from 'lit/decorators.js';
7
7
  import { i18next, localize } from '@operato/i18n';
8
- import { toggleOverlay } from '@operato/layout';
8
+ import { openPopup } from '@operato/layout';
9
+ import { StateRibonStyles } from './state-ribon.js';
9
10
  let ActivityApprovalRibon = class ActivityApprovalRibon extends localize(i18next)(LitElement) {
10
- static { this.styles = [
11
- css `
12
- :host {
13
- display: flex;
14
- background-color: var(--worklist-status-color);
15
- color: var(--md-sys-color-on-primary);
16
- align-items: center;
17
- min-height: 44px;
18
- --fontsize-small: 12px;
19
- }
20
-
21
- div {
22
- border-left: var(--border-dim-color);
23
- padding: var(--spacing-small) var(--spacing-medium);
24
- align-self: center;
25
- }
26
-
27
- :host([state='assigned']) {
28
- --worklist-status-color: #5f7184;
29
- }
30
-
31
- :host([state='started']) {
32
- --worklist-status-color: #56af45;
33
- }
34
-
35
- :host([state='delegated']) {
36
- --worklist-status-color: #8654b0;
37
- }
38
-
39
- :host([state='submitted']) {
40
- --worklist-status-color: #428df3;
41
- }
42
-
43
- :host([state='escalated']) {
44
- --worklist-status-color: #595de5;
45
- }
46
-
47
- :host([state='rejected']) {
48
- --worklist-status-color: #f27429;
49
- }
50
-
51
- :host([state='ended']) {
52
- --worklist-status-color: #02acae;
53
- }
54
-
55
- :host([state='aborted']) {
56
- --worklist-status-color: #cb3a33;
57
- }
58
-
59
- md-icon {
60
- font-size: 24px;
61
- }
62
-
63
- [desc] {
64
- flex: 2;
65
- font-size: var(--fontsize-small);
66
- }
67
-
68
- [desc] * {
69
- vertical-align: middle;
70
- }
71
-
72
- [assignee] {
73
- text-align: center;
74
- font-size: var(--fontsize-small);
75
- line-height: 1.3;
76
- }
77
-
78
- [assignee] span {
79
- display: block;
80
- font-size: var(--fontsize-large);
81
- font-weight: bold;
82
- }
83
-
84
- approval-line-brief {
85
- background-color: var(--worklist-status-color);
86
- font-size: var(--fontsize-small);
87
- }
88
-
89
- [state] {
90
- text-align: right;
91
- font-size: var(--fontsize-small);
92
- }
93
-
94
- @media only screen and (max-width: 460px) {
95
- div {
96
- border-left: none;
97
- }
98
-
99
- [assignee] {
100
- display: none;
101
- }
102
- md-icon {
103
- padding: 0;
104
- }
105
- approval-line-brief {
106
- display: none;
107
- }
108
- }
109
- `
110
- ]; }
11
+ static { this.styles = [StateRibonStyles]; }
111
12
  render() {
112
13
  const { activityInstance, state } = this.activityApproval?.activityThread || {};
113
14
  const { description, assignee, approvalLine, activity } = activityInstance || {};
114
15
  const { startingType } = activity || {};
115
16
  return html `
116
- <div desc>
117
- <md-icon
118
- @click=${() => {
119
- toggleOverlay('activity-info-overlay', {
120
- template: html `<activity-approval-context-template
121
- .activityApproval=${this.activityApproval}
122
- state=${state}
123
- ></activity-approval-context-template>`
124
- });
17
+ <span class="state">
18
+ <i class="dot"></i>
19
+ ${i18next.t('label.activity-state-' + state)}
20
+ </span>
21
+
22
+ <md-icon
23
+ class="info"
24
+ role="button"
25
+ tabindex="0"
26
+ title=${String(i18next.t('label.approval-info'))}
27
+ @click=${() => this.openInfo(state)}
28
+ @keydown=${(e) => {
29
+ if (e.key === 'Enter' || e.key === ' ') {
30
+ e.preventDefault();
31
+ this.openInfo(state);
32
+ }
125
33
  }}
126
- >description</md-icon
127
- >
128
- ${description}
129
- </div>
34
+ >info</md-icon
35
+ >
36
+
37
+ <span class="desc" title=${description || ''}>${description || ''}</span>
38
+
39
+
130
40
  ${startingType == 'issue' && assignee
131
- ? html `
132
- <div assignee>
133
- ${i18next.t('field.assignee')}
134
- <span>${assignee?.name}&nbsp;</span>
135
- </div>
136
- `
137
- : html ``}
41
+ ? html `<span class="who"><span class="who-label">${i18next.t('field.assignee')}</span>${assignee?.name}</span>`
42
+ : ''}
138
43
  ${approvalLine
139
44
  ? html `<approval-line-brief
140
45
  .model=${approvalLine}
141
46
  .current=${this.activityApproval.order}
142
47
  ></approval-line-brief>`
143
- : html ``}
144
-
145
- <div state>${i18next.t('label.activity-state-' + state)}</div>
48
+ : ''}
146
49
  `;
147
50
  }
51
+ /**
52
+ * 이 건의 기본 정보 — **팝업으로 연다.**
53
+ *
54
+ * ── 왜 aside 가 아닌가 (2026-09-06) ────────────────────────────────────
55
+ * 전에는 화면 오른쪽에서 나오는 aside 판이었다. 두 가지가 걸린다.
56
+ *
57
+ * 자리를 다툰다 화면 오른쪽 세로 판은 operato-twin 의 twin-entity360 이 쓰는 자리다
58
+ * (position: fixed · right: 0 · height: 100vh). 같은 자리에 둘이 선다.
59
+ * 전역 슬롯이다 VIEWPART_POSITION.ASIDEBAR 은 앱이 하나만 갖는다. 열 개가 넘는 패키지가
60
+ * 그 자리를 쓰고 있어(twin 의 AI 협의 도크 · board-ui · help · more-ui …),
61
+ * 이 판을 열면 그중 열려 있던 것이 닫힌다.
62
+ *
63
+ * 그리고 자리의 뜻이 다르다. 오른쪽 드로어는 **보고 있는 대상을 따라다니는 상세**이고(360 이
64
+ * 그것이다 — 포커스가 바뀌면 내용이 바뀌고 화면을 옮겨도 남는다), 이 판은 **이 화면의 이 건**에
65
+ * 대한 것이라 화면을 벗어나면 뜻이 없다. 페이지에 매인 것을 앱 전역 자리에 두지 않는다.
66
+ *
67
+ * 내용도 팝업 크기다 — 값 여덟 줄과 접힌 타임라인이라 세로 100vh 를 쓸 일이 없었다.
68
+ */
69
+ openInfo(state) {
70
+ openPopup(html `<activity-approval-context-template .activityApproval=${this.activityApproval} state=${state}></activity-approval-context-template>`, { title: i18next.t('label.approval-info') });
71
+ }
148
72
  updated(changes) {
149
73
  if (changes.has('activityApproval') && this.activityApproval) {
150
74
  const { state } = this.activityApproval.activityThread;
@@ -1 +1 @@
1
- {"version":3,"file":"activity-approval-ribon.js","sourceRoot":"","sources":["../../client/components/activity-approval-ribon.ts"],"names":[],"mappings":";AAAA,OAAO,wDAAwD,CAAA;AAC/D,OAAO,2EAA2E,CAAA;AAClF,OAAO,oDAAoD,CAAA;AAE3D,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAS,MAAM,mBAAmB,CAAA;AAElE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAGxC,IAAM,qBAAqB,GAA3B,MAAM,qBAAsB,SAAQ,QAAQ,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC;aAC/D,WAAM,GAAG;QACd,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAkGF;KACF,AApGY,CAoGZ;IAID,MAAM;QACJ,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,gBAAgB,EAAE,cAAc,IAAI,EAAE,CAAA;QAC/E,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,GAAG,gBAAgB,IAAI,EAAE,CAAA;QAChF,MAAM,EAAE,YAAY,EAAE,GAAG,QAAQ,IAAI,EAAE,CAAA;QAEvC,OAAO,IAAI,CAAA;;;mBAGI,GAAG,EAAE;YACZ,aAAa,CAAC,uBAAuB,EAAE;gBACrC,QAAQ,EAAE,IAAI,CAAA;oCACQ,IAAI,CAAC,gBAAgB;wBACjC,KAAK;qDACwB;aACxC,CAAC,CAAA;QACJ,CAAC;;;UAGD,WAAW;;QAEb,YAAY,IAAI,OAAO,IAAI,QAAQ;YACnC,CAAC,CAAC,IAAI,CAAA;;gBAEE,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC;sBACrB,QAAQ,EAAE,IAAI;;WAEzB;YACH,CAAC,CAAC,IAAI,CAAA,EAAE;QACR,YAAY;YACZ,CAAC,CAAC,IAAI,CAAA;qBACO,YAAY;uBACV,IAAI,CAAC,gBAAgB,CAAC,KAAK;kCAChB;YAC1B,CAAC,CAAC,IAAI,CAAA,EAAE;;mBAEG,OAAO,CAAC,CAAC,CAAC,uBAAuB,GAAG,KAAK,CAAC;KACxD,CAAA;IACH,CAAC;IAED,OAAO,CAAC,OAAO;QACb,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC7D,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,gBAAgB,CAAC,cAAc,CAAA;YAEtD,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QACnC,CAAC;IACH,CAAC;;AA/C2B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;+DAAsB;AAvGtC,qBAAqB;IADjC,aAAa,CAAC,yBAAyB,CAAC;GAC5B,qBAAqB,CAuJjC","sourcesContent":["import '@operato/property-editor/ox-properties-dynamic-view.js'\nimport '@things-factory/organization/dist-client/component/approval-line-brief.js'\nimport '../templates/activity-approval-context-template.js'\n\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { toggleOverlay } from '@operato/layout'\n\n@customElement('activity-approval-ribon')\nexport class ActivityApprovalRibon extends localize(i18next)(LitElement) {\n static styles = [\n css`\n :host {\n display: flex;\n background-color: var(--worklist-status-color);\n color: var(--md-sys-color-on-primary);\n align-items: center;\n min-height: 44px;\n --fontsize-small: 12px;\n }\n\n div {\n border-left: var(--border-dim-color);\n padding: var(--spacing-small) var(--spacing-medium);\n align-self: center;\n }\n\n :host([state='assigned']) {\n --worklist-status-color: #5f7184;\n }\n\n :host([state='started']) {\n --worklist-status-color: #56af45;\n }\n\n :host([state='delegated']) {\n --worklist-status-color: #8654b0;\n }\n\n :host([state='submitted']) {\n --worklist-status-color: #428df3;\n }\n\n :host([state='escalated']) {\n --worklist-status-color: #595de5;\n }\n\n :host([state='rejected']) {\n --worklist-status-color: #f27429;\n }\n\n :host([state='ended']) {\n --worklist-status-color: #02acae;\n }\n\n :host([state='aborted']) {\n --worklist-status-color: #cb3a33;\n }\n\n md-icon {\n font-size: 24px;\n }\n\n [desc] {\n flex: 2;\n font-size: var(--fontsize-small);\n }\n\n [desc] * {\n vertical-align: middle;\n }\n\n [assignee] {\n text-align: center;\n font-size: var(--fontsize-small);\n line-height: 1.3;\n }\n\n [assignee] span {\n display: block;\n font-size: var(--fontsize-large);\n font-weight: bold;\n }\n\n approval-line-brief {\n background-color: var(--worklist-status-color);\n font-size: var(--fontsize-small);\n }\n\n [state] {\n text-align: right;\n font-size: var(--fontsize-small);\n }\n\n @media only screen and (max-width: 460px) {\n div {\n border-left: none;\n }\n\n [assignee] {\n display: none;\n }\n md-icon {\n padding: 0;\n }\n approval-line-brief {\n display: none;\n }\n }\n `\n ]\n\n @property({ type: Object }) activityApproval: any\n\n render() {\n const { activityInstance, state } = this.activityApproval?.activityThread || {}\n const { description, assignee, approvalLine, activity } = activityInstance || {}\n const { startingType } = activity || {}\n\n return html`\n <div desc>\n <md-icon\n @click=${() => {\n toggleOverlay('activity-info-overlay', {\n template: html`<activity-approval-context-template\n .activityApproval=${this.activityApproval}\n state=${state}\n ></activity-approval-context-template>`\n })\n }}\n >description</md-icon\n >\n ${description}\n </div>\n ${startingType == 'issue' && assignee\n ? html`\n <div assignee>\n ${i18next.t('field.assignee')}\n <span>${assignee?.name}&nbsp;</span>\n </div>\n `\n : html``}\n ${approvalLine\n ? html`<approval-line-brief\n .model=${approvalLine}\n .current=${this.activityApproval.order}\n ></approval-line-brief>`\n : html``}\n\n <div state>${i18next.t('label.activity-state-' + state)}</div>\n `\n }\n\n updated(changes) {\n if (changes.has('activityApproval') && this.activityApproval) {\n const { state } = this.activityApproval.activityThread\n\n this.setAttribute('state', state)\n }\n }\n}\n"]}
1
+ {"version":3,"file":"activity-approval-ribon.js","sourceRoot":"","sources":["../../client/components/activity-approval-ribon.ts"],"names":[],"mappings":";AAAA,OAAO,wDAAwD,CAAA;AAC/D,OAAO,2EAA2E,CAAA;AAClF,OAAO,oDAAoD,CAAA;AAE3D,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AACtC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAE3D,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAG5C,IAAM,qBAAqB,GAA3B,MAAM,qBAAsB,SAAQ,QAAQ,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC;aAC/D,WAAM,GAAG,CAAC,gBAAgB,CAAC,AAArB,CAAqB;IAIlC,MAAM;QACJ,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,gBAAgB,EAAE,cAAc,IAAI,EAAE,CAAA;QAC/E,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,GAAG,gBAAgB,IAAI,EAAE,CAAA;QAChF,MAAM,EAAE,YAAY,EAAE,GAAG,QAAQ,IAAI,EAAE,CAAA;QAEvC,OAAO,IAAI,CAAA;;;UAGL,OAAO,CAAC,CAAC,CAAC,uBAAuB,GAAG,KAAK,CAAC;;;;;;;gBAOpC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC;iBACvC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;mBACxB,CAAC,CAAgB,EAAE,EAAE;YAC9B,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;gBACvC,CAAC,CAAC,cAAc,EAAE,CAAA;gBAClB,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;YACtB,CAAC;QACH,CAAC;;;;iCAIwB,WAAW,IAAI,EAAE,IAAI,WAAW,IAAI,EAAE;;;QAG/D,YAAY,IAAI,OAAO,IAAI,QAAQ;YACnC,CAAC,CAAC,IAAI,CAAA,6CAA6C,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC,UAAU,QAAQ,EAAE,IAAI,SAAS;YAC/G,CAAC,CAAC,EAAE;QACJ,YAAY;YACZ,CAAC,CAAC,IAAI,CAAA;qBACO,YAAY;uBACV,IAAI,CAAC,gBAAgB,CAAC,KAAK;kCAChB;YAC1B,CAAC,CAAC,EAAE;KACP,CAAA;IACH,CAAC;IAED;;;;;;;;;;;;;;;;;OAiBG;IACK,QAAQ,CAAC,KAAa;QAC5B,SAAS,CACP,IAAI,CAAA,yDAAyD,IAAI,CAAC,gBAAgB,UAAU,KAAK,wCAAwC,EACzI,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,qBAAqB,CAAC,EAAE,CAC5C,CAAA;IACH,CAAC;IAED,OAAO,CAAC,OAAO;QACb,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC7D,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,gBAAgB,CAAC,cAAc,CAAA;YAEtD,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QACnC,CAAC;IACH,CAAC;;AA1E2B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;+DAAsB;AAHtC,qBAAqB;IADjC,aAAa,CAAC,yBAAyB,CAAC;GAC5B,qBAAqB,CA8EjC","sourcesContent":["import '@operato/property-editor/ox-properties-dynamic-view.js'\nimport '@things-factory/organization/dist-client/component/approval-line-brief.js'\nimport '../templates/activity-approval-context-template.js'\n\nimport { html, LitElement } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { openPopup } from '@operato/layout'\n\nimport { StateRibonStyles } from './state-ribon.js'\n\n@customElement('activity-approval-ribon')\nexport class ActivityApprovalRibon extends localize(i18next)(LitElement) {\n static styles = [StateRibonStyles]\n\n @property({ type: Object }) activityApproval: any\n\n render() {\n const { activityInstance, state } = this.activityApproval?.activityThread || {}\n const { description, assignee, approvalLine, activity } = activityInstance || {}\n const { startingType } = activity || {}\n\n return html`\n <span class=\"state\">\n <i class=\"dot\"></i>\n ${i18next.t('label.activity-state-' + state)}\n </span>\n\n <md-icon\n class=\"info\"\n role=\"button\"\n tabindex=\"0\"\n title=${String(i18next.t('label.approval-info'))}\n @click=${() => this.openInfo(state)}\n @keydown=${(e: KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n this.openInfo(state)\n }\n }}\n >info</md-icon\n >\n\n <span class=\"desc\" title=${description || ''}>${description || ''}</span>\n\n\n ${startingType == 'issue' && assignee\n ? html`<span class=\"who\"><span class=\"who-label\">${i18next.t('field.assignee')}</span>${assignee?.name}</span>`\n : ''}\n ${approvalLine\n ? html`<approval-line-brief\n .model=${approvalLine}\n .current=${this.activityApproval.order}\n ></approval-line-brief>`\n : ''}\n `\n }\n\n /**\n * 이 건의 기본 정보 — **팝업으로 연다.**\n *\n * ── 왜 aside 가 아닌가 (2026-09-06) ────────────────────────────────────\n * 전에는 화면 오른쪽에서 나오는 aside 판이었다. 두 가지가 걸린다.\n *\n * 자리를 다툰다 화면 오른쪽 세로 판은 operato-twin twin-entity360 쓰는 자리다\n * (position: fixed · right: 0 · height: 100vh). 같은 자리에 둘이 선다.\n * 전역 슬롯이다 VIEWPART_POSITION.ASIDEBAR 은 앱이 하나만 갖는다. 열 개가 넘는 패키지가\n * 그 자리를 쓰고 있어(twin AI 협의 도크 · board-ui · help · more-ui …),\n * 이 판을 열면 그중 열려 있던 것이 닫힌다.\n *\n * 그리고 자리의 뜻이 다르다. 오른쪽 드로어는 **보고 있는 대상을 따라다니는 상세**이고(360 이\n * 그것이다 포커스가 바뀌면 내용이 바뀌고 화면을 옮겨도 남는다), 판은 **이 화면의 건**에\n * 대한 것이라 화면을 벗어나면 뜻이 없다. 페이지에 매인 것을 앱 전역 자리에 두지 않는다.\n *\n * 내용도 팝업 크기다 — 값 여덟 줄과 접힌 타임라인이라 세로 100vh 를 쓸 일이 없었다.\n */\n private openInfo(state: string) {\n openPopup(\n html`<activity-approval-context-template .activityApproval=${this.activityApproval} state=${state}></activity-approval-context-template>`,\n { title: i18next.t('label.approval-info') }\n )\n }\n\n updated(changes) {\n if (changes.has('activityApproval') && this.activityApproval) {\n const { state } = this.activityApproval.activityThread\n\n this.setAttribute('state', state)\n }\n }\n}\n"]}
@@ -0,0 +1,50 @@
1
+ import '@material/web/icon/icon.js';
2
+ /**
3
+ * 업무 항목을 화면에 내는 방식 — **두 화면이 한 벌을 쓴다.**
4
+ *
5
+ * 업무 상세(activity-thread-page)와 결재 상세(activity-approval-page)는 거의 복사본이다. 항목을
6
+ * 고르는 함수, 화면 종류에 따라 그리는 분기, 절을 그리는 부분이 두 파일에 같은 코드로 있었다.
7
+ * 그래서 한쪽만 고치면 나머지가 그대로 남는다 — 실제로 그랬다(2026-09-06, 상세 화면의
8
+ * Input · Output 영문 legend 를 고쳤는데 결재 화면에는 그대로 있었다).
9
+ *
10
+ * 화면마다 다른 것은 남긴다 — 상세는 결과를 적고, 결재는 읽기만 한다.
11
+ */
12
+ /** 절과 값 표의 모양. 카드 값은 SurfaceStyles 의 .ui-card 와 같다. */
13
+ export declare const ActivityFieldsStyles: import("lit").CSSResult;
14
+ /** 받은 값 — 업무를 시작할 때 함께 온 것. */
15
+ export declare function inputSpecOf(model: any[] | null | undefined): any[];
16
+ /** 적어 낼 값 — 수행자가 채우는 것. */
17
+ export declare function outputSpecOf(model: any[] | null | undefined): any[];
18
+ /**
19
+ * 값을 **표로** 보인다. 입력칸으로 그리지 않는다.
20
+ *
21
+ * 전에는 읽기만 하는 절도 ox-properties-dynamic-view 로 그렸다. 그러면 값마다 흰 입력 상자가
22
+ * 생기고 사용자는 고쳐 쓸 수 있는 것으로 읽는다. 그런데 그 절에는 변경을 받는 자리가 없다 —
23
+ * 결재 화면은 아예 output 을 mutation 에 싣지 않는다(approveActivityApproval 은 id 와 comment 만
24
+ * 보낸다). 「적을 수 있게 보이는데 적히지 않는 칸」이라 가장 나쁜 쪽이다.
25
+ */
26
+ export declare function renderGiven(specs: any[], values: any): import("lit-html").TemplateResult<1>;
27
+ /**
28
+ * 항목 이름 — ox-property-editor 와 같은 규칙으로 찾는다(그쪽은 label. 접두사를 스스로 붙인다).
29
+ *
30
+ * 번역이 없으면 **키를 그대로 보이지 않는다.** 전에는 label.because 가 없어서 화면에
31
+ * Label.Because 가 나왔다. 사용자에게 뜻이 없는 글자다.
32
+ *
33
+ * description 으로 물러나지도 않는다 — 액티비티의 description 은 개발자가 영어로 적어 둔 설명이다
34
+ * (예: "The disposition waiting for approval"). 한국어 화면에 영어 문장이 나오는 것보다 항목
35
+ * 이름이 낫다.
36
+ */
37
+ export declare function fieldLabel(spec: any): string;
38
+ /**
39
+ * 값 한 칸.
40
+ *
41
+ * ── 오브젝트를 한 줄 JSON 으로 내지 않는다 (2026-09-07) ─────────────────────
42
+ * `JSON.stringify` 한 줄이었다. 트윈의 작동 명령을 열면 그 한 줄이 700자가 넘어서, 「업무 내용」이
43
+ * 읽을 수 없는 글자 덩어리 하나가 됐다. 값은 다 있는데 아무것도 읽히지 않는다.
44
+ *
45
+ * 화면은 이미 이름과 값을 두 칸으로 보이는 말을 갖고 있다(dl.given). 오브젝트도 그 말로 편다 —
46
+ * 새 표현을 만들지 않고, 안쪽이든 바깥쪽이든 같은 모양으로 읽힌다.
47
+ *
48
+ * 값이 없으면 빈 칸으로 두지 않는다 — 빈 칸은 값이 없는 것인지 화면이 못 낸 것인지 모른다.
49
+ */
50
+ export declare function fieldValue(value: any, depth?: number): unknown;
@@ -0,0 +1,393 @@
1
+ import '@material/web/icon/icon.js';
2
+ import { css, html } from 'lit';
3
+ import { i18next } from '@operato/i18n';
4
+ /**
5
+ * 업무 항목을 화면에 내는 방식 — **두 화면이 한 벌을 쓴다.**
6
+ *
7
+ * 업무 상세(activity-thread-page)와 결재 상세(activity-approval-page)는 거의 복사본이다. 항목을
8
+ * 고르는 함수, 화면 종류에 따라 그리는 분기, 절을 그리는 부분이 두 파일에 같은 코드로 있었다.
9
+ * 그래서 한쪽만 고치면 나머지가 그대로 남는다 — 실제로 그랬다(2026-09-06, 상세 화면의
10
+ * Input · Output 영문 legend 를 고쳤는데 결재 화면에는 그대로 있었다).
11
+ *
12
+ * 화면마다 다른 것은 남긴다 — 상세는 결과를 적고, 결재는 읽기만 한다.
13
+ */
14
+ /** 절과 값 표의 모양. 카드 값은 SurfaceStyles 의 .ui-card 와 같다. */
15
+ export const ActivityFieldsStyles = css `
16
+ section {
17
+ margin: var(--spacing-large, 16px) var(--spacing-large, 16px) 0;
18
+ padding: 14px;
19
+ background: var(--ui-panel, #ffffff);
20
+ border: 1px solid var(--ui-line, #d5dce0);
21
+ border-radius: var(--ui-r, 10px);
22
+ }
23
+
24
+ section > h2 {
25
+ margin: 0 0 10px;
26
+ font-size: var(--ui-t-title, 15px);
27
+ font-weight: 600;
28
+ color: var(--ui-ink, #17242c);
29
+ }
30
+
31
+ /* 읽기만 하는 절 — 값을 바꿀 수 없다는 것이 보여야 한다. */
32
+ section[readonly] > h2 {
33
+ color: var(--ui-muted, #57676f);
34
+ }
35
+
36
+ /* 받은 값 — 이름과 값을 두 칸으로. 이름은 오른쪽 정렬이라 값의 왼쪽 끝이 한 줄로 선다. */
37
+ dl.given {
38
+ display: grid;
39
+ grid-template-columns: minmax(80px, max-content) 1fr;
40
+ gap: 7px var(--spacing-large, 16px);
41
+ margin: 0;
42
+ }
43
+
44
+ dl.given dt {
45
+ text-align: right;
46
+ color: var(--ui-muted, #57676f);
47
+ word-break: keep-all;
48
+ }
49
+
50
+ dl.given dd {
51
+ margin: 0;
52
+ color: var(--ui-ink, #17242c);
53
+ word-break: break-word;
54
+ }
55
+
56
+ dl.given .none {
57
+ color: var(--ui-faint, #8a959c);
58
+ }
59
+
60
+ dl.given code {
61
+ font-family: var(--ui-mono, ui-monospace, Menlo, monospace);
62
+ font-size: var(--ui-t-small, 11.5px);
63
+ }
64
+
65
+ /*
66
+ * 안쪽으로 펼친 오브젝트 · 배열.
67
+ *
68
+ * 바깥과 같은 두 칸 구조를 쓰되 **한 단 들여쓰고 왼쪽에 선을 긋는다.** 선이 없으면 세 단이 겹칠 때
69
+ * 어느 이름이 어느 값에 딸린 것인지 눈으로 못 가른다.
70
+ *
71
+ * 안쪽 이름은 왼쪽 정렬이다. 바깥은 오른쪽 정렬이라 값의 왼쪽 끝이 한 줄로 서는데(dl.given dt),
72
+ * 안쪽까지 그러면 단이 깊어질수록 이름이 값 쪽으로 밀려 붙는다.
73
+ */
74
+ dl.given .nested {
75
+ margin: 2px 0 0;
76
+ padding-left: 10px;
77
+ border-left: 1px solid var(--ui-line, #d5dce0);
78
+ }
79
+
80
+ dl.given dl.nested {
81
+ display: grid;
82
+ grid-template-columns: minmax(60px, max-content) 1fr;
83
+ gap: 4px var(--spacing-medium, 8px);
84
+ }
85
+
86
+ dl.given dl.nested dt {
87
+ text-align: left;
88
+ color: var(--ui-faint, #8a959c);
89
+ font-family: var(--ui-mono, ui-monospace, Menlo, monospace);
90
+ font-size: var(--ui-t-small, 11.5px);
91
+ }
92
+
93
+ /*
94
+ * 페이로드를 그대로 가져가는 자리.
95
+ *
96
+ * 화면은 값을 읽기 좋게 펴 놓지만, 이 값을 **다른 도구에 넣어 볼 일**이 있다 — 트윈 명령 하나가
97
+ * 네 단짜리 오브젝트이고, 그것을 손으로 옮겨 적을 수는 없다. 화면이 편 글자가 아니라 원문 JSON 을
98
+ * 준다. 붙여 넣는 쪽이 기대하는 것은 원문이다.
99
+ *
100
+ * 값 위에 얹지 않고 오른쪽 위 구석에 둔다 — 값이 한 줄일 때도 두 단 깊을 때도 자리가 같다.
101
+ */
102
+ dl.given dd:has(> .payload) {
103
+ position: relative;
104
+ }
105
+
106
+ .copy {
107
+ position: absolute;
108
+ top: 0;
109
+ right: 0;
110
+ display: inline-flex;
111
+ align-items: center;
112
+ gap: 3px;
113
+ padding: 1px 5px;
114
+ border: 1px solid var(--ui-line, #d5dce0);
115
+ border-radius: 999px;
116
+ background: var(--ui-panel, #ffffff);
117
+ color: var(--ui-muted, #57676f);
118
+ font-size: var(--ui-t-micro, 11px);
119
+ line-height: 16px;
120
+ cursor: pointer;
121
+ }
122
+
123
+ .copy md-icon {
124
+ font-size: 13px;
125
+ width: 13px;
126
+ height: 13px;
127
+ }
128
+
129
+ .copy:hover {
130
+ color: var(--ui-ink, #17242c);
131
+ border-color: var(--ui-muted, #57676f);
132
+ }
133
+
134
+ .copy:focus-visible {
135
+ outline: 2px solid var(--ui-accent, #00727e);
136
+ outline-offset: 1px;
137
+ }
138
+
139
+ /* 복사한 직후 — 눌린 것이 보여야 한다. 소리도 움직임도 없는 조작이라 이 표시가 유일한 대답이다. */
140
+ .copy[done] {
141
+ border-color: var(--ui-good, #297d39);
142
+ color: var(--ui-good, #297d39);
143
+ }
144
+
145
+ /*
146
+ * 못 했을 때도 **같은 크기로** 말한다.
147
+ *
148
+ * 처음에는 실패를 title 로만 적었다. 그러면 누른 사람 눈에는 아무 일도 안 일어난 것과 같고,
149
+ * 이유를 알려면 그 자리에 마우스를 올려 두어야 한다 — 손가락으로 쓰는 화면에는 그 길이 없다.
150
+ * 클립보드는 브라우저가 막을 수 있는 자리라(보안 컨텍스트가 아니거나 권한이 없으면) 실제로 나는 일이다.
151
+ */
152
+ .copy[failed] {
153
+ border-color: var(--ui-crit, #d14946);
154
+ color: var(--ui-crit, #d14946);
155
+ }
156
+
157
+ /* 배열 — 번호로 몇 번째인지 말한다. 순서가 뜻을 갖는 값이 온다(주의 목록 · 권장 조치). */
158
+ dl.given ol.nested {
159
+ list-style: decimal;
160
+ padding-left: 24px;
161
+ }
162
+
163
+ dl.given ol.nested li {
164
+ margin: 2px 0;
165
+ }
166
+
167
+ dl.given ol.nested li::marker {
168
+ color: var(--ui-faint, #8a959c);
169
+ font-size: var(--ui-t-small, 11.5px);
170
+ }
171
+
172
+ /*
173
+ * 빈 화면은 가장 중요한 자리다. 없다고만 하지 않고 왜 비어 있는지 적는다.
174
+ *
175
+ * 두 가지가 있다. 화면 전체가 비는 것(건을 못 읽었다)은 세로로 늘려 가운데에 두고, 절 자리가
176
+ * 비는 것(항목이 정의되지 않았다)은 **다른 절과 같은 카드**로 둔다. 절 하나가 없다고 해서
177
+ * 화면 높이를 다 쓰면 그 아래의 타임라인이 화면 밖으로 밀린다.
178
+ */
179
+ section[empty] {
180
+ display: flex;
181
+ flex: none;
182
+ flex-direction: column;
183
+ align-items: center;
184
+ gap: 6px;
185
+ padding: 28px 14px;
186
+ text-align: center;
187
+ color: var(--ui-muted, #57676f);
188
+ }
189
+
190
+ div[empty] {
191
+ flex: 1;
192
+ display: flex;
193
+ flex-direction: column;
194
+ align-items: center;
195
+ justify-content: center;
196
+ gap: 6px;
197
+ padding: var(--spacing-large, 16px);
198
+ text-align: center;
199
+ color: var(--ui-muted, #57676f);
200
+ }
201
+
202
+ [empty] .title {
203
+ font-size: var(--ui-t-title, 15px);
204
+ font-weight: 600;
205
+ color: var(--ui-ink, #17242c);
206
+ }
207
+
208
+ [empty] .why {
209
+ max-width: 40em;
210
+ font-size: var(--ui-t-body, 13px);
211
+ color: var(--ui-faint, #8a959c);
212
+ word-break: keep-all;
213
+ }
214
+ `;
215
+ /**
216
+ * 화면에 낼 항목을 고른다.
217
+ *
218
+ * hidden 을 지킨다. 전에는 이 선언을 보지 않았다 — 액티비티가 「이 항목은 보이지 않는다」고 적어
219
+ * 두어도 화면에 나왔다. 편집기가 없는 타입(object 등)이라 조용히 빠졌을 뿐이고, 편집기가 있는
220
+ * 타입이면 그대로 보였다. 값을 표로 그리기 시작하면서 실제로 드러났다 — 처분 승인 화면에
221
+ * disposition 객체의 JSON 원문이 나왔다.
222
+ */
223
+ function specsOf(model, inouts) {
224
+ return (model || [])
225
+ .filter(item => !item.hidden)
226
+ .filter(item => inouts.includes(item.inout))
227
+ .map(item => ({
228
+ ...item,
229
+ name: item.tag,
230
+ label: item.tKey || item.name,
231
+ property: item.options
232
+ }));
233
+ }
234
+ /** 받은 값 — 업무를 시작할 때 함께 온 것. */
235
+ export function inputSpecOf(model) {
236
+ return specsOf(model, ['in', 'inout']);
237
+ }
238
+ /** 적어 낼 값 — 수행자가 채우는 것. */
239
+ export function outputSpecOf(model) {
240
+ return specsOf(model, ['out', 'inout']);
241
+ }
242
+ /**
243
+ * 값을 **표로** 보인다. 입력칸으로 그리지 않는다.
244
+ *
245
+ * 전에는 읽기만 하는 절도 ox-properties-dynamic-view 로 그렸다. 그러면 값마다 흰 입력 상자가
246
+ * 생기고 사용자는 고쳐 쓸 수 있는 것으로 읽는다. 그런데 그 절에는 변경을 받는 자리가 없다 —
247
+ * 결재 화면은 아예 output 을 mutation 에 싣지 않는다(approveActivityApproval 은 id 와 comment 만
248
+ * 보낸다). 「적을 수 있게 보이는데 적히지 않는 칸」이라 가장 나쁜 쪽이다.
249
+ */
250
+ export function renderGiven(specs, values) {
251
+ return html `
252
+ <dl class="given">
253
+ ${specs.map(spec => html `
254
+ <dt>${fieldLabel(spec)}</dt>
255
+ <dd>${fieldValue(values?.[spec.name])}</dd>
256
+ `)}
257
+ </dl>
258
+ `;
259
+ }
260
+ /**
261
+ * 항목 이름 — ox-property-editor 와 같은 규칙으로 찾는다(그쪽은 label. 접두사를 스스로 붙인다).
262
+ *
263
+ * 번역이 없으면 **키를 그대로 보이지 않는다.** 전에는 label.because 가 없어서 화면에
264
+ * Label.Because 가 나왔다. 사용자에게 뜻이 없는 글자다.
265
+ *
266
+ * description 으로 물러나지도 않는다 — 액티비티의 description 은 개발자가 영어로 적어 둔 설명이다
267
+ * (예: "The disposition waiting for approval"). 한국어 화면에 영어 문장이 나오는 것보다 항목
268
+ * 이름이 낫다.
269
+ */
270
+ export function fieldLabel(spec) {
271
+ const key = String(spec?.label ?? spec?.name ?? '');
272
+ const full = key.indexOf('label.') >= 0 ? key : 'label.' + key;
273
+ return i18next.t(full, { defaultValue: spec?.name || key });
274
+ }
275
+ /**
276
+ * 얼마나 깊이 펼치나.
277
+ *
278
+ * 이 화면들이 받는 값은 액티비티가 정하므로 얼마나 깊을지 알 수 없다. 트윈의 작동 명령은 네 단이다
279
+ * (twinNow → attentions[0] → params → 값). 끝없이 펼치면 어떤 페이로드에서 화면이 통째로 페이로드가
280
+ * 되므로, 이 깊이를 넘으면 원문을 그대로 보인다 — 접는 것이 아니라 **다른 방식으로 보이는 것**이다.
281
+ */
282
+ const MAX_DEPTH = 4;
283
+ /**
284
+ * 값 한 칸.
285
+ *
286
+ * ── 오브젝트를 한 줄 JSON 으로 내지 않는다 (2026-09-07) ─────────────────────
287
+ * `JSON.stringify` 한 줄이었다. 트윈의 작동 명령을 열면 그 한 줄이 700자가 넘어서, 「업무 내용」이
288
+ * 읽을 수 없는 글자 덩어리 하나가 됐다. 값은 다 있는데 아무것도 읽히지 않는다.
289
+ *
290
+ * 화면은 이미 이름과 값을 두 칸으로 보이는 말을 갖고 있다(dl.given). 오브젝트도 그 말로 편다 —
291
+ * 새 표현을 만들지 않고, 안쪽이든 바깥쪽이든 같은 모양으로 읽힌다.
292
+ *
293
+ * 값이 없으면 빈 칸으로 두지 않는다 — 빈 칸은 값이 없는 것인지 화면이 못 낸 것인지 모른다.
294
+ */
295
+ export function fieldValue(value, depth = 0) {
296
+ if (value === null || value === undefined || value === '') {
297
+ return html `<span class="none">${i18next.t('text.no-value')}</span>`;
298
+ }
299
+ if (typeof value === 'boolean') {
300
+ return i18next.t(value ? 'label.yes' : 'label.no', { defaultValue: String(value) });
301
+ }
302
+ if (typeof value !== 'object') {
303
+ return String(value);
304
+ }
305
+ /* 너무 깊으면 펼치기를 그만두고 원문을 보인다 — 없는 척하지 않는다. */
306
+ if (depth >= MAX_DEPTH) {
307
+ return html `<code>${JSON.stringify(value)}</code>`;
308
+ }
309
+ const laid = layOut(value, depth);
310
+ /*
311
+ * 맨 바깥 값에만 복사를 붙인다.
312
+ *
313
+ * 안쪽 단마다 달면 네 단짜리 페이로드에 단추가 열 개 넘게 붙어, 정작 읽으라고 편 값이 단추에
314
+ * 묻힌다. 사람이 가져가려는 것은 「이 항목」이다.
315
+ */
316
+ return depth === 0 ? html `<span class="payload">${copyButton(value)}${laid}</span>` : laid;
317
+ }
318
+ /**
319
+ * 이 값을 원문 그대로 가져간다.
320
+ *
321
+ * 화면이 편 글자가 아니라 JSON 을 준다 — 붙여 넣는 쪽(질의 · 로그 검색 · 문의 글)이 기대하는 것은
322
+ * 원문이다. 들여쓰기를 넣어 그대로 읽을 수 있게 한다.
323
+ *
324
+ * 눌러도 화면이 안 바뀌면 눌린 줄을 모른다. 그래서 잠깐 표시를 남긴다 — 이 조작에는 그것 말고
325
+ * 대답이 없다.
326
+ */
327
+ function copyButton(value) {
328
+ const onCopy = async (e) => {
329
+ const button = e.currentTarget;
330
+ const mark = (attr) => {
331
+ button.toggleAttribute(attr, true);
332
+ setTimeout(() => button.toggleAttribute(attr, false), 1500);
333
+ };
334
+ try {
335
+ await navigator.clipboard.writeText(JSON.stringify(value, null, 2));
336
+ }
337
+ catch {
338
+ /*
339
+ * 클립보드는 브라우저가 막을 수 있다(보안 컨텍스트가 아니거나 권한이 없으면). 안 된 것을 된
340
+ * 것처럼 말하지 않고, 성공과 같은 크기로 보인다 — title 만 바꾸면 누른 사람 눈에는 아무 일도
341
+ * 안 일어난 것과 같다.
342
+ */
343
+ button.setAttribute('title', String(i18next.t('text.copy-failed')));
344
+ mark('failed');
345
+ return;
346
+ }
347
+ mark('done');
348
+ };
349
+ return html `
350
+ <button class="copy" type="button" title=${String(i18next.t('text.copy-payload'))} @click=${onCopy}>
351
+ <md-icon>content_copy</md-icon>${i18next.t('button.copy')}
352
+ </button>
353
+ `;
354
+ }
355
+ /** 값을 이름·값 두 칸으로 편다. 복사 단추는 여기 끼지 않는다 — 편 결과만 만든다. */
356
+ function layOut(value, depth) {
357
+ if (Array.isArray(value)) {
358
+ /* 빈 배열은 「값 없음」이 아니다 — 「비어 있다」는 그 자체로 사실이다. */
359
+ if (value.length === 0) {
360
+ return html `<span class="none">${i18next.t('text.empty-list')}</span>`;
361
+ }
362
+ /*
363
+ * 배열은 깊이를 먹지 않는다 — **같은 것이 여러 개**이지 새로운 단이 아니다.
364
+ *
365
+ * 배열도 한 단으로 세었더니 트윈 작동 명령의 마지막 줄이 잘렸다: twinNow → attentions →
366
+ * attentions[0] → recommendedActions → { code } 에서 recommendedActions 의 항목이 다섯 번째가
367
+ * 되어, 정작 가장 단순한 { code: 'advice.raise-priority' } 하나가 JSON 원문으로 남았다.
368
+ * 사람이 세는 단은 넷이다.
369
+ */
370
+ return html `
371
+ <ol class="nested">
372
+ ${value.map(item => html `<li>${fieldValue(item, depth)}</li>`)}
373
+ </ol>
374
+ `;
375
+ }
376
+ const keys = Object.keys(value);
377
+ if (keys.length === 0) {
378
+ return html `<span class="none">${i18next.t('text.empty-object')}</span>`;
379
+ }
380
+ /*
381
+ * 안쪽 이름은 번역하지 않는다. 액티비티가 정한 키라 번역이 있을 리 없고, 없는 번역을 찾으면
382
+ * 「Label.MaterialDefinitionId」 같은 글자가 나온다 — 사용자에게 뜻이 없는 글자다.
383
+ */
384
+ return html `
385
+ <dl class="given nested">
386
+ ${keys.map(key => html `
387
+ <dt>${key}</dt>
388
+ <dd>${fieldValue(value[key], depth + 1)}</dd>
389
+ `)}
390
+ </dl>
391
+ `;
392
+ }
393
+ //# sourceMappingURL=activity-fields.js.map