@things-factory/worklist 10.1.168 → 10.1.170

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 (66) hide show
  1. package/dist-client/components/decision-content.d.ts +35 -0
  2. package/dist-client/components/decision-content.js +322 -0
  3. package/dist-client/components/decision-content.js.map +1 -0
  4. package/dist-client/components/decision-slots.d.ts +8 -0
  5. package/dist-client/components/decision-slots.js +17 -0
  6. package/dist-client/components/decision-slots.js.map +1 -1
  7. package/dist-client/components/work-box-groups.d.ts +25 -0
  8. package/dist-client/components/work-box-groups.js +98 -0
  9. package/dist-client/components/work-box-groups.js.map +1 -0
  10. package/dist-client/my-work-menus.d.ts +1 -6
  11. package/dist-client/my-work-menus.js +0 -1
  12. package/dist-client/my-work-menus.js.map +1 -1
  13. package/dist-client/pages/activity-approval/activity-approval-page.d.ts +2 -52
  14. package/dist-client/pages/activity-approval/activity-approval-page.js +8 -298
  15. package/dist-client/pages/activity-approval/activity-approval-page.js.map +1 -1
  16. package/dist-client/pages/activity-instance/activity-instance-view.d.ts +19 -2
  17. package/dist-client/pages/activity-instance/activity-instance-view.js +47 -23
  18. package/dist-client/pages/activity-instance/activity-instance-view.js.map +1 -1
  19. package/dist-client/pages/activity-thread/activity-thread-page.d.ts +6 -2
  20. package/dist-client/pages/activity-thread/activity-thread-page.js +31 -16
  21. package/dist-client/pages/activity-thread/activity-thread-page.js.map +1 -1
  22. package/dist-client/pages/activity-thread/activity-thread-view-page.js +2 -2
  23. package/dist-client/pages/activity-thread/activity-thread-view-page.js.map +1 -1
  24. package/dist-client/pages/activity-thread/thread-sections.d.ts +34 -0
  25. package/dist-client/pages/activity-thread/thread-sections.js +36 -0
  26. package/dist-client/pages/activity-thread/thread-sections.js.map +1 -0
  27. package/dist-client/pages/todo/approval-pending-list-page.d.ts +1 -0
  28. package/dist-client/pages/todo/approval-pending-list-page.js +10 -77
  29. package/dist-client/pages/todo/approval-pending-list-page.js.map +1 -1
  30. package/dist-client/pages/todo/draft-list-page.d.ts +20 -0
  31. package/dist-client/pages/todo/draft-list-page.js +102 -5
  32. package/dist-client/pages/todo/draft-list-page.js.map +1 -1
  33. package/dist-client/pages/todo/todo-list-page.d.ts +19 -0
  34. package/dist-client/pages/todo/todo-list-page.js +147 -23
  35. package/dist-client/pages/todo/todo-list-page.js.map +1 -1
  36. package/dist-client/pages/todo/work-box.d.ts +36 -0
  37. package/dist-client/pages/todo/work-box.js +44 -0
  38. package/dist-client/pages/todo/work-box.js.map +1 -0
  39. package/dist-client/route.d.ts +1 -1
  40. package/dist-client/route.js +0 -3
  41. package/dist-client/route.js.map +1 -1
  42. package/dist-client/tsconfig.tsbuildinfo +1 -1
  43. package/dist-client/types/activity.d.ts +2 -0
  44. package/dist-client/types/activity.js.map +1 -1
  45. package/dist-server/service/activity-summary/open-work.d.ts +0 -1
  46. package/dist-server/service/activity-summary/open-work.js +3 -15
  47. package/dist-server/service/activity-summary/open-work.js.map +1 -1
  48. package/dist-server/service/activity-thread/activity-thread-query.d.ts +3 -0
  49. package/dist-server/service/activity-thread/activity-thread-query.js +24 -5
  50. package/dist-server/service/activity-thread/activity-thread-query.js.map +1 -1
  51. package/dist-server/service/activity-thread/my-open-threads.d.ts +23 -4
  52. package/dist-server/service/activity-thread/my-open-threads.js +38 -7
  53. package/dist-server/service/activity-thread/my-open-threads.js.map +1 -1
  54. package/dist-server/tsconfig.tsbuildinfo +1 -1
  55. package/package.json +5 -5
  56. package/tests/open-doors.test.ts +3 -2
  57. package/things-factory.config.js +0 -1
  58. package/translations/en.json +10 -2
  59. package/translations/es.json +10 -2
  60. package/translations/ja.json +10 -2
  61. package/translations/ko.json +10 -2
  62. package/translations/ms.json +10 -2
  63. package/translations/zh.json +10 -2
  64. package/dist-client/pages/todo/done-list-page.d.ts +0 -38
  65. package/dist-client/pages/todo/done-list-page.js +0 -382
  66. package/dist-client/pages/todo/done-list-page.js.map +0 -1
