@things-factory/worklist 10.0.18 → 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
@@ -4,159 +4,281 @@ import '@things-factory/organization/dist-client/component/approval-line-view.js
4
4
  import { html, css, LitElement, nothing } from 'lit';
5
5
  import { customElement, property } from 'lit/decorators.js';
6
6
  import { i18next } from '@operato/i18n';
7
+ import { whenText } from '../when.js';
7
8
  import { ActivityThread } from '../types/activity-thread.js';
8
- function getSafeFormatter(locale, options) {
9
- try {
10
- const safeLocale = locale || 'en-US';
11
- return new Intl.DateTimeFormat(safeLocale, options);
12
- }
13
- catch (e) {
14
- console.warn(`Invalid locale or options provided: ${locale}, falling back to default en-US.`, e);
15
- return new Intl.DateTimeFormat('en-US', options);
16
- }
9
+ /** 줄이 화면에 내보이는 시각. 정렬도 이 값으로 한다. */
10
+ function whenOf(item) {
11
+ const at = item?.terminatedAt ?? item?.updatedAt ?? item?.createdAt;
12
+ const ms = at === null || at === undefined || at === '' ? NaN : Date.parse(String(at));
13
+ return Number.isFinite(ms) ? ms : 0;
17
14
  }
18
- const formatter = getSafeFormatter(navigator.language, { dateStyle: 'full', timeStyle: 'short' });
19
15
  let ActivityThreadTimeline = class ActivityThreadTimeline extends LitElement {
20
16
  static { this.styles = [
21
17
  css `
18
+ /*
19
+ * 업무 하나가 지나온 자취 — **언제 · 무슨 일이 · 누구에 의해.**
20
+ *
21
+ * ── 무엇을 고쳤나 (2026-09-06) ─────────────────────────────────────
22
+ * 1. 절 제목이 단추처럼 생겼었다. border: 2px 에 border-radius: 15px 인 알약이라
23
+ * 누를 수 있는 것으로 보인다. 이 화면에서 알약은 거르개(chip)의 모양이다.
24
+ * 2. 말풍선 꼬리에 #f4f4f4 가, 표 선에 lightgray 가 하드코딩되어 있었다. 어두운
25
+ * 화면에서 밝은 회색이 그대로 나온다.
26
+ * 3. 시각이 Tuesday, September 1, 2026 at 11:51 AM 이었다. when.ts 머리말 참조.
27
+ *
28
+ * ── 내용이 없는 줄에 상자를 그리지 않는다 ─────────────────────────
29
+ * 의견이나 결과가 없는 줄에도 회색 상자가 min-height: 20px 으로 깔려 있었다. 저장 ·
30
+ * 게시처럼 적을 것이 없는 기록이 대부분이라, 화면의 절반이 빈 회색 상자였다.
31
+ */
32
+ /*
33
+ * 절 하나이므로 다른 절과 같은 카드로 둔다. 전에는 카드 밖에 그냥 놓여 있어서 「업무 정보」
34
+ * 카드와 위계가 어긋났다 — 같은 층의 두 절인데 하나만 면 위에 있었다.
35
+ */
22
36
  :host {
23
37
  display: block;
38
+ padding: 14px;
39
+ background: var(--ui-panel, #ffffff);
40
+ border: 1px solid var(--ui-line, #d5dce0);
41
+ border-radius: var(--ui-r, 10px);
42
+ color: var(--ui-ink, #17242c);
43
+ font-size: var(--ui-t-body, 13px);
24
44
  }
25
45
 
26
- [subtitle] {
27
- padding: var(--spacing-small) var(--spacing-medium);
28
- background-color: var(--md-sys-color-surface-variant);
29
- border: 2px solid var(--md-sys-color-outline);
30
- border-radius: 15px;
31
- color: var(--md-sys-color-on-surface);
32
- font-weight: bold;
46
+ /* 그릴 것이 없으면 빈 카드를 남기지 않는다. */
47
+ :host(:not(:has(ol))) {
48
+ display: none;
33
49
  }
34
50
 
35
- ol {
36
- list-style: none;
37
- margin: var(--spacing-medium) 0 0 var(--spacing-small);
38
- padding: 0;
51
+ /*
52
+ * ── 접어 둔다 (2026-09-06) ────────────────────────────────────────
53
+ * 이력은 참고다. 결재자가 먼저 읽는 것은 「무엇을 승인하나」이고, 이력은 그것으로 판단이
54
+ * 안 설 때 펴 보는 것이다. 그런데 이 카드가 내용 절과 똑같은 무게로 서 있었다.
55
+ *
56
+ * 접어 두되 **마지막에 무슨 일이 있었는지 한 줄은 남긴다.** 「이력 4건」만 있으면 펴 볼지
57
+ * 말지를 정할 수가 없다.
58
+ *
59
+ * 펴 두어야 하는 화면이 있다 — 지난 일을 보러 온 화면(완료 조회), 그리고 보일 항목이 하나도
60
+ * 없어서 「타임라인의 상신 사유를 보고 판단하세요」라고 안내한 경우다. 그때 접혀 있으면 그
61
+ * 안내가 거짓말이 된다. 그래서 여는 쪽은 담는 화면이 정한다(open 속성).
62
+ */
63
+ h2 {
64
+ margin: 0;
65
+ font-size: var(--ui-t-title, 15px);
66
+ font-weight: 600;
67
+ color: var(--ui-ink, #17242c);
39
68
  }
40
69
 
41
- li {
70
+ summary {
42
71
  display: flex;
43
- border: none;
72
+ align-items: baseline;
73
+ gap: var(--spacing-medium, 8px);
74
+ cursor: pointer;
75
+ list-style: none;
44
76
  }
45
77
 
46
- [info] {
47
- flex: 1;
48
- color: var(--md-sys-color-on-surface);
49
- background: var(--md-sys-color-surface);
78
+ summary::-webkit-details-marker {
79
+ display: none;
80
+ }
50
81
 
51
- div {
52
- float: right;
53
- opacity: 0.7;
54
- }
82
+ /* 펴고 접는 표시. 색만으로 말하지 않으려고 방향이 바뀐다. */
83
+ .chevron {
84
+ flex: none;
85
+ font-size: 18px;
86
+ color: var(--ui-muted, #57676f);
87
+ transition: transform 0.12s ease;
88
+ }
55
89
 
56
- md-icon {
57
- position: relative;
58
- top: 3px;
59
- font-size: var(--fontsize-large);
60
- }
90
+ details[open] .chevron {
91
+ transform: rotate(90deg);
92
+ }
61
93
 
62
- p {
63
- color: var(--md-sys-color-on-surface);
64
- background: var(--md-sys-color-surface-variant);
94
+ /* 접혔을 때 보이는 한 줄 — 마지막에 무슨 일이 있었나. */
95
+ .last {
96
+ flex: 1;
97
+ min-width: 0;
98
+ overflow: hidden;
99
+ text-overflow: ellipsis;
100
+ white-space: nowrap;
101
+ font-size: var(--ui-t-small, 11.5px);
102
+ color: var(--ui-faint, #8a959c);
103
+ }
65
104
 
66
- margin: var(--spacing-small) 0 var(--spacing-medium) 0;
67
- padding: var(--spacing-small) var(--spacing-medium);
68
- font-size: var(--fontsize-small);
69
- text-align: justify;
70
- min-height: 20px;
71
- }
105
+ details[open] .last {
106
+ display: none;
107
+ }
72
108
 
73
- p::before {
74
- content: '';
75
- float: right;
76
- margin-top: -10px;
77
- margin-right: 20px;
78
- border: 7px solid transparent;
79
- border-bottom-color: #f4f4f4;
80
- border-top: 0;
81
- }
109
+ .count {
110
+ flex: none;
111
+ font-size: var(--ui-t-small, 11.5px);
112
+ color: var(--ui-muted, #57676f);
113
+ font-variant-numeric: tabular-nums;
114
+ }
115
+
116
+ ol {
117
+ list-style: none;
118
+ margin: var(--spacing-medium, 8px) 0 0;
119
+ padding: 0;
82
120
  }
83
121
 
122
+ li {
123
+ display: grid;
124
+ grid-template-columns: max-content 16px 1fr;
125
+ align-items: start;
126
+ column-gap: var(--spacing-small, 4px);
127
+ }
128
+
129
+ /* 시각 — 자릿수를 맞춰 위아래를 바로 비교할 수 있게. */
84
130
  [date] {
85
- opacity: 0.7;
86
- flex: initial;
87
- width: 200px;
88
- max-width: 30%;
89
- font-size: var(--fontsize-small);
131
+ padding-top: 1px;
132
+ font-family: var(--ui-mono, ui-monospace, Menlo, monospace);
133
+ font-size: var(--ui-t-small, 11.5px);
134
+ font-variant-numeric: tabular-nums;
135
+ color: var(--ui-faint, #8a959c);
136
+ white-space: nowrap;
90
137
  }
91
138
 
139
+ /*
140
+ * 점과 그 아래로 이어지는 세로선.
141
+ *
142
+ * 칸을 세로로 늘려(align-self: stretch) 그 안에 점과 선을 그린다. 전에는 칸이 9px 이라
143
+ * 선 높이를 calc(100% + 100px) 으로 짐작해서 늘렸고, 줄 높이가 그보다 낮으면 선이 다음
144
+ * 줄을 지나 카드 아래까지 흘러내렸다.
145
+ */
92
146
  [status] {
93
- margin: 0 var(--spacing-small);
94
- display: block;
95
- border-radius: 50%;
96
- flex: initial;
97
- width: 12px;
98
- height: 12px;
99
147
  position: relative;
100
- top: 3px;
101
- border: 2px solid #fff;
102
- background-color: var(--worklist-status-color, tomato);
103
- color: var(--primary-text-color);
148
+ align-self: stretch;
149
+ width: 9px;
150
+ margin-left: 3px;
151
+ }
152
+
153
+ [status]::after {
154
+ content: '';
155
+ position: absolute;
156
+ top: 4px;
157
+ left: 0;
158
+ width: 9px;
159
+ height: 9px;
160
+ border-radius: 50%;
161
+ background-color: var(--ui-accent, #00727e);
104
162
  }
105
163
 
106
164
  [status]::before {
107
165
  content: '';
108
- height: 60px;
166
+ position: absolute;
167
+ top: 15px;
168
+ bottom: 0;
169
+ left: 3px;
109
170
  width: 2px;
110
- display: block;
111
- position: relative;
112
- margin-left: 5px;
113
- background-color: var(--worklist-status-color, tomato);
114
- opacity: 0.2;
171
+ background-color: var(--ui-line, #d5dce0);
115
172
  }
116
173
 
117
- table,
118
- th,
119
- td {
120
- border: 1px solid lightgray;
121
- border-collapse: collapse;
174
+ li:last-child [status]::before {
175
+ display: none;
122
176
  }
123
177
 
124
- th,
125
- td {
126
- padding: 10px;
178
+ [info] {
179
+ min-width: 0;
180
+ padding-bottom: var(--spacing-large, 16px);
181
+ }
182
+
183
+ li:last-child [info] {
184
+ padding-bottom: 0;
185
+ }
186
+
187
+ /* 무슨 일이 있었나 — 이 줄에서 먼저 읽혀야 하는 값. */
188
+ [what] {
189
+ font-weight: 600;
190
+ }
191
+
192
+ /* 누가 — 곁말이다. 아이콘은 두지 않는다(이름 앞의 원형 아이콘이 값처럼 보였다). */
193
+ [who] {
194
+ margin-left: var(--spacing-small, 4px);
195
+ font-size: var(--ui-t-small, 11.5px);
196
+ color: var(--ui-faint, #8a959c);
197
+ }
198
+
199
+ /* 적힌 말 — 의견 · 사유 · 결과. 없으면 이 요소 자체를 그리지 않는다. */
200
+ p {
201
+ /* 줄을 바꿔 아래에 두되(block), 폭은 내용만큼만 — 짧은 의견에 화면 폭짜리 상자가 생겼다. */
202
+ display: block;
203
+ width: fit-content;
204
+ max-width: 100%;
205
+ margin: 5px 0 0;
206
+ padding: 7px 10px;
207
+ background: var(--ui-panel-2, #eef1f3);
208
+ border-radius: var(--ui-r-small, 6px);
209
+ color: var(--ui-ink, #17242c);
210
+ font-size: var(--ui-t-small, 11.5px);
211
+ line-height: 1.5;
212
+ word-break: keep-all;
213
+ }
214
+
215
+ @media only screen and (max-width: 460px) {
216
+ li {
217
+ grid-template-columns: 16px 1fr;
218
+ }
219
+
220
+ /* 좁은 화면에서는 시각을 내용 아래로 내린다 — 한 줄의 절반을 시각에 내줄 수 없다. */
221
+ [date] {
222
+ grid-column: 2;
223
+ grid-row: 2;
224
+ padding: 0 0 var(--spacing-medium, 8px);
225
+ }
127
226
  }
128
227
  `
129
228
  ]; }
130
229
  render() {
131
230
  const { activityApprovals = [], activityThreadHistories = [] } = this.activityThread || {};
231
+ /*
232
+ * **정렬 키와 표시 키가 같아야 한다.**
233
+ *
234
+ * 전에는 둘 다 updatedAt 으로 정렬해 놓고, 결재 줄만 terminatedAt 을 보여 주었다. 그래서
235
+ * 시각이 오르내리는 목록이 나왔다 — 11:51 승인됨 · 11:50 제출 · 11:50 게시 · 11:51 상신.
236
+ * 시간 순으로 읽으라고 만든 화면인데 시간 순이 아니었다.
237
+ */
132
238
  const timeline = [
133
239
  ...activityThreadHistories.filter(item => item.transaction && (!this.shortForm || !/^.*(reject|approve|save|restart).*$/.test(item.transaction))),
134
240
  ...activityApprovals.filter(item => item.judgment)
135
- ].sort((a, b) => ((a.updatedAt || 0) > (b.updatedAt || 0) ? 1 : -1));
136
- return html ` ${timeline.length > 0
137
- ? html `
138
- <span subtitle>${this.subtitle || i18next.t('label.timeline')}</span>
139
-
140
- <ol>
141
- ${timeline.map(item => 'reason' in item
142
- ? this.renderActivityThreadHistory(item)
143
- : this.renderActivityApproval(item))}
144
- </ol>
145
- `
146
- : nothing}`;
241
+ ].sort((a, b) => (whenOf(a) > whenOf(b) ? 1 : -1));
242
+ if (!timeline.length) {
243
+ return nothing;
244
+ }
245
+ return html `
246
+ <details ?open=${this.open}>
247
+ <summary>
248
+ <md-icon class="chevron">chevron_right</md-icon>
249
+ <h2>${this.subtitle || i18next.t('label.timeline')}</h2>
250
+ <span class="count">${i18next.t('text.timeline-count', { count: timeline.length })}</span>
251
+ <span class="last">${this.lastLine(timeline[timeline.length - 1])}</span>
252
+ </summary>
253
+
254
+ <ol>
255
+ ${timeline.map(item => 'reason' in item
256
+ ? this.renderActivityThreadHistory(item)
257
+ : this.renderActivityApproval(item))}
258
+ </ol>
259
+ </details>
260
+ `;
261
+ }
262
+ /** 접혔을 때 보이는 한 줄 — 마지막 기록의 「무엇을 · 누가 · 언제」. */
263
+ lastLine(item) {
264
+ const what = 'reason' in item
265
+ ? i18next.t('label.activity-transaction-' + item.transaction)
266
+ : i18next.t('label.activity-state-' + item.judgment);
267
+ const who = item?.updater?.name || item?.approver?.name || '';
268
+ return [what, who, whenText(whenOf(item))].filter(Boolean).join(' · ');
147
269
  }
148
270
  renderActivityThreadHistory(activityThreadHistory) {
149
271
  const { updater, updatedAt, state, reason, output, transaction } = activityThreadHistory;
150
272
  const { name } = updater || {};
151
273
  return html `
152
274
  <li>
153
- <span date>${formatter.format(new Date(updatedAt))}</span>
275
+ <span date>${whenText(updatedAt)}</span>
154
276
  <span status></span>
155
277
 
156
278
  <span info>
157
- ${i18next.t('label.activity-transaction-' + transaction)}
158
- <div><md-icon>account_circle</md-icon>${name}</div>
159
- <p>${reason ? reason : output ? this.renderOutput(output) : nothing}</p>
279
+ <span what>${i18next.t('label.activity-transaction-' + transaction)}</span>
280
+ ${name ? html `<span who>${name}</span>` : nothing}
281
+ ${this.renderSaid(reason || (output ? this.renderOutput(output) : ''))}
160
282
  </span>
161
283
  </li>
162
284
  `;
@@ -166,16 +288,20 @@ let ActivityThreadTimeline = class ActivityThreadTimeline extends LitElement {
166
288
  const { name } = approver || {};
167
289
  return html `
168
290
  <li>
169
- <span date>${formatter.format(new Date(terminatedAt || createdAt))}</span>
291
+ <span date>${whenText(terminatedAt ?? createdAt)}</span>
170
292
  <span status></span>
171
293
  <span info>
172
- ${i18next.t('label.activity-state-' + judgment)}
173
- <div><md-icon>account_circle</md-icon>${name}</div>
174
- <p>${comment}</p>
294
+ <span what>${i18next.t('label.activity-state-' + judgment)}</span>
295
+ ${name ? html `<span who>${name}</span>` : nothing}
296
+ ${this.renderSaid(comment)}
175
297
  </span>
176
298
  </li>
177
299
  `;
178
300
  }
301
+ /** 적힌 말이 없으면 상자를 그리지 않는다 — 빈 회색 상자는 내용이 있는 줄과 구별되지 않는다. */
302
+ renderSaid(said) {
303
+ return said ? html `<p>${said}</p>` : nothing;
304
+ }
179
305
  renderOutput(output) {
180
306
  const keys = Object.keys(output);
181
307
  if (keys.length === 0) {
@@ -193,7 +319,7 @@ let ActivityThreadTimeline = class ActivityThreadTimeline extends LitElement {
193
319
  let result = `${firstKey}: ${value}`;
194
320
  // 추가 속성이 있는 경우 표시
195
321
  if (keys.length > 1) {
196
- result += ` ( ${keys.length - 1}개 항목)`;
322
+ result += ' ' + i18next.t('text.and-n-more-fields', { n: keys.length - 1 });
197
323
  }
198
324
  return result;
199
325
  }
@@ -210,6 +336,10 @@ __decorate([
210
336
  property({ type: Boolean, attribute: 'short-form' }),
211
337
  __metadata("design:type", Boolean)
212
338
  ], ActivityThreadTimeline.prototype, "shortForm", void 0);
339
+ __decorate([
340
+ property({ type: Boolean }),
341
+ __metadata("design:type", Boolean)
342
+ ], ActivityThreadTimeline.prototype, "open", void 0);
213
343
  ActivityThreadTimeline = __decorate([
214
344
  customElement('activity-thread-timeline')
215
345
  ], ActivityThreadTimeline);
@@ -1 +1 @@
1
- {"version":3,"file":"activity-thread-timeline.js","sourceRoot":"","sources":["../../client/components/activity-thread-timeline.ts"],"names":[],"mappings":";AAAA,OAAO,4BAA4B,CAAA;AACnC,OAAO,0EAA0E,CAAA;AAEjF,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,KAAK,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAE3D,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAEvC,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAI5D,SAAS,gBAAgB,CAAC,MAAc,EAAE,OAAmC;IAC3E,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,MAAM,IAAI,OAAO,CAAA;QACpC,OAAO,IAAI,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;IACrD,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,IAAI,CAAC,uCAAuC,MAAM,kCAAkC,EAAE,CAAC,CAAC,CAAA;QAChG,OAAO,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IAClD,CAAC;AACH,CAAC;AAED,MAAM,SAAS,GAAG,gBAAgB,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAA;AAG1F,IAAM,sBAAsB,GAA5B,MAAM,sBAAuB,SAAQ,UAAU;aAC7C,WAAM,GAAG;QACd,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA2GF;KACF,AA7GY,CA6GZ;IAMD,MAAM;QACJ,MAAM,EAAE,iBAAiB,GAAG,EAAE,EAAE,uBAAuB,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,cAAc,IAAI,EAAE,CAAA;QAE1F,MAAM,QAAQ,GAAG;YACf,GAAG,uBAAuB,CAAC,MAAM,CAC/B,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,qCAAqC,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAC/G;YACD,GAAG,iBAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC;SACnD,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAEpE,OAAO,IAAI,CAAA,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;YAChC,CAAC,CAAC,IAAI,CAAA;2BACe,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC;;;cAGzD,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CACpB,QAAQ,IAAI,IAAI;gBACd,CAAC,CAAC,IAAI,CAAC,2BAA2B,CAAC,IAA6B,CAAC;gBACjE,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC,IAAwB,CAAC,CAC1D;;SAEJ;YACH,CAAC,CAAC,OAAO,EAAE,CAAA;IACf,CAAC;IAED,2BAA2B,CAAC,qBAA4C;QACtE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,qBAAqB,CAAA;QACxF,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,IAAI,EAAE,CAAA;QAE9B,OAAO,IAAI,CAAA;;qBAEM,SAAS,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,SAAU,CAAC,CAAC;;;;YAI/C,OAAO,CAAC,CAAC,CAAC,6BAA6B,GAAG,WAAW,CAAC;kDAChB,IAAI;eACvC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO;;;KAGxE,CAAA;IACH,CAAC;IAED,sBAAsB,CAAC,gBAAkC;QACvD,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,gBAAgB,CAAA;QACjF,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,IAAI,EAAE,CAAA;QAE/B,OAAO,IAAI,CAAA;;qBAEM,SAAS,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,YAAY,IAAI,SAAU,CAAC,CAAC;;;YAG/D,OAAO,CAAC,CAAC,CAAC,uBAAuB,GAAG,QAAQ,CAAC;kDACP,IAAI;eACvC,OAAO;;;KAGjB,CAAA;IACH,CAAC;IAED,YAAY,CAAC,MAAW;QACtB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAChC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,EAAE,CAAA;QACX,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;QACxB,IAAI,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAA;QAE5B,wBAAwB;QACxB,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAA;QAClD,CAAC;aAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YAC1D,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,CAAA;QACxC,CAAC;QAED,IAAI,MAAM,GAAG,GAAG,QAAQ,KAAK,KAAK,EAAE,CAAA;QAEpC,kBAAkB;QAClB,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,IAAI,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,OAAO,CAAA;QACzC,CAAC;QAED,OAAO,MAAM,CAAA;IACf,CAAC;;AAxF2B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;8BAAkB,cAAc;8DAAA;AAC/B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;wDAAkB;AACS;IAArD,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;;yDAAoB;AAlH9D,sBAAsB;IADlC,aAAa,CAAC,0BAA0B,CAAC;GAC7B,sBAAsB,CAyMlC","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@things-factory/organization/dist-client/component/approval-line-view.js'\n\nimport { html, css, LitElement, nothing } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { i18next } from '@operato/i18n'\n\nimport { ActivityThread } from '../types/activity-thread.js'\nimport { ActivityThreadHistory } from '../types/activity-thread-history.js'\nimport { ActivityApproval } from '../types/activity-approval.js'\n\nfunction getSafeFormatter(locale: string, options: Intl.DateTimeFormatOptions): Intl.DateTimeFormat {\n try {\n const safeLocale = locale || 'en-US'\n return new Intl.DateTimeFormat(safeLocale, options)\n } catch (e) {\n console.warn(`Invalid locale or options provided: ${locale}, falling back to default en-US.`, e)\n return new Intl.DateTimeFormat('en-US', options)\n }\n}\n\nconst formatter = getSafeFormatter(navigator.language, { dateStyle: 'full', timeStyle: 'short' })\n\n@customElement('activity-thread-timeline')\nexport class ActivityThreadTimeline extends LitElement {\n static styles = [\n css`\n :host {\n display: block;\n }\n\n [subtitle] {\n padding: var(--spacing-small) var(--spacing-medium);\n background-color: var(--md-sys-color-surface-variant);\n border: 2px solid var(--md-sys-color-outline);\n border-radius: 15px;\n color: var(--md-sys-color-on-surface);\n font-weight: bold;\n }\n\n ol {\n list-style: none;\n margin: var(--spacing-medium) 0 0 var(--spacing-small);\n padding: 0;\n }\n\n li {\n display: flex;\n border: none;\n }\n\n [info] {\n flex: 1;\n color: var(--md-sys-color-on-surface);\n background: var(--md-sys-color-surface);\n\n div {\n float: right;\n opacity: 0.7;\n }\n\n md-icon {\n position: relative;\n top: 3px;\n font-size: var(--fontsize-large);\n }\n\n p {\n color: var(--md-sys-color-on-surface);\n background: var(--md-sys-color-surface-variant);\n\n margin: var(--spacing-small) 0 var(--spacing-medium) 0;\n padding: var(--spacing-small) var(--spacing-medium);\n font-size: var(--fontsize-small);\n text-align: justify;\n min-height: 20px;\n }\n\n p::before {\n content: '';\n float: right;\n margin-top: -10px;\n margin-right: 20px;\n border: 7px solid transparent;\n border-bottom-color: #f4f4f4;\n border-top: 0;\n }\n }\n\n [date] {\n opacity: 0.7;\n flex: initial;\n width: 200px;\n max-width: 30%;\n font-size: var(--fontsize-small);\n }\n\n [status] {\n margin: 0 var(--spacing-small);\n display: block;\n border-radius: 50%;\n flex: initial;\n width: 12px;\n height: 12px;\n position: relative;\n top: 3px;\n border: 2px solid #fff;\n background-color: var(--worklist-status-color, tomato);\n color: var(--primary-text-color);\n }\n\n [status]::before {\n content: '';\n height: 60px;\n width: 2px;\n display: block;\n position: relative;\n margin-left: 5px;\n background-color: var(--worklist-status-color, tomato);\n opacity: 0.2;\n }\n\n table,\n th,\n td {\n border: 1px solid lightgray;\n border-collapse: collapse;\n }\n\n th,\n td {\n padding: 10px;\n }\n `\n ]\n\n @property({ type: Object }) activityThread?: ActivityThread\n @property({ type: String }) subtitle?: string\n @property({ type: Boolean, attribute: 'short-form' }) shortForm?: boolean\n\n render() {\n const { activityApprovals = [], activityThreadHistories = [] } = this.activityThread || {}\n\n const timeline = [\n ...activityThreadHistories.filter(\n item => item.transaction && (!this.shortForm || !/^.*(reject|approve|save|restart).*$/.test(item.transaction))\n ),\n ...activityApprovals.filter(item => item.judgment)\n ].sort((a, b) => ((a.updatedAt || 0) > (b.updatedAt || 0) ? 1 : -1))\n\n return html` ${timeline.length > 0\n ? html`\n <span subtitle>${this.subtitle || i18next.t('label.timeline')}</span>\n\n <ol>\n ${timeline.map(item =>\n 'reason' in item\n ? this.renderActivityThreadHistory(item as ActivityThreadHistory)\n : this.renderActivityApproval(item as ActivityApproval)\n )}\n </ol>\n `\n : nothing}`\n }\n\n renderActivityThreadHistory(activityThreadHistory: ActivityThreadHistory) {\n const { updater, updatedAt, state, reason, output, transaction } = activityThreadHistory\n const { name } = updater || {}\n\n return html`\n <li>\n <span date>${formatter.format(new Date(updatedAt!))}</span>\n <span status></span>\n\n <span info>\n ${i18next.t('label.activity-transaction-' + transaction)}\n <div><md-icon>account_circle</md-icon>${name}</div>\n <p>${reason ? reason : output ? this.renderOutput(output) : nothing}</p>\n </span>\n </li>\n `\n }\n\n renderActivityApproval(activityApproval: ActivityApproval) {\n const { judgment, approver, comment, createdAt, terminatedAt } = activityApproval\n const { name } = approver || {}\n\n return html`\n <li>\n <span date>${formatter.format(new Date(terminatedAt || createdAt!))}</span>\n <span status></span>\n <span info>\n ${i18next.t('label.activity-state-' + judgment)}\n <div><md-icon>account_circle</md-icon>${name}</div>\n <p>${comment}</p>\n </span>\n </li>\n `\n }\n\n renderOutput(output: any) {\n const keys = Object.keys(output)\n if (keys.length === 0) {\n return ''\n }\n\n const firstKey = keys[0]\n let value = output[firstKey]\n\n // 값이 객체이거나 긴 문자열인 경우 처리\n if (typeof value === 'object') {\n value = Array.isArray(value) ? '[...]' : '{...}'\n } else if (typeof value === 'string' && value.length > 50) {\n value = value.substring(0, 47) + '...'\n }\n\n let result = `${firstKey}: ${value}`\n\n // 추가 속성이 있는 경우 표시\n if (keys.length > 1) {\n result += ` (외 ${keys.length - 1}개 항목)`\n }\n\n return result\n }\n}\n"]}
1
+ {"version":3,"file":"activity-thread-timeline.js","sourceRoot":"","sources":["../../client/components/activity-thread-timeline.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@things-factory/organization/dist-client/component/approval-line-view.js'\n\nimport { html, css, LitElement, nothing } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { i18next } from '@operato/i18n'\n\nimport { whenText } from '../when.js'\n\nimport { ActivityThread } from '../types/activity-thread.js'\nimport { ActivityThreadHistory } from '../types/activity-thread-history.js'\nimport { ActivityApproval } from '../types/activity-approval.js'\n\n\n/** 그 줄이 화면에 내보이는 시각. 정렬도 이 값으로 한다. */\nfunction whenOf(item: any): number {\n const at = item?.terminatedAt ?? item?.updatedAt ?? item?.createdAt\n\n const ms = at === null || at === undefined || at === '' ? NaN : Date.parse(String(at))\n\n return Number.isFinite(ms) ? ms : 0\n}\n\n@customElement('activity-thread-timeline')\nexport class ActivityThreadTimeline extends LitElement {\n static styles = [\n css`\n /*\n * 업무 하나가 지나온 자취 — **언제 · 무슨 일이 · 누구에 의해.**\n *\n * ── 무엇을 고쳤나 (2026-09-06) ─────────────────────────────────────\n * 1. 절 제목이 단추처럼 생겼었다. border: 2px 에 border-radius: 15px 인 알약이라\n * 누를 수 있는 것으로 보인다. 이 화면에서 알약은 거르개(chip)의 모양이다.\n * 2. 말풍선 꼬리에 #f4f4f4 가, 표 선에 lightgray 가 하드코딩되어 있었다. 어두운\n * 화면에서 밝은 회색이 그대로 나온다.\n * 3. 시각이 Tuesday, September 1, 2026 at 11:51 AM 이었다. when.ts 머리말 참조.\n *\n * ── 내용이 없는 줄에 상자를 그리지 않는다 ─────────────────────────\n * 의견이나 결과가 없는 줄에도 회색 상자가 min-height: 20px 으로 깔려 있었다. 저장 ·\n * 게시처럼 적을 것이 없는 기록이 대부분이라, 화면의 절반이 빈 회색 상자였다.\n */\n /*\n * 절 하나이므로 다른 절과 같은 카드로 둔다. 전에는 카드 밖에 그냥 놓여 있어서 「업무 정보」\n * 카드와 위계가 어긋났다 — 같은 층의 두 절인데 하나만 면 위에 있었다.\n */\n :host {\n display: block;\n padding: 14px;\n background: var(--ui-panel, #ffffff);\n border: 1px solid var(--ui-line, #d5dce0);\n border-radius: var(--ui-r, 10px);\n color: var(--ui-ink, #17242c);\n font-size: var(--ui-t-body, 13px);\n }\n\n /* 그릴 것이 없으면 빈 카드를 남기지 않는다. */\n :host(:not(:has(ol))) {\n display: none;\n }\n\n /*\n * ── 접어 둔다 (2026-09-06) ────────────────────────────────────────\n * 이력은 참고다. 결재자가 먼저 읽는 것은 「무엇을 승인하나」이고, 이력은 그것으로 판단이\n * 안 설 때 펴 보는 것이다. 그런데 이 카드가 내용 절과 똑같은 무게로 서 있었다.\n *\n * 접어 두되 **마지막에 무슨 일이 있었는지 한 줄은 남긴다.** 「이력 4건」만 있으면 펴 볼지\n * 말지를 정할 수가 없다.\n *\n * 펴 두어야 하는 화면이 있다 — 지난 일을 보러 온 화면(완료 조회), 그리고 보일 항목이 하나도\n * 없어서 「타임라인의 상신 사유를 보고 판단하세요」라고 안내한 경우다. 그때 접혀 있으면 그\n * 안내가 거짓말이 된다. 그래서 여는 쪽은 담는 화면이 정한다(open 속성).\n */\n h2 {\n margin: 0;\n font-size: var(--ui-t-title, 15px);\n font-weight: 600;\n color: var(--ui-ink, #17242c);\n }\n\n summary {\n display: flex;\n align-items: baseline;\n gap: var(--spacing-medium, 8px);\n cursor: pointer;\n list-style: none;\n }\n\n summary::-webkit-details-marker {\n display: none;\n }\n\n /* 펴고 접는 표시. 색만으로 말하지 않으려고 방향이 바뀐다. */\n .chevron {\n flex: none;\n font-size: 18px;\n color: var(--ui-muted, #57676f);\n transition: transform 0.12s ease;\n }\n\n details[open] .chevron {\n transform: rotate(90deg);\n }\n\n /* 접혔을 때 보이는 한 줄 — 마지막에 무슨 일이 있었나. */\n .last {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-faint, #8a959c);\n }\n\n details[open] .last {\n display: none;\n }\n\n .count {\n flex: none;\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-muted, #57676f);\n font-variant-numeric: tabular-nums;\n }\n\n ol {\n list-style: none;\n margin: var(--spacing-medium, 8px) 0 0;\n padding: 0;\n }\n\n li {\n display: grid;\n grid-template-columns: max-content 16px 1fr;\n align-items: start;\n column-gap: var(--spacing-small, 4px);\n }\n\n /* 시각 — 자릿수를 맞춰 위아래를 바로 비교할 수 있게. */\n [date] {\n padding-top: 1px;\n font-family: var(--ui-mono, ui-monospace, Menlo, monospace);\n font-size: var(--ui-t-small, 11.5px);\n font-variant-numeric: tabular-nums;\n color: var(--ui-faint, #8a959c);\n white-space: nowrap;\n }\n\n /*\n * 점과 그 아래로 이어지는 세로선.\n *\n * 칸을 세로로 늘려(align-self: stretch) 그 안에 점과 선을 그린다. 전에는 칸이 9px 이라\n * 선 높이를 calc(100% + 100px) 으로 짐작해서 늘렸고, 줄 높이가 그보다 낮으면 선이 다음\n * 줄을 지나 카드 아래까지 흘러내렸다.\n */\n [status] {\n position: relative;\n align-self: stretch;\n width: 9px;\n margin-left: 3px;\n }\n\n [status]::after {\n content: '';\n position: absolute;\n top: 4px;\n left: 0;\n width: 9px;\n height: 9px;\n border-radius: 50%;\n background-color: var(--ui-accent, #00727e);\n }\n\n [status]::before {\n content: '';\n position: absolute;\n top: 15px;\n bottom: 0;\n left: 3px;\n width: 2px;\n background-color: var(--ui-line, #d5dce0);\n }\n\n li:last-child [status]::before {\n display: none;\n }\n\n [info] {\n min-width: 0;\n padding-bottom: var(--spacing-large, 16px);\n }\n\n li:last-child [info] {\n padding-bottom: 0;\n }\n\n /* 무슨 일이 있었나 — 이 줄에서 먼저 읽혀야 하는 값. */\n [what] {\n font-weight: 600;\n }\n\n /* 누가 — 곁말이다. 아이콘은 두지 않는다(이름 앞의 원형 아이콘이 값처럼 보였다). */\n [who] {\n margin-left: var(--spacing-small, 4px);\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-faint, #8a959c);\n }\n\n /* 적힌 말 — 의견 · 사유 · 결과. 없으면 이 요소 자체를 그리지 않는다. */\n p {\n /* 줄을 바꿔 아래에 두되(block), 폭은 내용만큼만 — 짧은 의견에 화면 폭짜리 상자가 생겼다. */\n display: block;\n width: fit-content;\n max-width: 100%;\n margin: 5px 0 0;\n padding: 7px 10px;\n background: var(--ui-panel-2, #eef1f3);\n border-radius: var(--ui-r-small, 6px);\n color: var(--ui-ink, #17242c);\n font-size: var(--ui-t-small, 11.5px);\n line-height: 1.5;\n word-break: keep-all;\n }\n\n @media only screen and (max-width: 460px) {\n li {\n grid-template-columns: 16px 1fr;\n }\n\n /* 좁은 화면에서는 시각을 내용 아래로 내린다 — 한 줄의 절반을 시각에 내줄 수 없다. */\n [date] {\n grid-column: 2;\n grid-row: 2;\n padding: 0 0 var(--spacing-medium, 8px);\n }\n }\n `\n ]\n\n @property({ type: Object }) activityThread?: ActivityThread\n @property({ type: String }) subtitle?: string\n @property({ type: Boolean, attribute: 'short-form' }) shortForm?: boolean\n\n /**\n * 펴 둘 것인가 — **담는 화면이 정한다.**\n *\n * 기본은 접는다. 펴는 곳은 둘이다.\n * 완료 조회 지난 일을 보러 온 화면이라 이력이 곧 본문이다\n * 보일 항목 없음 화면이 「타임라인의 상신 사유를 보고 판단하세요」로 여기를 가리킨다\n */\n @property({ type: Boolean }) open?: boolean\n\n render() {\n const { activityApprovals = [], activityThreadHistories = [] } = this.activityThread || {}\n\n /*\n * **정렬 키와 표시 키가 같아야 한다.**\n *\n * 전에는 둘 다 updatedAt 으로 정렬해 놓고, 결재 줄만 terminatedAt 을 보여 주었다. 그래서\n * 시각이 오르내리는 목록이 나왔다 — 11:51 승인됨 · 11:50 제출 · 11:50 게시 · 11:51 상신.\n * 시간 순으로 읽으라고 만든 화면인데 시간 순이 아니었다.\n */\n const timeline = [\n ...activityThreadHistories.filter(\n item => item.transaction && (!this.shortForm || !/^.*(reject|approve|save|restart).*$/.test(item.transaction))\n ),\n ...activityApprovals.filter(item => item.judgment)\n ].sort((a, b) => (whenOf(a) > whenOf(b) ? 1 : -1))\n\n if (!timeline.length) {\n return nothing\n }\n\n return html`\n <details ?open=${this.open}>\n <summary>\n <md-icon class=\"chevron\">chevron_right</md-icon>\n <h2>${this.subtitle || i18next.t('label.timeline')}</h2>\n <span class=\"count\">${i18next.t('text.timeline-count', { count: timeline.length })}</span>\n <span class=\"last\">${this.lastLine(timeline[timeline.length - 1])}</span>\n </summary>\n\n <ol>\n ${timeline.map(item =>\n 'reason' in item\n ? this.renderActivityThreadHistory(item as ActivityThreadHistory)\n : this.renderActivityApproval(item as ActivityApproval)\n )}\n </ol>\n </details>\n `\n }\n\n /** 접혔을 때 보이는 한 줄 — 마지막 기록의 「무엇을 · 누가 · 언제」. */\n lastLine(item: any): string {\n const what =\n 'reason' in item\n ? i18next.t('label.activity-transaction-' + item.transaction)\n : i18next.t('label.activity-state-' + item.judgment)\n\n const who = item?.updater?.name || item?.approver?.name || ''\n\n return [what, who, whenText(whenOf(item))].filter(Boolean).join(' · ')\n }\n\n renderActivityThreadHistory(activityThreadHistory: ActivityThreadHistory) {\n const { updater, updatedAt, state, reason, output, transaction } = activityThreadHistory\n const { name } = updater || {}\n\n return html`\n <li>\n <span date>${whenText(updatedAt)}</span>\n <span status></span>\n\n <span info>\n <span what>${i18next.t('label.activity-transaction-' + transaction)}</span>\n ${name ? html`<span who>${name}</span>` : nothing}\n ${this.renderSaid(reason || (output ? this.renderOutput(output) : ''))}\n </span>\n </li>\n `\n }\n\n renderActivityApproval(activityApproval: ActivityApproval) {\n const { judgment, approver, comment, createdAt, terminatedAt } = activityApproval\n const { name } = approver || {}\n\n return html`\n <li>\n <span date>${whenText(terminatedAt ?? createdAt)}</span>\n <span status></span>\n <span info>\n <span what>${i18next.t('label.activity-state-' + judgment)}</span>\n ${name ? html`<span who>${name}</span>` : nothing}\n ${this.renderSaid(comment)}\n </span>\n </li>\n `\n }\n\n /** 적힌 말이 없으면 상자를 그리지 않는다 — 빈 회색 상자는 내용이 있는 줄과 구별되지 않는다. */\n renderSaid(said?: string) {\n return said ? html`<p>${said}</p>` : nothing\n }\n\n renderOutput(output: any) {\n const keys = Object.keys(output)\n if (keys.length === 0) {\n return ''\n }\n\n const firstKey = keys[0]\n let value = output[firstKey]\n\n // 값이 객체이거나 긴 문자열인 경우 처리\n if (typeof value === 'object') {\n value = Array.isArray(value) ? '[...]' : '{...}'\n } else if (typeof value === 'string' && value.length > 50) {\n value = value.substring(0, 47) + '...'\n }\n\n let result = `${firstKey}: ${value}`\n\n // 추가 속성이 있는 경우 표시\n if (keys.length > 1) {\n result += ' ' + i18next.t('text.and-n-more-fields', { n: keys.length - 1 })\n }\n\n return result\n }\n}\n"]}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * 업무 · 결재 화면의 머리 띠 — **세 화면이 한 모양을 쓴다.**
3
+ *
4
+ * activity-thread-ribon · activity-approval-ribon · activity-instance-ribon 이 같은 자리에 같은
5
+ * 것을 보인다(상태 · 설명 · 결재선 또는 담당자). 그런데 셋이 각자 100여 줄의 CSS 를 갖고 있었고,
6
+ * 상태색 여덟 개가 세 벌로 복사되어 있었다.
7
+ *
8
+ * ── 채운 띠를 걷은 이유 — 글자가 안 읽혔다 ─────────────────────────────────
9
+ * 전에는 상태마다 다른 색으로 띠 전체를 채우고 그 위에 흰 글자를 얹었다. 색은 하드코딩된 hex 여덟
10
+ * 개였고, --md-sys-color-on-primary(대개 흰색)를 글자색으로 썼다. contrast 를 계산하면 12px 글자
11
+ * 기준 AA(4.5:1)에 네 개가 못 미친다.
12
+ *
13
+ * started #56af45 2.76 ← 작업자가 가장 오래 보는 상태
14
+ * ended #02acae 2.79
15
+ * rejected #f27429 2.87
16
+ * submitted #428df3 3.31
17
+ * unassigned #5f7184 5.02 (통과)
18
+ * escalated #595de5 5.09 (통과)
19
+ * aborted #cb3a33 5.01 (통과)
20
+ * delegated #8654b0 5.37 (통과)
21
+ *
22
+ * 색을 여덟 개 다시 고르는 대신 **띠를 채우지 않는다.** SurfaceStyles 가 정한 규칙과 같다 —
23
+ * 흰 면에 1px 선을 두르고, 색은 상태 앞의 점 하나가 말한다. 글자는 --ui-ink 라 어느 상태에서든
24
+ * 읽히고, 점은 UI 요소라 3:1 기준으로 판단한다.
25
+ *
26
+ * ── 색 여덟 개를 뜻 여섯 개로 ──────────────────────────────────────────────
27
+ * 하드코딩된 hex 는 앱 팔레트를 따르지 않는다. worklist 는 framework 패키지라 operato-plant ·
28
+ * operato-gangsters · operato-dataset 이 모두 이 화면을 쓰는데, 어느 앱에서도 그 앱 색이 아니었다.
29
+ * 토큰으로 바꾸면 앱마다 자기 라이트 · 다크 테마를 따라온다.
30
+ *
31
+ * unassigned · assigned --ui-muted 아직 시작하지 않았다
32
+ * started --ui-accent 내 손에서 진행 중이다
33
+ * submitted · escalated · delegated --ui-info 남의 판단을 기다린다
34
+ * rejected --ui-warn 되돌아왔다. 다시 해야 한다
35
+ * ended · approved --ui-good 끝났다
36
+ * aborted --ui-crit 중단됐다
37
+ *
38
+ * 색만으로 뜻을 전하지 않는다 — 상태 이름을 항상 함께 적는다.
39
+ */
40
+ export declare const StateRibonStyles: import("lit").CSSResult;