@@ -9,7 +9,6 @@ export const MY_WORK_MENUS = {
9
9
  badge: 'numberOfApprovalPendings'
10
10
  },
11
11
  draft: { path: 'draft-list', icon: 'drive_file_rename_outline', label: 'title.draft list', badge: 'numberOfDrafts' },
12
- done: { path: 'done-list', icon: 'task_alt', label: 'title.done list' },
13
12
  'approval-done': { path: 'approval-done-list', icon: 'history', label: 'title.approval done list' },
14
13
  'my-work-calendar': { path: 'done-list-calendar', icon: 'calendar_month', label: 'title.my work calendar' },
15
14
  pickable: { path: 'pickable-list', icon: 'shop', label: 'title.activity shopping', badge: 'numberOfPicks' },
@@ -1 +1 @@
1
- {"version":3,"file":"my-work-menus.js","sourceRoot":"","sources":["../client/my-work-menus.ts"],"names":[],"mappings":"AA2BA,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,2EAA2E;IAC3E,SAAS,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,gBAAgB,EAAE;IAC/F,IAAI,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,iBAAiB,EAAE,KAAK,EAAE,eAAe,EAAE;IACnG,kBAAkB,EAAE;QAClB,IAAI,EAAE,uBAAuB;QAC7B,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,6BAA6B;QACpC,KAAK,EAAE,0BAA0B;KAClC;IACD,KAAK,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,2BAA2B,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,gBAAgB,EAAE;IACpH,IAAI,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,iBAAiB,EAAE;IACvE,eAAe,EAAE,EAAE,IAAI,EAAE,oBAAoB,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,0BAA0B,EAAE;IACnG,kBAAkB,EAAE,EAAE,IAAI,EAAE,oBAAoB,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,wBAAwB,EAAE;IAC3G,QAAQ,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,yBAAyB,EAAE,KAAK,EAAE,eAAe,EAAE;IAC3G,OAAO,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,6BAA6B,EAAE;IAC9F,QAAQ,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,sBAAsB,EAAE,KAAK,EAAE,8BAA8B,EAAE;IACxG,mBAAmB,EAAE,EAAE,IAAI,EAAE,wBAAwB,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,8BAA8B,EAAE;IACtH,kBAAkB,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,wBAAwB,EAAE;IACpG,YAAY,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,iBAAiB,EAAE;CAC1C,CAAA","sourcesContent":["/**\n * The menu entries for worklist's own screens — **which address, which icon, which label, which count.**\n *\n * Every product puts \"my work\" in its menu, and each used to write these rows itself: the same path,\n * icon, label key and badge name, copied and drifting (one app had a different to-do icon, one had its\n * own English labels). The screens are worklist's, so the facts about them are too. A product picks the\n * entries it shows, in its own order, and keeps its own menu shape:\n *\n * ```ts\n * const MY_WORK: MyWorkMenuName[] = ['todo', 'approval-pending', 'draft', 'done']\n * MY_WORK.map(name => MY_WORK_MENUS[name]).map(({ path, icon, label, badge }) => ({ path, icon, name: i18next.t(label), … }))\n * ```\n *\n * `label` is a translation key that worklist carries in every language. `badge` is a name for\n * `generateActivitySummary`; how a product draws the count stays with the product.\n */\nexport interface MyWorkMenu {\n /** Address of the worklist screen. */\n path: string\n /** Material Symbols name. */\n icon: string\n /** Translation key in worklist's translations. */\n label: string\n /** `generateActivitySummary` name, for entries that count something. */\n badge?: 'numberOfToDos' | 'numberOfApprovalPendings' | 'numberOfDrafts' | 'numberOfPicks' | 'numberOfMyWork'\n}\n\nexport const MY_WORK_MENUS = {\n /** The group row: everything that has come to me. Opens the to-do list. */\n 'my-work': { path: 'todo-list', icon: 'inbox', label: 'menu.my-work', badge: 'numberOfMyWork' },\n todo: { path: 'todo-list', icon: 'things_to_do', label: 'title.todo list', badge: 'numberOfToDos' },\n 'approval-pending': {\n path: 'approval-pending-list',\n icon: 'approval',\n label: 'title.approval pending list',\n badge: 'numberOfApprovalPendings'\n },\n draft: { path: 'draft-list', icon: 'drive_file_rename_outline', label: 'title.draft list', badge: 'numberOfDrafts' },\n done: { path: 'done-list', icon: 'task_alt', label: 'title.done list' },\n 'approval-done': { path: 'approval-done-list', icon: 'history', label: 'title.approval done list' },\n 'my-work-calendar': { path: 'done-list-calendar', icon: 'calendar_month', label: 'title.my work calendar' },\n pickable: { path: 'pickable-list', icon: 'shop', label: 'title.activity shopping', badge: 'numberOfPicks' },\n starter: { path: 'starter-list', icon: 'rocket_launch', label: 'title.activity starter list' },\n reporter: { path: 'reporter-list', icon: 'content_paste_search', label: 'title.activity reporter list' },\n 'activity-instance': { path: 'activity-instance-list', icon: 'list_alt_check', label: 'title.activity instance list' },\n 'activity-catalog': { path: 'activity-catalog', icon: 'menu_book', label: 'title.activity catalog' },\n 'work-doors': { path: 'work-doors-page', icon: 'how_to_reg', label: 'menu.work-doors' }\n} as const satisfies Record<string, MyWorkMenu>\n\nexport type MyWorkMenuName = keyof typeof MY_WORK_MENUS\n"]}
1
+ {"version":3,"file":"my-work-menus.js","sourceRoot":"","sources":["../client/my-work-menus.ts"],"names":[],"mappings":"AA2BA,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,2EAA2E;IAC3E,SAAS,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,gBAAgB,EAAE;IAC/F,IAAI,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,iBAAiB,EAAE,KAAK,EAAE,eAAe,EAAE;IACnG,kBAAkB,EAAE;QAClB,IAAI,EAAE,uBAAuB;QAC7B,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,6BAA6B;QACpC,KAAK,EAAE,0BAA0B;KAClC;IACD,KAAK,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,2BAA2B,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,gBAAgB,EAAE;IACpH,eAAe,EAAE,EAAE,IAAI,EAAE,oBAAoB,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,0BAA0B,EAAE;IACnG,kBAAkB,EAAE,EAAE,IAAI,EAAE,oBAAoB,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,wBAAwB,EAAE;IAC3G,QAAQ,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,yBAAyB,EAAE,KAAK,EAAE,eAAe,EAAE;IAC3G,OAAO,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,6BAA6B,EAAE;IAC9F,QAAQ,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,sBAAsB,EAAE,KAAK,EAAE,8BAA8B,EAAE;IACxG,mBAAmB,EAAE,EAAE,IAAI,EAAE,wBAAwB,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,8BAA8B,EAAE;IACtH,kBAAkB,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,wBAAwB,EAAE;IACpG,YAAY,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,iBAAiB,EAAE;CAC1C,CAAA","sourcesContent":["/**\n * The menu entries for worklist's own screens — **which address, which icon, which label, which count.**\n *\n * Every product puts \"my work\" in its menu, and each used to write these rows itself: the same path,\n * icon, label key and badge name, copied and drifting (one app had a different to-do icon, one had its\n * own English labels). The screens are worklist's, so the facts about them are too. A product picks the\n * entries it shows, in its own order, and keeps its own menu shape:\n *\n * ```ts\n * const MY_WORK: MyWorkMenuName[] = ['todo', 'approval-pending', 'draft']\n * MY_WORK.map(name => MY_WORK_MENUS[name]).map(({ path, icon, label, badge }) => ({ path, icon, name: i18next.t(label), … }))\n * ```\n *\n * `label` is a translation key that worklist carries in every language. `badge` is a name for\n * `generateActivitySummary`; how a product draws the count stays with the product.\n */\nexport interface MyWorkMenu {\n /** Address of the worklist screen. */\n path: string\n /** Material Symbols name. */\n icon: string\n /** Translation key in worklist's translations. */\n label: string\n /** `generateActivitySummary` name, for entries that count something. */\n badge?: 'numberOfToDos' | 'numberOfApprovalPendings' | 'numberOfDrafts' | 'numberOfPicks' | 'numberOfMyWork'\n}\n\nexport const MY_WORK_MENUS = {\n /** The group row: everything that has come to me. Opens the to-do list. */\n 'my-work': { path: 'todo-list', icon: 'inbox', label: 'menu.my-work', badge: 'numberOfMyWork' },\n todo: { path: 'todo-list', icon: 'things_to_do', label: 'title.todo list', badge: 'numberOfToDos' },\n 'approval-pending': {\n path: 'approval-pending-list',\n icon: 'approval',\n label: 'title.approval pending list',\n badge: 'numberOfApprovalPendings'\n },\n draft: { path: 'draft-list', icon: 'drive_file_rename_outline', label: 'title.draft list', badge: 'numberOfDrafts' },\n 'approval-done': { path: 'approval-done-list', icon: 'history', label: 'title.approval done list' },\n 'my-work-calendar': { path: 'done-list-calendar', icon: 'calendar_month', label: 'title.my work calendar' },\n pickable: { path: 'pickable-list', icon: 'shop', label: 'title.activity shopping', badge: 'numberOfPicks' },\n starter: { path: 'starter-list', icon: 'rocket_launch', label: 'title.activity starter list' },\n reporter: { path: 'reporter-list', icon: 'content_paste_search', label: 'title.activity reporter list' },\n 'activity-instance': { path: 'activity-instance-list', icon: 'list_alt_check', label: 'title.activity instance list' },\n 'activity-catalog': { path: 'activity-catalog', icon: 'menu_book', label: 'title.activity catalog' },\n 'work-doors': { path: 'work-doors-page', icon: 'how_to_reg', label: 'menu.work-doors' }\n} as const satisfies Record<string, MyWorkMenu>\n\nexport type MyWorkMenuName = keyof typeof MY_WORK_MENUS\n"]}
@@ -105,60 +105,10 @@ export declare class ActivityApprovalPage extends ActivityApprovalPage_base {
105
105
  * approveActivityApproval / rejectActivityApproval 은 id 와 comment 만 보낸다 — 그 값은 어디에도
106
106
  * 가지 않는다. 결재자가 적을 수 있는 것은 의견 하나이고, 그것은 화면 아래 칸이 받는다.
107
107
  */
108
- /**
109
- * The decision the activity declared, or nothing when it declared none.
110
- *
111
- * Returning nothing is how generatedContent knows to fall back to the model. An activity with no
112
- * decisionKeys draws exactly what it drew before.
113
- *
114
- * A slot whose path led nowhere is skipped rather than drawn empty. Older instances of an activity
115
- * predate a field the declaration names and an empty
116
- * bar on the screen would read as "there is no reason" when the truth is that this record has no
117
- * such value. What cannot be shown is left out; what is shown is only what the payload actually
118
- * carries.
119
- *
120
- * `reason` is the exception. Missing and blank mean the same thing to whoever is reading — nobody
121
- * wrote one — so both draw the grey line rather than disappearing. A reason that silently vanishes
122
- * would let an approver assume it was never asked for.
123
- */
108
+ /** The decision the activity declared, or nothing - drawn by `renderDecision`, the one drawing both screens use. */
124
109
  decisionContent(): DirectiveResult<{
125
- new (_partInfo: import("lit-html/directive.js").PartInfo): import("lit-html/directives/keyed.js").Keyed<TemplateResult<1>>;
110
+ new (_partInfo: import("lit-html/directive.js").PartInfo): import("lit-html/directives/keyed.js").Keyed<TemplateResult>;
126
111
  }>;
127
- /**
128
- * Whether approving can be undone, drawn as the frame around the decision.
129
- *
130
- * Only an explicit true or false draws. Anything else — the path led nowhere, or the value is not
131
- * a boolean — draws nothing, because saying "this can be undone" on a screen that does not know is
132
- * the one mistake this line must never make.
133
- *
134
- * This is not rejectionIsFinal. That one says whether **rejecting** ends the case for good; this
135
- * says whether **approving** can be taken back. The twin actuation carries both, which is exactly
136
- * why they must not share a sentence.
137
- */
138
- private renderReversibility;
139
- /**
140
- * One slot's value as something to read.
141
- *
142
- * A scalar prints with its unit when one was declared. Anything shaped — an object, a list — goes
143
- * through fieldValue, the same laying-out the model sections use, so a payload reads the same way
144
- * wherever it appears and a four-level object does not become one long line.
145
- *
146
- * ── The sentence is built here, not frozen when the request was raised ──
147
- * Eighteen doors write a Korean sentence at issue and the issue snapshots it, so it stands in the
148
- * language of whoever raised it and an approver reading another language never catches up. Where
149
- * a door declares `valueTKey`, the sentence is made at reading time instead, out of the values
150
- * the request did freeze.
151
- *
152
- * **A door that has not moved yet draws exactly what it drew before.** That is what lets plant
153
- * move them one at a time instead of eighteen at once, and it covers a door that moved into a
154
- * locale nobody has written yet: an empty `defaultValue` means a missing key falls through to the
155
- * frozen sentence rather than printing `approval.…` on the card.
156
- */
157
- private slotText;
158
- /** The sentence this slot declares, when it declares one and the locale has it. */
159
- private sentenceOf;
160
- /** Did a person actually write something here. Whitespace is not words. */
161
- private hasWords;
162
112
  generatedContent(): DirectiveResult;
163
113
  boardContent(): DirectiveResult;
164
114
  /**
@@ -19,8 +19,9 @@ import { PageView } from '@operato/shell';
19
19
  import { DocumentPageStyles, ScrollbarStyles } from '@operato/styles';
20
20
  import { provider } from '@operato/board/board-provider.js';
21
21
  import { isReferenceStep } from '@things-factory/organization/dist-client';
22
- import { ActivityFieldsStyles, declaredLabel, fieldValue, inputSpecOf, outputSpecOf, renderGiven } from '../../components/activity-fields.js';
23
- import { decisionSlots, reversibilityIsMalformed, reversibilityOf, COMPENSABLE, slotOfRole, slotsOfRole } from '../../components/decision-slots.js';
22
+ import { ActivityFieldsStyles, inputSpecOf, outputSpecOf, renderGiven } from '../../components/activity-fields.js';
23
+ import { DecisionContentStyles, DecisionKeysFields, renderDecision } from '../../components/decision-content.js';
24
+ import { decisionSlots, slotOfRole } from '../../components/decision-slots.js';
24
25
  const ActivityApprovalFetchResult = `\
25
26
  { id
26
27
  judgment
@@ -66,11 +67,7 @@ const ActivityApprovalFetchResult = `\
66
67
  activity { id
67
68
  label
68
69
  labelTKey
69
- decisionKeys { role
70
- inputPath
71
- tKey
72
- unit
73
- }
70
+ ${DecisionKeysFields}
74
71
  model { name
75
72
  description
76
73
  active
@@ -144,6 +141,7 @@ let ActivityApprovalPage = class ActivityApprovalPage extends localize(i18next)(
144
141
  DocumentPageStyles,
145
142
  ScrollbarStyles,
146
143
  ActivityFieldsStyles,
144
+ DecisionContentStyles,
147
145
  css `
148
146
  :host {
149
147
  color: var(--ui-ink, #17242c);
@@ -305,126 +303,6 @@ let ActivityApprovalPage = class ActivityApprovalPage extends localize(i18next)(
305
303
  color: var(--ui-faint, #8a959c);
306
304
  word-break: keep-all;
307
305
  }
308
-
309
- /*
310
- * The decision, drawn from the activity's declaration.
311
- *
312
- * The order is the declaration's and the weight descends with it: what is being asked reads
313
- * first at title size, then what it is about, then the words a person wrote, then what changes
314
- * on approval, and last why it came up at all.
315
- */
316
- section[decision] .headline {
317
- font-size: var(--ui-t-headline, 19px);
318
- font-weight: 600;
319
- line-height: 1.35;
320
- color: var(--ui-ink, #17242c);
321
- word-break: keep-all;
322
- }
323
-
324
- section[decision] .subject {
325
- margin-top: 3px;
326
- font-family: var(--ui-mono, ui-monospace, Menlo, monospace);
327
- font-size: var(--ui-t-body, 13px);
328
- color: var(--ui-muted, #57676f);
329
- }
330
-
331
- section[decision] .block {
332
- margin-top: 14px;
333
- }
334
-
335
- section[decision] .block > .label {
336
- margin-bottom: 4px;
337
- font-size: var(--ui-t-small, 11.5px);
338
- color: var(--ui-muted, #57676f);
339
- }
340
-
341
- /* What a person wrote is set apart, because it is the only part nobody computed. */
342
- section[decision] .said {
343
- padding: 9px 11px;
344
- border-left: 3px solid var(--ui-line, #d5dce0);
345
- background: var(--ui-line-soft, #f2f5f7);
346
- color: var(--ui-ink, #17242c);
347
- word-break: break-word;
348
- }
349
-
350
- section[decision] .none {
351
- color: var(--ui-faint, #8a959c);
352
- }
353
-
354
- section[decision] .consequence {
355
- margin-top: 8px;
356
- padding: 9px 11px;
357
- border: 1px solid var(--ui-line, #d5dce0);
358
- border-radius: var(--ui-r-small, 6px);
359
- }
360
-
361
- section[decision] .consequence > .label {
362
- margin-bottom: 3px;
363
- font-size: var(--ui-t-small, 11.5px);
364
- color: var(--ui-muted, #57676f);
365
- }
366
-
367
- /*
368
- * Whether approving can be undone reads as the frame around the decision, not as one of its
369
- * consequences. Among them it carries the same weight as "four orders get pushed back", and
370
- * on an approval that moves equipment it is the heaviest line on the screen.
371
- */
372
- section[decision] .reversibility {
373
- display: flex;
374
- align-items: center;
375
- gap: 6px;
376
-
377
- margin-top: 12px;
378
- padding: 8px 11px;
379
- border-radius: var(--ui-r-small, 6px);
380
-
381
- background: var(--ui-line-soft, #f2f5f7);
382
- color: var(--ui-muted, #57676f);
383
- font-size: var(--ui-t-small, 11.5px);
384
- }
385
-
386
- section[decision] .reversibility[irreversible] {
387
- background: var(--md-sys-color-error-container, #ffdad6);
388
- color: var(--md-sys-color-on-error-container, #410002);
389
- font-weight: 600;
390
- }
391
-
392
- section[decision] .reversibility md-icon {
393
- font-size: 1.15em;
394
- }
395
-
396
- /*
397
- * What the decision was made from. Set flatter than the consequences — it is the record the
398
- * approver checks, not something the approval brings about.
399
- */
400
- section[decision] .evidence {
401
- margin-top: 8px;
402
- padding: 7px 0 7px 11px;
403
- border-left: 2px solid var(--ui-line, #d5dce0);
404
- }
405
-
406
- section[decision] .evidence > .label {
407
- margin-bottom: 2px;
408
- font-size: var(--ui-t-small, 11.5px);
409
- color: var(--ui-muted, #57676f);
410
- }
411
-
412
- /*
413
- * What is true right now sits apart from what the approval would change. Sharing the box the
414
- * consequences use would say the approval causes these readings.
415
- */
416
- section[decision] .current-state {
417
- margin-top: 14px;
418
- padding: 9px 11px;
419
- border: 1px dashed var(--ui-line, #d5dce0);
420
- border-radius: var(--ui-r-small, 6px);
421
- }
422
-
423
- section[decision] .current-state > .label {
424
- margin-bottom: 3px;
425
- font-size: var(--ui-t-small, 11.5px);
426
- color: var(--ui-muted, #57676f);
427
- }
428
306
  `
429
307
  ]; }
430
308
  /**
@@ -682,179 +560,11 @@ let ActivityApprovalPage = class ActivityApprovalPage extends localize(i18next)(
682
560
  * approveActivityApproval / rejectActivityApproval 은 id 와 comment 만 보낸다 — 그 값은 어디에도
683
561
  * 가지 않는다. 결재자가 적을 수 있는 것은 의견 하나이고, 그것은 화면 아래 칸이 받는다.
684
562
  */
685
- /**
686
- * The decision the activity declared, or nothing when it declared none.
687
- *
688
- * Returning nothing is how generatedContent knows to fall back to the model. An activity with no
689
- * decisionKeys draws exactly what it drew before.
690
- *
691
- * A slot whose path led nowhere is skipped rather than drawn empty. Older instances of an activity
692
- * predate a field the declaration names and an empty
693
- * bar on the screen would read as "there is no reason" when the truth is that this record has no
694
- * such value. What cannot be shown is left out; what is shown is only what the payload actually
695
- * carries.
696
- *
697
- * `reason` is the exception. Missing and blank mean the same thing to whoever is reading — nobody
698
- * wrote one — so both draw the grey line rather than disappearing. A reason that silently vanishes
699
- * would let an approver assume it was never asked for.
700
- */
563
+ /** The decision the activity declared, or nothing - drawn by `renderDecision`, the one drawing both screens use. */
701
564
  decisionContent() {
702
565
  const { activityInstance } = this.activityThread || {};
703
- const slots = decisionSlots(activityInstance?.activity?.decisionKeys, activityInstance?.input);
704
- if (!slots.length) {
705
- return nothing;
706
- }
707
- const headline = slotOfRole(slots, 'headline');
708
- const subject = slotOfRole(slots, 'subject');
709
- const reason = slotOfRole(slots, 'reason');
710
- const consequences = slotsOfRole(slots, 'consequence').filter(slot => slot.found);
711
- const evidence = slotsOfRole(slots, 'evidence').filter(slot => slot.found);
712
- const currentState = slotOfRole(slots, 'currentState');
713
- const reversibility = slotOfRole(slots, 'reversibility');
714
- const drawable = [headline, subject, currentState].some(slot => slot?.found) ||
715
- !!reason ||
716
- consequences.length ||
717
- evidence.length ||
718
- reversibilityOf(reversibility) !== undefined;
719
- /*
720
- * Every declared slot missed this payload. Saying so is not this screen's job — falling back to
721
- * the model at least shows the values that are there, and the reader is no worse off than before
722
- * the declaration existed.
723
- */
724
- if (!drawable) {
725
- return nothing;
726
- }
727
- return keyed(this.activityApproval.id, html `
728
- <section decision>
729
- ${headline?.found ? html `<div class="headline">${this.slotText(headline)}</div>` : ''}
730
- ${subject?.found ? html `<div class="subject">${this.slotText(subject)}</div>` : ''}
731
- ${reason
732
- ? html `
733
- <div class="block">
734
- <div class="label">${i18next.t('field.reason')}</div>
735
- <div class="said ${this.hasWords(reason) ? '' : 'none'}">
736
- ${this.hasWords(reason) ? this.slotText(reason) : i18next.t('text.no-reason-given')}
737
- </div>
738
- </div>
739
- `
740
- : ''}
741
- ${this.renderReversibility(reversibility)}
742
- ${evidence.length
743
- ? html `
744
- <div class="block">
745
- <div class="label">${i18next.t('label.what-it-was-judged-from')}</div>
746
- ${evidence.map(slot => html `
747
- <div class="evidence">
748
- ${declaredLabel(slot) ? html `<div class="label">${declaredLabel(slot)}</div>` : ''}
749
- <div>${this.slotText(slot)}</div>
750
- </div>
751
- `)}
752
- </div>
753
- `
754
- : ''}
755
- ${currentState?.found
756
- ? html `
757
- <div class="current-state">
758
- <div class="label">${declaredLabel(currentState) || i18next.t('label.current-state')}</div>
759
- <div>${this.slotText(currentState)}</div>
760
- </div>
761
- `
762
- : ''}
763
- ${consequences.length
764
- ? html `
765
- <div class="block">
766
- <div class="label">${i18next.t('label.what-changes')}</div>
767
- ${consequences.map(slot => html `
768
- <div class="consequence">
769
- ${declaredLabel(slot) ? html `<div class="label">${declaredLabel(slot)}</div>` : ''}
770
- <div>${this.slotText(slot)}</div>
771
- </div>
772
- `)}
773
- </div>
774
- `
775
- : ''}
776
- </section>
777
- `);
778
- }
779
- /**
780
- * Whether approving can be undone, drawn as the frame around the decision.
781
- *
782
- * Only an explicit true or false draws. Anything else — the path led nowhere, or the value is not
783
- * a boolean — draws nothing, because saying "this can be undone" on a screen that does not know is
784
- * the one mistake this line must never make.
785
- *
786
- * This is not rejectionIsFinal. That one says whether **rejecting** ends the case for good; this
787
- * says whether **approving** can be taken back. The twin actuation carries both, which is exactly
788
- * why they must not share a sentence.
789
- */
790
- renderReversibility(slot) {
791
- const said = reversibilityOf(slot);
792
- if (!said) {
793
- if (reversibilityIsMalformed(slot)) {
794
- /*
795
- * Declared, and the value is there, but not the shape this role takes — a bare sentence
796
- * where { reversible, caveat } belongs. Nothing is drawn, because this side cannot tell
797
- * which way it goes; the log is so a developer does not have to find it by hand, which is
798
- * how it was found the first time.
799
- */
800
- console.error(`[worklist] reversibility at "${slot?.inputPath}" is not { reversible, caveat } — nothing drawn`);
801
- }
802
- return '';
803
- }
804
- /*
805
- * The module's own sentence when it wrote one. 「버린 자재는 돌아오지 않습니다」 says why, which
806
- * the plain wording cannot; the boolean beside it is what decides how heavily this is drawn.
807
- */
808
- return html `
809
- <div class="reversibility" ?irreversible=${!said.reversible}>
810
- <md-icon>${said.kind === COMPENSABLE ? 'history' : said.reversible ? 'undo' : 'lock'}</md-icon>
811
- <span
812
- >${said.caveat || (said.kind === COMPENSABLE
813
- ? i18next.t('text.approval-is-compensable', { defaultValue: 'The original decision remains. A subsequent corrective action is required.' })
814
- : i18next.t(said.reversible ? 'text.approval-is-reversible' : 'text.approval-is-irreversible'))}</span
815
- >
816
- </div>
817
- `;
818
- }
819
- /**
820
- * One slot's value as something to read.
821
- *
822
- * A scalar prints with its unit when one was declared. Anything shaped — an object, a list — goes
823
- * through fieldValue, the same laying-out the model sections use, so a payload reads the same way
824
- * wherever it appears and a four-level object does not become one long line.
825
- *
826
- * ── The sentence is built here, not frozen when the request was raised ──
827
- * Eighteen doors write a Korean sentence at issue and the issue snapshots it, so it stands in the
828
- * language of whoever raised it and an approver reading another language never catches up. Where
829
- * a door declares `valueTKey`, the sentence is made at reading time instead, out of the values
830
- * the request did freeze.
831
- *
832
- * **A door that has not moved yet draws exactly what it drew before.** That is what lets plant
833
- * move them one at a time instead of eighteen at once, and it covers a door that moved into a
834
- * locale nobody has written yet: an empty `defaultValue` means a missing key falls through to the
835
- * frozen sentence rather than printing `approval.…` on the card.
836
- */
837
- slotText(slot) {
838
- const said = this.sentenceOf(slot);
839
- if (said) {
840
- return said;
841
- }
842
- const scalar = slot.value !== null && slot.value !== undefined && typeof slot.value !== 'object';
843
- if (scalar && slot.unit) {
844
- return `${slot.value} ${slot.unit}`;
845
- }
846
- return scalar ? String(slot.value) : fieldValue(slot.value);
847
- }
848
- /** The sentence this slot declares, when it declares one and the locale has it. */
849
- sentenceOf(slot) {
850
- if (!slot.valueTKey) {
851
- return '';
852
- }
853
- return String(i18next.t(slot.valueTKey, { ...(slot.params || {}), defaultValue: '' }) || '');
854
- }
855
- /** Did a person actually write something here. Whitespace is not words. */
856
- hasWords(slot) {
857
- return typeof slot.value === 'string' ? slot.value.trim().length > 0 : slot.found && slot.value != null;
566
+ const decision = renderDecision(activityInstance?.activity?.decisionKeys, activityInstance?.input);
567
+ return decision === nothing ? nothing : keyed(this.activityApproval.id, decision);
858
568
  }
859
569
  generatedContent() {
860
570
  if (!this.activityApproval) {