@things-factory/worklist 10.1.42 → 10.1.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-client/components/decision-slots.d.ts +14 -1
- package/dist-client/components/decision-slots.js +33 -1
- package/dist-client/components/decision-slots.js.map +1 -1
- package/dist-client/pages/activity-approval/activity-approval-page.js +3 -3
- package/dist-client/pages/activity-approval/activity-approval-page.js.map +1 -1
- package/dist-client/pages/activity-approval/approval-gate.d.ts +14 -6
- package/dist-client/pages/activity-approval/approval-gate.js +17 -5
- package/dist-client/pages/activity-approval/approval-gate.js.map +1 -1
- package/dist-client/route.d.ts +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/controllers/activity-instance/approval-line-input.d.ts +29 -0
- package/dist-server/controllers/activity-instance/approval-line-input.js +44 -0
- package/dist-server/controllers/activity-instance/approval-line-input.js.map +1 -0
- package/dist-server/controllers/activity-instance/assign.js +12 -2
- package/dist-server/controllers/activity-instance/assign.js.map +1 -1
- package/dist-server/controllers/activity-instance/claim-draft.d.ts +17 -0
- package/dist-server/controllers/activity-instance/claim-draft.js +30 -0
- package/dist-server/controllers/activity-instance/claim-draft.js.map +1 -0
- package/dist-server/controllers/activity-instance/discard-draft.d.ts +5 -5
- package/dist-server/controllers/activity-instance/discard-draft.js +5 -5
- package/dist-server/controllers/activity-instance/discard-draft.js.map +1 -1
- package/dist-server/controllers/activity-instance/draft.js +11 -0
- package/dist-server/controllers/activity-instance/draft.js.map +1 -1
- package/dist-server/controllers/activity-instance/issue.js +20 -0
- package/dist-server/controllers/activity-instance/issue.js.map +1 -1
- package/dist-server/controllers/activity-instance/pick.js +16 -0
- package/dist-server/controllers/activity-instance/pick.js.map +1 -1
- package/dist-server/controllers/common.js +4 -3
- package/dist-server/controllers/common.js.map +1 -1
- package/dist-server/service/index.d.ts +3 -3
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/tests/approval-line-input.test.ts +112 -0
- package/tests/delegate-thread.test.ts +101 -0
- package/tests/discard-draft.test.ts +53 -15
- package/tests/discard-versus-issue.pg.test.ts +127 -0
- package/tests/org-member-domain.test.ts +87 -0
- package/tests/pick-race.pg.test.ts +128 -0
- package/translations/en.json +3 -0
- package/translations/ja.json +3 -0
- package/translations/ko.json +3 -0
- package/translations/ms.json +3 -0
- package/translations/zh.json +3 -0
|
@@ -120,9 +120,22 @@ export declare function slotOfRole(slots: DecisionSlot[], role: string): Decisio
|
|
|
120
120
|
* a person's words, in the same neighbourhood as this screen. Same word for a module's sentence
|
|
121
121
|
* would be read as the person's. `caveat` is used nowhere in the three repositories.
|
|
122
122
|
*/
|
|
123
|
+
/**
|
|
124
|
+
* The kinds of an effect, as shell names them (shell server/reversibility, ADR-0064 decision 1).
|
|
125
|
+
*
|
|
126
|
+
* A copy, and the only one on this side: shell's client entry carries side effects and a client
|
|
127
|
+
* importing server code moves the build, so the shell file itself says screens do not import it.
|
|
128
|
+
* test/reversibility-kinds.test.ts holds this list to shell's, word for word.
|
|
129
|
+
*/
|
|
130
|
+
export declare const REVERSIBILITY_KINDS: readonly ["irreversible", "compensable", "reversible", "unknown"];
|
|
131
|
+
export type ReversibilityKind = (typeof REVERSIBILITY_KINDS)[number];
|
|
132
|
+
/** The one kind that is not undone and still not destructive: the original stays, a new record corrects it. */
|
|
133
|
+
export declare const COMPENSABLE: ReversibilityKind;
|
|
134
|
+
/** Undone by a paired operation. The only kind that agrees with reversible: true. */
|
|
135
|
+
export declare const REVERSIBLE: ReversibilityKind;
|
|
123
136
|
export type Reversibility = {
|
|
124
137
|
reversible: boolean;
|
|
125
|
-
kind?:
|
|
138
|
+
kind?: ReversibilityKind;
|
|
126
139
|
caveat?: string;
|
|
127
140
|
};
|
|
128
141
|
/**
|
|
@@ -116,6 +116,38 @@ export function slotsOfRole(slots, role) {
|
|
|
116
116
|
export function slotOfRole(slots, role) {
|
|
117
117
|
return slots.find(slot => slot.role === role);
|
|
118
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* What the declaration says about undoing this approval.
|
|
121
|
+
*
|
|
122
|
+
* The value is an object: `reversible` is required and `caveat` is the module's own sentence about
|
|
123
|
+
* it. The asymmetry is the point — the boolean decides how heavily the screen draws the band, and
|
|
124
|
+
* a sentence alone cannot decide that.
|
|
125
|
+
*
|
|
126
|
+
* { reversible: false, caveat: '버린 자재는 돌아오지 않습니다' }
|
|
127
|
+
*
|
|
128
|
+
* ── A condition is written as false ───────────────────────────────────────
|
|
129
|
+
* 「되돌릴 수 있습니다 — 단, 실적이 마감되기 전까지」 is written `false` with the condition in the
|
|
130
|
+
* caveat. Written `true` the screen draws it lightly and the reader walks past the condition. There
|
|
131
|
+
* is no third value: a middle one always draws ambiguously, and the reader has to guess which side
|
|
132
|
+
* it is on. (2026-09-11, architect's ruling)
|
|
133
|
+
*
|
|
134
|
+
* ── Not `note` ───────────────────────────────────────────────────────────
|
|
135
|
+
* ops-contract already has CommandApproval.note, and that one is **the approver's own comment** —
|
|
136
|
+
* a person's words, in the same neighbourhood as this screen. Same word for a module's sentence
|
|
137
|
+
* would be read as the person's. `caveat` is used nowhere in the three repositories.
|
|
138
|
+
*/
|
|
139
|
+
/**
|
|
140
|
+
* The kinds of an effect, as shell names them (shell server/reversibility, ADR-0064 decision 1).
|
|
141
|
+
*
|
|
142
|
+
* A copy, and the only one on this side: shell's client entry carries side effects and a client
|
|
143
|
+
* importing server code moves the build, so the shell file itself says screens do not import it.
|
|
144
|
+
* test/reversibility-kinds.test.ts holds this list to shell's, word for word.
|
|
145
|
+
*/
|
|
146
|
+
export const REVERSIBILITY_KINDS = ['irreversible', 'compensable', 'reversible', 'unknown'];
|
|
147
|
+
/** The one kind that is not undone and still not destructive: the original stays, a new record corrects it. */
|
|
148
|
+
export const COMPENSABLE = 'compensable';
|
|
149
|
+
/** Undone by a paired operation. The only kind that agrees with reversible: true. */
|
|
150
|
+
export const REVERSIBLE = 'reversible';
|
|
119
151
|
/**
|
|
120
152
|
* What the slot says, or undefined when it does not say.
|
|
121
153
|
*
|
|
@@ -129,7 +161,7 @@ export function reversibilityOf(slot) {
|
|
|
129
161
|
if (!value || typeof value !== 'object' || typeof value.reversible !== 'boolean') {
|
|
130
162
|
return undefined;
|
|
131
163
|
}
|
|
132
|
-
if (value.kind !== undefined && (!
|
|
164
|
+
if (value.kind !== undefined && (!REVERSIBILITY_KINDS.includes(value.kind) || (value.kind === REVERSIBLE) !== value.reversible))
|
|
133
165
|
return undefined;
|
|
134
166
|
return {
|
|
135
167
|
reversible: value.reversible,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decision-slots.js","sourceRoot":"","sources":["../../client/components/decision-slots.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Turns an activity's decisionKeys declaration plus an instance's input into the slots a decision\n * screen draws.\n *\n * ── Nothing is imported ───────────────────────────────────────────────────\n * No lit, no i18next. The rule is a mapping from a declaration and a payload to an ordered list,\n * and keeping it free of imports is what lets the test runner hold it: that runner parses\n * TypeScript by stripping types and reads neither decorators nor enums, so one heavy import takes\n * a whole test file down with it. Labels and markup are the caller's half.\n */\n\n/** The roles that may appear more than once. Every other role names one value. */\nexport const REPEATABLE_ROLES = ['consequence', 'evidence'] as const\n\n/** Listed in the order the screen reads them: what is asked, then past, present, future. */\nexport const DECISION_ROLES = [\n 'headline',\n 'subject',\n 'reversibility',\n 'reason',\n 'evidence',\n 'currentState',\n 'consequence'\n] as const\n\nexport type DecisionKeyDeclaration = {\n role?: string\n inputPath?: string\n name?: string\n tKey?: string\n unit?: string\n /** i18n key for the value **sentence** — `approval.<door>.<inputPath>`, computed by the server. */\n valueTKey?: string\n /** What the sentence puts in its blanks: name in the sentence → inputPath to read it from. */\n params?: { [name: string]: string }\n}\n\nexport type DecisionSlot = {\n role: string\n inputPath: string\n /**\n * 번역이 없을 때 화면이 보일 수 있는 단 하나의 글.\n *\n * 선언은 늘 이것을 주는데 슬롯이 버리고 있었다. 그래서 `tKey` 가 다섯 언어 어디에도 없으면 화면에\n * 남는 것이 점 섞인 키뿐이었다 — 보일 것이 없는 게 아니라 **가진 것을 안 나른 것**이다.\n */\n name?: string\n tKey?: string\n unit?: string\n /** i18n key for the value sentence. Absent means the frozen `value` is what this slot has. */\n valueTKey?: string\n /**\n * The sentence's blanks, **already read out of the input.**\n *\n * The declaration says where each value lives; this says what was there. Resolving here rather\n * than on the screen keeps the path language in one place — the same `valueAtPath` the slot's own\n * value goes through, so a params path that leads nowhere behaves the way an inputPath does\n * instead of inventing a second rule.\n */\n params?: { [name: string]: unknown }\n /** What the path led to. Undefined both when the path leads nowhere and when the value is unset. */\n value: unknown\n /**\n * Whether the path led anywhere at all.\n *\n * A reason nobody wrote and a reason whose path is wrong look identical in `value`, and the screen\n * owes different answers: the first is 「사유 없음」 in grey, the second is a declaration that does\n * not match the payload and must not be drawn as if the person left it blank. Folding them into\n * one value is the fallback that shifts meaning.\n */\n found: boolean\n}\n\n/**\n * Reads a dotted path out of the input.\n *\n * Dots only. An array index or a wildcard is not part of the path language — drawing a list is the\n * slot's job — so 'shift.passes[0]' looks for a key spelled \"passes[0]\", finds nothing, and reports\n * found: false. The declaration guard refuses such a path when it arrives, so this is the second\n * line rather than the first.\n *\n * A path that runs into a non-object partway reports found: false rather than throwing. The screen\n * has to draw something either way, and a thrown error here would take the whole approval down over\n * one slot.\n */\nexport function valueAtPath(input: any, path: string): { value: unknown; found: boolean } {\n const steps = String(path ?? '')\n .split('.')\n .filter(step => step.length > 0)\n\n if (!steps.length) {\n return { value: undefined, found: false }\n }\n\n let at: any = input\n\n for (const step of steps) {\n if (at === null || at === undefined || typeof at !== 'object') {\n return { value: undefined, found: false }\n }\n\n if (!Object.prototype.hasOwnProperty.call(at, step)) {\n return { value: undefined, found: false }\n }\n\n at = at[step]\n }\n\n return { value: at, found: true }\n}\n\n/**\n * The slots to draw, in the order they were declared.\n *\n * Declaration order is the screen's hierarchy: plant wrote four of these screens by hand and all\n * four put the headline first, then the subject, the reason, what changes, and why it came up. That\n * agreement is what makes the order part of the contract rather than a preference, so this function\n * neither sorts nor groups.\n *\n * Every declared key produces a slot, including one whose path leads nowhere. Dropping those would\n * leave the screen unable to say that a declaration and a payload disagree, and a reader would see\n * a screen that looks complete.\n */\nexport function decisionSlots(\n decisionKeys: DecisionKeyDeclaration[] | null | undefined,\n input: any\n): DecisionSlot[] {\n if (!decisionKeys?.length) {\n return []\n }\n\n return decisionKeys.map(key => {\n const { value, found } = valueAtPath(input, String(key?.inputPath ?? ''))\n\n return {\n role: String(key?.role ?? ''),\n inputPath: String(key?.inputPath ?? ''),\n name: key?.name,\n tKey: key?.tKey,\n unit: key?.unit,\n valueTKey: key?.valueTKey,\n params: paramsFrom(key?.params, input),\n value,\n found\n }\n })\n}\n\n/**\n * Read the sentence's blanks out of the input.\n *\n * A path that leads nowhere lands as `undefined`, the same as a slot's own value — i18next prints\n * an empty blank rather than the word `undefined`, and the sentence still says the rest. Folding it\n * into an empty string here would hide which of the two it was from anyone looking later.\n *\n * Returns undefined when nothing was declared, so a slot without a sentence carries no empty object\n * for a reader to puzzle over.\n */\nfunction paramsFrom(\n declared: { [name: string]: string } | undefined,\n input: { [key: string]: any } | undefined | null\n): { [name: string]: unknown } | undefined {\n const names = Object.keys(declared || {})\n\n if (!names.length) {\n return undefined\n }\n\n const read: { [name: string]: unknown } = {}\n\n for (const name of names) {\n read[name] = valueAtPath(input, String(declared![name] ?? '')).value\n }\n\n return read\n}\n\n/** The slots for one role, in declared order. Empty when the activity declared none. */\nexport function slotsOfRole(slots: DecisionSlot[], role: string): DecisionSlot[] {\n return slots.filter(slot => slot.role === role)\n}\n\n/**\n * The single slot for a role that names one value, or undefined.\n *\n * Reaching for a second one would be reading a declaration the guard refuses, so this answers the\n * first and does not quietly merge.\n */\nexport function slotOfRole(slots: DecisionSlot[], role: string): DecisionSlot | undefined {\n return slots.find(slot => slot.role === role)\n}\n\n/**\n * What the declaration says about undoing this approval.\n *\n * The value is an object: `reversible` is required and `caveat` is the module's own sentence about\n * it. The asymmetry is the point — the boolean decides how heavily the screen draws the band, and\n * a sentence alone cannot decide that.\n *\n * { reversible: false, caveat: '버린 자재는 돌아오지 않습니다' }\n *\n * ── A condition is written as false ───────────────────────────────────────\n * 「되돌릴 수 있습니다 — 단, 실적이 마감되기 전까지」 is written `false` with the condition in the\n * caveat. Written `true` the screen draws it lightly and the reader walks past the condition. There\n * is no third value: a middle one always draws ambiguously, and the reader has to guess which side\n * it is on. (2026-09-11, architect's ruling)\n *\n * ── Not `note` ───────────────────────────────────────────────────────────\n * ops-contract already has CommandApproval.note, and that one is **the approver's own comment** —\n * a person's words, in the same neighbourhood as this screen. Same word for a module's sentence\n * would be read as the person's. `caveat` is used nowhere in the three repositories.\n */\nexport type Reversibility = { reversible: boolean; kind?: 'reversible' | 'compensable' | 'irreversible'; caveat?: string }\n\n/**\n * What the slot says, or undefined when it does not say.\n *\n * Undefined covers both \"the path led nowhere\" and \"nobody declared the role\". Drawing nothing is\n * right for both: telling an approver an action can be taken back when the payload never said so\n * is the one mistake this line cannot make, and a truthy read would make it — the string\n * '되돌릴 수 없습니다…' is truthy.\n */\nexport function reversibilityOf(slot?: DecisionSlot): Reversibility | undefined {\n const value: any = slot?.value\n\n if (!value || typeof value !== 'object' || typeof value.reversible !== 'boolean') {\n return undefined\n }\n if (value.kind !== undefined && (!['reversible', 'compensable', 'irreversible'].includes(value.kind) || (value.kind === 'reversible') !== value.reversible)) return undefined\n\n return {\n reversible: value.reversible,\n ...(value.kind ? { kind: value.kind } : {}),\n ...(typeof value.caveat === 'string' && value.caveat.trim() ? { caveat: value.caveat } : {})\n }\n}\n\n/**\n * The value is there but not the shape this role takes.\n *\n * plant declared the role and put a sentence at that path, so the band drew nothing at all and the\n * heaviest line on the screen was silently missing. Reporting nothing to the reader is right — the\n * screen cannot tell which way it goes — but a developer should not have to find it by hand. The\n * caller logs this; the rule stays free of side effects so a test can hold it.\n */\nexport function reversibilityIsMalformed(slot?: DecisionSlot): boolean {\n return !!slot && slot.found && slot.value != null && reversibilityOf(slot) === undefined\n}\n"]}
|
|
1
|
+
{"version":3,"file":"decision-slots.js","sourceRoot":"","sources":["../../client/components/decision-slots.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,kFAAkF;AAClF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,aAAa,EAAE,UAAU,CAAU,CAAA;AAEpE,4FAA4F;AAC5F,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,UAAU;IACV,SAAS;IACT,eAAe;IACf,QAAQ;IACR,UAAU;IACV,cAAc;IACd,aAAa;CACL,CAAA;AAkDV;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,WAAW,CAAC,KAAU,EAAE,IAAY;IAClD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;SAC7B,KAAK,CAAC,GAAG,CAAC;SACV,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IAElC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QAClB,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;IAC3C,CAAC;IAED,IAAI,EAAE,GAAQ,KAAK,CAAA;IAEnB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,EAAE,KAAK,IAAI,IAAI,EAAE,KAAK,SAAS,IAAI,OAAO,EAAE,KAAK,QAAQ,EAAE,CAAC;YAC9D,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;QAC3C,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,CAAC;YACpD,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;QAC3C,CAAC;QAED,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,CAAA;IACf,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;AACnC,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa,CAC3B,YAAyD,EACzD,KAAU;IAEV,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC;QAC1B,OAAO,EAAE,CAAA;IACX,CAAC;IAED,OAAO,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;QAC5B,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,EAAE,SAAS,IAAI,EAAE,CAAC,CAAC,CAAA;QAEzE,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,GAAG,EAAE,IAAI,IAAI,EAAE,CAAC;YAC7B,SAAS,EAAE,MAAM,CAAC,GAAG,EAAE,SAAS,IAAI,EAAE,CAAC;YACvC,IAAI,EAAE,GAAG,EAAE,IAAI;YACf,IAAI,EAAE,GAAG,EAAE,IAAI;YACf,IAAI,EAAE,GAAG,EAAE,IAAI;YACf,SAAS,EAAE,GAAG,EAAE,SAAS;YACzB,MAAM,EAAE,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC;YACtC,KAAK;YACL,KAAK;SACN,CAAA;IACH,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,UAAU,CACjB,QAAgD,EAChD,KAAgD;IAEhD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAA;IAEzC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QAClB,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,MAAM,IAAI,GAAgC,EAAE,CAAA;IAE5C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,QAAS,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAA;IACtE,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,WAAW,CAAC,KAAqB,EAAE,IAAY;IAC7D,OAAO,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA;AACjD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,KAAqB,EAAE,IAAY;IAC5D,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA;AAC/C,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,cAAc,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,CAAU,CAAA;AAGpG,+GAA+G;AAC/G,MAAM,CAAC,MAAM,WAAW,GAAsB,aAAa,CAAA;AAE3D,qFAAqF;AACrF,MAAM,CAAC,MAAM,UAAU,GAAsB,YAAY,CAAA;AAIzD;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,IAAmB;IACjD,MAAM,KAAK,GAAQ,IAAI,EAAE,KAAK,CAAA;IAE9B,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACjF,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAE,mBAAyC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,UAAU,CAAC,KAAK,KAAK,CAAC,UAAU,CAAC;QAAE,OAAO,SAAS,CAAA;IAExK,OAAO;QACL,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3C,GAAG,CAAC,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC7F,CAAA;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB,CAAC,IAAmB;IAC1D,OAAO,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,IAAI,eAAe,CAAC,IAAI,CAAC,KAAK,SAAS,CAAA;AAC1F,CAAC","sourcesContent":["/**\n * Turns an activity's decisionKeys declaration plus an instance's input into the slots a decision\n * screen draws.\n *\n * ── Nothing is imported ───────────────────────────────────────────────────\n * No lit, no i18next. The rule is a mapping from a declaration and a payload to an ordered list,\n * and keeping it free of imports is what lets the test runner hold it: that runner parses\n * TypeScript by stripping types and reads neither decorators nor enums, so one heavy import takes\n * a whole test file down with it. Labels and markup are the caller's half.\n */\n\n/** The roles that may appear more than once. Every other role names one value. */\nexport const REPEATABLE_ROLES = ['consequence', 'evidence'] as const\n\n/** Listed in the order the screen reads them: what is asked, then past, present, future. */\nexport const DECISION_ROLES = [\n 'headline',\n 'subject',\n 'reversibility',\n 'reason',\n 'evidence',\n 'currentState',\n 'consequence'\n] as const\n\nexport type DecisionKeyDeclaration = {\n role?: string\n inputPath?: string\n name?: string\n tKey?: string\n unit?: string\n /** i18n key for the value **sentence** — `approval.<door>.<inputPath>`, computed by the server. */\n valueTKey?: string\n /** What the sentence puts in its blanks: name in the sentence → inputPath to read it from. */\n params?: { [name: string]: string }\n}\n\nexport type DecisionSlot = {\n role: string\n inputPath: string\n /**\n * 번역이 없을 때 화면이 보일 수 있는 단 하나의 글.\n *\n * 선언은 늘 이것을 주는데 슬롯이 버리고 있었다. 그래서 `tKey` 가 다섯 언어 어디에도 없으면 화면에\n * 남는 것이 점 섞인 키뿐이었다 — 보일 것이 없는 게 아니라 **가진 것을 안 나른 것**이다.\n */\n name?: string\n tKey?: string\n unit?: string\n /** i18n key for the value sentence. Absent means the frozen `value` is what this slot has. */\n valueTKey?: string\n /**\n * The sentence's blanks, **already read out of the input.**\n *\n * The declaration says where each value lives; this says what was there. Resolving here rather\n * than on the screen keeps the path language in one place — the same `valueAtPath` the slot's own\n * value goes through, so a params path that leads nowhere behaves the way an inputPath does\n * instead of inventing a second rule.\n */\n params?: { [name: string]: unknown }\n /** What the path led to. Undefined both when the path leads nowhere and when the value is unset. */\n value: unknown\n /**\n * Whether the path led anywhere at all.\n *\n * A reason nobody wrote and a reason whose path is wrong look identical in `value`, and the screen\n * owes different answers: the first is 「사유 없음」 in grey, the second is a declaration that does\n * not match the payload and must not be drawn as if the person left it blank. Folding them into\n * one value is the fallback that shifts meaning.\n */\n found: boolean\n}\n\n/**\n * Reads a dotted path out of the input.\n *\n * Dots only. An array index or a wildcard is not part of the path language — drawing a list is the\n * slot's job — so 'shift.passes[0]' looks for a key spelled \"passes[0]\", finds nothing, and reports\n * found: false. The declaration guard refuses such a path when it arrives, so this is the second\n * line rather than the first.\n *\n * A path that runs into a non-object partway reports found: false rather than throwing. The screen\n * has to draw something either way, and a thrown error here would take the whole approval down over\n * one slot.\n */\nexport function valueAtPath(input: any, path: string): { value: unknown; found: boolean } {\n const steps = String(path ?? '')\n .split('.')\n .filter(step => step.length > 0)\n\n if (!steps.length) {\n return { value: undefined, found: false }\n }\n\n let at: any = input\n\n for (const step of steps) {\n if (at === null || at === undefined || typeof at !== 'object') {\n return { value: undefined, found: false }\n }\n\n if (!Object.prototype.hasOwnProperty.call(at, step)) {\n return { value: undefined, found: false }\n }\n\n at = at[step]\n }\n\n return { value: at, found: true }\n}\n\n/**\n * The slots to draw, in the order they were declared.\n *\n * Declaration order is the screen's hierarchy: plant wrote four of these screens by hand and all\n * four put the headline first, then the subject, the reason, what changes, and why it came up. That\n * agreement is what makes the order part of the contract rather than a preference, so this function\n * neither sorts nor groups.\n *\n * Every declared key produces a slot, including one whose path leads nowhere. Dropping those would\n * leave the screen unable to say that a declaration and a payload disagree, and a reader would see\n * a screen that looks complete.\n */\nexport function decisionSlots(\n decisionKeys: DecisionKeyDeclaration[] | null | undefined,\n input: any\n): DecisionSlot[] {\n if (!decisionKeys?.length) {\n return []\n }\n\n return decisionKeys.map(key => {\n const { value, found } = valueAtPath(input, String(key?.inputPath ?? ''))\n\n return {\n role: String(key?.role ?? ''),\n inputPath: String(key?.inputPath ?? ''),\n name: key?.name,\n tKey: key?.tKey,\n unit: key?.unit,\n valueTKey: key?.valueTKey,\n params: paramsFrom(key?.params, input),\n value,\n found\n }\n })\n}\n\n/**\n * Read the sentence's blanks out of the input.\n *\n * A path that leads nowhere lands as `undefined`, the same as a slot's own value — i18next prints\n * an empty blank rather than the word `undefined`, and the sentence still says the rest. Folding it\n * into an empty string here would hide which of the two it was from anyone looking later.\n *\n * Returns undefined when nothing was declared, so a slot without a sentence carries no empty object\n * for a reader to puzzle over.\n */\nfunction paramsFrom(\n declared: { [name: string]: string } | undefined,\n input: { [key: string]: any } | undefined | null\n): { [name: string]: unknown } | undefined {\n const names = Object.keys(declared || {})\n\n if (!names.length) {\n return undefined\n }\n\n const read: { [name: string]: unknown } = {}\n\n for (const name of names) {\n read[name] = valueAtPath(input, String(declared![name] ?? '')).value\n }\n\n return read\n}\n\n/** The slots for one role, in declared order. Empty when the activity declared none. */\nexport function slotsOfRole(slots: DecisionSlot[], role: string): DecisionSlot[] {\n return slots.filter(slot => slot.role === role)\n}\n\n/**\n * The single slot for a role that names one value, or undefined.\n *\n * Reaching for a second one would be reading a declaration the guard refuses, so this answers the\n * first and does not quietly merge.\n */\nexport function slotOfRole(slots: DecisionSlot[], role: string): DecisionSlot | undefined {\n return slots.find(slot => slot.role === role)\n}\n\n/**\n * What the declaration says about undoing this approval.\n *\n * The value is an object: `reversible` is required and `caveat` is the module's own sentence about\n * it. The asymmetry is the point — the boolean decides how heavily the screen draws the band, and\n * a sentence alone cannot decide that.\n *\n * { reversible: false, caveat: '버린 자재는 돌아오지 않습니다' }\n *\n * ── A condition is written as false ───────────────────────────────────────\n * 「되돌릴 수 있습니다 — 단, 실적이 마감되기 전까지」 is written `false` with the condition in the\n * caveat. Written `true` the screen draws it lightly and the reader walks past the condition. There\n * is no third value: a middle one always draws ambiguously, and the reader has to guess which side\n * it is on. (2026-09-11, architect's ruling)\n *\n * ── Not `note` ───────────────────────────────────────────────────────────\n * ops-contract already has CommandApproval.note, and that one is **the approver's own comment** —\n * a person's words, in the same neighbourhood as this screen. Same word for a module's sentence\n * would be read as the person's. `caveat` is used nowhere in the three repositories.\n */\n/**\n * The kinds of an effect, as shell names them (shell server/reversibility, ADR-0064 decision 1).\n *\n * A copy, and the only one on this side: shell's client entry carries side effects and a client\n * importing server code moves the build, so the shell file itself says screens do not import it.\n * test/reversibility-kinds.test.ts holds this list to shell's, word for word.\n */\nexport const REVERSIBILITY_KINDS = ['irreversible', 'compensable', 'reversible', 'unknown'] as const\nexport type ReversibilityKind = (typeof REVERSIBILITY_KINDS)[number]\n\n/** The one kind that is not undone and still not destructive: the original stays, a new record corrects it. */\nexport const COMPENSABLE: ReversibilityKind = 'compensable'\n\n/** Undone by a paired operation. The only kind that agrees with reversible: true. */\nexport const REVERSIBLE: ReversibilityKind = 'reversible'\n\nexport type Reversibility = { reversible: boolean; kind?: ReversibilityKind; caveat?: string }\n\n/**\n * What the slot says, or undefined when it does not say.\n *\n * Undefined covers both \"the path led nowhere\" and \"nobody declared the role\". Drawing nothing is\n * right for both: telling an approver an action can be taken back when the payload never said so\n * is the one mistake this line cannot make, and a truthy read would make it — the string\n * '되돌릴 수 없습니다…' is truthy.\n */\nexport function reversibilityOf(slot?: DecisionSlot): Reversibility | undefined {\n const value: any = slot?.value\n\n if (!value || typeof value !== 'object' || typeof value.reversible !== 'boolean') {\n return undefined\n }\n if (value.kind !== undefined && (!(REVERSIBILITY_KINDS as readonly string[]).includes(value.kind) || (value.kind === REVERSIBLE) !== value.reversible)) return undefined\n\n return {\n reversible: value.reversible,\n ...(value.kind ? { kind: value.kind } : {}),\n ...(typeof value.caveat === 'string' && value.caveat.trim() ? { caveat: value.caveat } : {})\n }\n}\n\n/**\n * The value is there but not the shape this role takes.\n *\n * plant declared the role and put a sentence at that path, so the band drew nothing at all and the\n * heaviest line on the screen was silently missing. Reporting nothing to the reader is right — the\n * screen cannot tell which way it goes — but a developer should not have to find it by hand. The\n * caller logs this; the rule stays free of side effects so a test can hold it.\n */\nexport function reversibilityIsMalformed(slot?: DecisionSlot): boolean {\n return !!slot && slot.found && slot.value != null && reversibilityOf(slot) === undefined\n}\n"]}
|
|
@@ -20,7 +20,7 @@ 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
22
|
import { ActivityFieldsStyles, declaredLabel, fieldValue, inputSpecOf, outputSpecOf, renderGiven } from '../../components/activity-fields.js';
|
|
23
|
-
import { decisionSlots, reversibilityIsMalformed, reversibilityOf, slotOfRole, slotsOfRole } from '../../components/decision-slots.js';
|
|
23
|
+
import { decisionSlots, reversibilityIsMalformed, reversibilityOf, COMPENSABLE, slotOfRole, slotsOfRole } from '../../components/decision-slots.js';
|
|
24
24
|
const ActivityApprovalFetchResult = `\
|
|
25
25
|
{ id
|
|
26
26
|
judgment
|
|
@@ -806,9 +806,9 @@ let ActivityApprovalPage = class ActivityApprovalPage extends localize(i18next)(
|
|
|
806
806
|
*/
|
|
807
807
|
return html `
|
|
808
808
|
<div class="reversibility" ?irreversible=${!said.reversible}>
|
|
809
|
-
<md-icon>${said.kind ===
|
|
809
|
+
<md-icon>${said.kind === COMPENSABLE ? 'history' : said.reversible ? 'undo' : 'lock'}</md-icon>
|
|
810
810
|
<span
|
|
811
|
-
>${said.caveat || (said.kind ===
|
|
811
|
+
>${said.caveat || (said.kind === COMPENSABLE
|
|
812
812
|
? i18next.t('text.approval-is-compensable', { defaultValue: 'The original decision remains. A subsequent corrective action is required.' })
|
|
813
813
|
: i18next.t(said.reversible ? 'text.approval-is-reversible' : 'text.approval-is-irreversible'))}</span
|
|
814
814
|
>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activity-approval-page.js","sourceRoot":"","sources":["../../../client/pages/activity-approval/activity-approval-page.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@operato/list-state/ox-page-state.js'\nimport '@operato/property-editor/ox-properties-dynamic-view.js'\nimport '@operato/board/ox-board-viewer.js'\nimport '../../components/activity-approval-ribon.js'\nimport '../../components/activity-thread-timeline.js'\n\nimport gql from 'graphql-tag'\nimport { TemplateResult, css, html, nothing, unsafeCSS } from 'lit'\nimport { DirectiveResult } from 'lit-html/directive.js'\nimport { unsafeHTML } from 'lit-html/directives/unsafe-html.js'\nimport { customElement, query, state } from 'lit/decorators.js'\nimport { keyed } from 'lit/directives/keyed.js'\nimport { client } from '@operato/graphql'\nimport { OxPrompt } from '@operato/popup'\nimport { i18next, localize } from '@operato/i18n'\nimport { ranMutation } from '../../components/say-outcome.js'\nimport { approvalGate } from './approval-gate.js'\nimport { PageView } from '@operato/shell'\nimport { DocumentPageStyles, ScrollbarStyles } from '@operato/styles'\nimport { provider } from '@operato/board/board-provider.js'\nimport { isReferenceStep } from '@things-factory/organization/dist-client'\n\nimport {\n ActivityFieldsStyles,\n declaredLabel,\n fieldValue,\n inputSpecOf,\n outputSpecOf,\n renderGiven\n} from '../../components/activity-fields.js'\nimport {\n DecisionSlot,\n decisionSlots,\n reversibilityIsMalformed,\n reversibilityOf,\n slotOfRole,\n slotsOfRole\n} from '../../components/decision-slots.js'\n\nconst ActivityApprovalFetchResult = `\\\n{ id\n judgment\n comment\n approver { id\n name\n }\n round\n order\n createdAt\n terminatedAt\n activityThread { state\n dueAt\n assignedAt\n assignee { id\n name\n }\n updatedAt\n terminatedAt\n output\n activityApprovals { round\n order\n approver { name\n }\n judgment\n comment\n createdAt\n updatedAt\n terminatedAt\n }\n activityThreadHistories { transaction\n reason\n updater { name\n }\n updatedAt\n state\n output\n }\n activityInstance { id\n name\n description\n state\n activity { id\n label\n labelTKey\n decisionKeys { role\n inputPath\n tKey\n unit\n }\n model { name\n description\n active\n tag\n inout\n type\n unit\n options\n quantifier\n spec\n hidden\n }\n }\n approvalLine { type\n value\n participation\n approver { id\n name\n description\n controlNo\n }\n }\n thumbnail\n input\n output\n viewType\n viewSource\n dueAt\n createdAt\n } \n }\n}`\n\nexport const ActivityThreadStatus = {\n Unassigned: 'unassigned',\n Assigned: 'assigned',\n Started: 'started',\n Delegated: 'delegated',\n Ended: 'ended',\n Aborted: 'aborted'\n}\n\n/** 타임라인이 옆에 서는 폭 — 읽는 단 1080 + 사이 24 + 최소 판 360 + page 좌우 40. */\nconst BESIDE = '(min-width: 1504px)'\n\n@customElement('activity-approval-page')\nexport class ActivityApprovalPage extends localize(i18next)(PageView) {\n static styles = [\n /* The frame owns the page shell and the reading column (ADR-0060 decision 8). */\n DocumentPageStyles,\n ScrollbarStyles,\n ActivityFieldsStyles,\n css`\n :host {\n color: var(--ui-ink, #17242c);\n font-size: var(--ui-t-body, 13px);\n }\n\n /*\n * 넓을 때만 타임라인이 옆에 선다 — **읽는 단은 줄이지 않는다.**\n *\n * 결재자가 판단에 쓰는 것(결정 · 사유 · 의견)이 왼쪽이고, 참고하는 것(타임라인)이 오른쪽이다.\n * 처음 낸 안은 읽는 단 1080 안에서 둘로 쪼개는 것이었는데, 그러면 결정 카드가 1014 에서\n * 520 으로 줄어든다 — 주된 것을 좁혀 참고용 자리를 만드는 꼴이라 물렀다.\n *\n * 그래서 **읽는 단을 넘는 여분이 한 판만큼 있을 때만** 편다:\n *\n * 1080(읽는 단) + 24(사이) + 360(최소 판) + 40(page 좌우 여백) = 1504\n *\n * 그 아래에서는 한 단이고 차례가 그대로다 — 결재선 · 결정 · 타임라인 · 의견. 좁은 화면이\n * 기본이고 넓은 화면이 덤이다.\n */\n @media ${unsafeCSS(BESIDE)} {\n .page {\n max-width: none;\n display: grid;\n grid-template-columns: var(--ui-measure, 1080px) minmax(360px, 1fr);\n column-gap: 24px;\n align-items: start;\n }\n\n /*\n * 두 판의 자리는 문서 차례가 아니라 격자가 정한다. 문서 차례는 좁은 화면 그대로 두어야\n * 하고(결재선 · 결정 · 타임라인 · 의견), 넓은 화면에서만 타임라인이 오른쪽으로 빠진다.\n */\n .main {\n grid-column: 1;\n min-width: 0;\n }\n\n /* 옆에 선 타임라인은 읽는 동안 따라온다 — 결정과 맞대어 보는 값이다. */\n .aside {\n grid-column: 2;\n grid-row: 1 / span 3;\n position: sticky;\n top: 0;\n }\n }\n\n .comment-actions {\n display: flex;\n justify-content: flex-end;\n }\n\n .comment-actions button {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n height: var(--form-action-height, 32px);\n padding: 0 12px;\n border: 1px solid var(--ui-line, var(--md-sys-color-outline-variant));\n border-radius: var(--ui-r-small, 6px);\n background: transparent;\n color: var(--ui-ink, var(--md-sys-color-on-surface));\n font: inherit;\n font-size: var(--ui-t-body, 13px);\n cursor: pointer;\n }\n\n .comment-actions md-icon {\n font-size: 16px;\n --md-icon-size: 16px;\n }\n\n /* 카드가 담아 주므로 안에 들어오는 화면은 자기 면과 여백을 갖지 않는다. */\n /*\n * 줄어들지 않는다 — 줄어들면 본문이 잘린다.\n *\n * flex 항목인데 크기를 안 정해 두면 기본이 flex: 0 1 auto 다. **1 이 줄어든다는 뜻이다** —\n * 세로가 모자라면 브라우저가 이 칸부터 줄이는데, 여기엔 카드 모서리를 따라 자르려고\n * overflow: hidden 이 걸려 있어서 줄어든 만큼이 **스크롤도 없이 사라진다.**\n *\n * 완료 조회에서 실측했다(1400x620): 칸이 242px 로 줄고 내용은 362px 이라 「업무 내용」 절이\n * 대부분 잘렸고, 잘린 자리 바로 아래에서 타임라인이 시작하니 타임라인이 본문을 덮은 것처럼\n * 보였다. 세로가 넉넉하면 줄일 일이 없어 안 드러난다.\n *\n * 안에 오는 것은 문서다. 높이를 정해 주어야 하는 것은 board 뿐이고 board 는 이 칸을 거치지\n * 않는다(activityContent 참조). 문서는 제 높이로 서고, 모자라면 페이지 전체가 스크롤한다 —\n * :host 의 overflow: auto 가 그 자리다.\n */\n section[hosted] {\n flex: none;\n padding: 0;\n overflow: hidden;\n }\n\n ox-board-viewer {\n width: 100%;\n height: 100%;\n }\n\n activity-thread-timeline {\n margin: var(--spacing-large, 16px);\n }\n\n /*\n * 의견 칸 — 이 화면에서 **결재자가 유일하게 적는 자리**다.\n *\n * 화면 아래에 붙어 있어(sticky) 목록을 내려 읽는 동안에도 보인다. 그 자리를 지키되 모양은\n * 다른 절과 같은 카드로 둔다. 전에는 회색 면(surface-variant)에 다른 테두리라 이 화면에서\n * 혼자 다른 부품처럼 보였다.\n */\n /*\n * 의견 — 이 화면에서 **결재자가 유일하게 적는 자리**다.\n *\n * 화면 아래에 붙어 있어(sticky) 내용을 내려 읽는 동안에도 보인다. 그 자리를 지키되 모양은\n * 다른 절과 같은 카드로 둔다. 전에는 회색 면(surface-variant)에 다른 테두리라 이 화면에서\n * 혼자 다른 부품처럼 보였다.\n */\n div[comment] {\n position: sticky;\n bottom: 0;\n padding-bottom: var(--spacing-large, 16px);\n background: var(--ui-bg, #f0f2f4);\n }\n\n /*\n * 이 화면에서 **사용자가 유일하게 손대는 자리**라는 것을 모양으로 말한다.\n *\n * 나머지 절은 전부 읽는 것이고, 판단은 아래 툴바의 단추가 받는다. 그런데 의견 칸이 읽는\n * 절들과 똑같은 카드라 「내 차례가 어디인지」가 화면에 없었다. 왼쪽에 강조색 띠를 둔다 —\n * sticky 라 툴바 바로 위에 붙으므로 띠와 단추가 한 덩어리로 읽힌다.\n */\n div[comment] section {\n border-left: 3px solid var(--ui-accent, #00727e);\n }\n\n div[comment] textarea {\n display: block;\n box-sizing: border-box;\n width: 100%;\n height: 76px;\n padding: 10px 12px;\n background: var(--ui-panel, #ffffff);\n border: 1px solid var(--ui-line, #d5dce0);\n border-radius: var(--ui-r-small, 6px);\n color: var(--ui-ink, #17242c);\n font: inherit;\n font-size: var(--ui-t-body, 13px);\n resize: none;\n outline: none;\n }\n\n div[comment] textarea:focus {\n border-color: var(--ui-accent, #00727e);\n }\n\n div[comment] .hint {\n margin: 7px 0 0;\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-faint, #8a959c);\n word-break: keep-all;\n }\n\n /*\n * The decision, drawn from the activity's declaration.\n *\n * The order is the declaration's and the weight descends with it: what is being asked reads\n * first at title size, then what it is about, then the words a person wrote, then what changes\n * on approval, and last why it came up at all.\n */\n section[decision] .headline {\n font-size: var(--ui-t-headline, 19px);\n font-weight: 600;\n line-height: 1.35;\n color: var(--ui-ink, #17242c);\n word-break: keep-all;\n }\n\n section[decision] .subject {\n margin-top: 3px;\n font-family: var(--ui-mono, ui-monospace, Menlo, monospace);\n font-size: var(--ui-t-body, 13px);\n color: var(--ui-muted, #57676f);\n }\n\n section[decision] .block {\n margin-top: 14px;\n }\n\n section[decision] .block > .label {\n margin-bottom: 4px;\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-muted, #57676f);\n }\n\n /* What a person wrote is set apart, because it is the only part nobody computed. */\n section[decision] .said {\n padding: 9px 11px;\n border-left: 3px solid var(--ui-line, #d5dce0);\n background: var(--ui-line-soft, #f2f5f7);\n color: var(--ui-ink, #17242c);\n word-break: break-word;\n }\n\n section[decision] .none {\n color: var(--ui-faint, #8a959c);\n }\n\n section[decision] .consequence {\n margin-top: 8px;\n padding: 9px 11px;\n border: 1px solid var(--ui-line, #d5dce0);\n border-radius: var(--ui-r-small, 6px);\n }\n\n section[decision] .consequence > .label {\n margin-bottom: 3px;\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-muted, #57676f);\n }\n\n /*\n * Whether approving can be undone reads as the frame around the decision, not as one of its\n * consequences. Among them it carries the same weight as \"four orders get pushed back\", and\n * on an approval that moves equipment it is the heaviest line on the screen.\n */\n section[decision] .reversibility {\n display: flex;\n align-items: center;\n gap: 6px;\n\n margin-top: 12px;\n padding: 8px 11px;\n border-radius: var(--ui-r-small, 6px);\n\n background: var(--ui-line-soft, #f2f5f7);\n color: var(--ui-muted, #57676f);\n font-size: var(--ui-t-small, 11.5px);\n }\n\n section[decision] .reversibility[irreversible] {\n background: var(--md-sys-color-error-container, #ffdad6);\n color: var(--md-sys-color-on-error-container, #410002);\n font-weight: 600;\n }\n\n section[decision] .reversibility md-icon {\n font-size: 1.15em;\n }\n\n /*\n * What the decision was made from. Set flatter than the consequences — it is the record the\n * approver checks, not something the approval brings about.\n */\n section[decision] .evidence {\n margin-top: 8px;\n padding: 7px 0 7px 11px;\n border-left: 2px solid var(--ui-line, #d5dce0);\n }\n\n section[decision] .evidence > .label {\n margin-bottom: 2px;\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-muted, #57676f);\n }\n\n /*\n * What is true right now sits apart from what the approval would change. Sharing the box the\n * consequences use would say the approval causes these readings.\n */\n section[decision] .current-state {\n margin-top: 14px;\n padding: 9px 11px;\n border: 1px dashed var(--ui-line, #d5dce0);\n border-radius: var(--ui-r-small, 6px);\n }\n\n section[decision] .current-state > .label {\n margin-bottom: 3px;\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-muted, #57676f);\n }\n `\n ]\n\n @state() activityApproval: any\n /**\n * 타임라인이 옆에 설 만큼 넓은가.\n *\n * 폭은 CSS 가 정하지만 타임라인을 펴는 것은 속성이라 CSS 로 안 된다. 같은 값을 두 곳에 적지\n * 않도록 물음은 하나이고(`BESIDE`), 그 물음을 CSS 와 여기가 함께 쓴다.\n */\n @state() private beside = false\n\n /**\n * 조회를 마쳤나 — **「아직 안 읽었다」와 「없다」는 다르다.**\n *\n * 전에는 값이 없으면 곧바로 「업무활동 정보가 없습니다」를 그렸다. 그런데 이 화면은 열리자마자\n * 조회를 시작하므로, 응답이 오기 전까지 **없다고 단언하는 화면**이 보인다. 목록에서 줄을 눌러\n * 들어온 사람은 방금 본 건이 없다는 말을 먼저 읽는다.\n *\n * 조회가 끝난 뒤에만 그 말을 한다. 그 전에는 아무 말도 하지 않는다.\n */\n @state() loaded = false\n /** Why the approval could not be read - ox-page-state says it, with retry (ADR-0060 decision 8). */\n @state() loadError: unknown = null\n private lastId?: string\n private approvalRead = 0\n\n @state() activityThread: any\n @state() board: any\n\n @query('textarea') commentInput!: HTMLTextAreaElement\n\n /**\n * Is the reader seated here as a reference — someone who comments and does not decide?\n *\n * The rule comes from organization's client types, the same function its own approval-line editor\n * and list use, so the two wire spellings of the enum are handled in one place. The approval line\n * answers rather than ActivityApproval.participation, matching what the server refuses on\n * (isReferenceOrder in approval-line.ts): the line decides who comes next, so letting the row\n * decide who may judge would leave two authorities with nothing to settle a disagreement.\n *\n * The value comes back false while the query is still out. That is the same answer as for an\n * approver, so the buttons show for a moment on a reference's screen — pressing one is refused by\n * the server, which is why the server closed first.\n */\n get isReference(): boolean {\n const approvalLine = this.activityThread?.activityInstance?.approvalLine\n const order = Number(this.activityApproval?.order ?? 0)\n\n return isReferenceStep(approvalLine?.[order - 1])\n }\n\n get context() {\n const judgment = this.activityApproval?.judgment\n\n return {\n /*\n * 이 화면이 어떤 틀인가 — 동작 줄이 이것을 읽고 자기 오른쪽 끝을 내용에 맞춘다\n * (ADR-0060 결정 9 보탬). 안 적으면 창 폭이고, 그것은 작업면의 모양이다.\n */\n frame: 'document',\n title: this.lifecycle?.params?.['title'] || i18next.t('title.activity approval'),\n help: 'worklist/activity-approval',\n /*\n * 이 화면은 메뉴에 없다 — 「내 결재함」에서 줄을 눌러 들어온다. 그래서 왼쪽 메뉴가 가리킬\n * 항목이 없고, 표시가 사라져 들어온 사람이 자기가 어디에 있는지 잃는다.\n *\n * 어느 목록에서 열리는지는 이 화면이 안다. 그것을 적어 둔다.\n */\n menu: 'approval-pending-list',\n /*\n * A reference is offered neither approve nor reject.\n *\n * ActivityApprovalJudgment has carried Closed for this seat from the start — \"the case ended\"\n * rather than a judgment — so a reference row was never meant to end in approved or rejected.\n * The buttons were offered anyway, and the server accepted them until 2026-09-09.\n *\n * What stays is the note, because a comment is the whole point of the seat, and close. The\n * hint under the textarea says which seat this is; buttons that simply vanish leave the\n * reader looking for them.\n */\n actions: judgment\n ? []\n : this.isReference\n ? []\n : /*\n * ── 결정 단추의 순서와 무게 (ADR-0060 결정 9 보탬) ─────────────\n * 전에는 반려가 첫 자리였고, 승인 · 반려 · 저장이 모두 같은 모양이었다. 되돌리기 어려운\n * 것이 앞에 서고 무엇이 주된 흐름인지 화면이 말하지 않았다.\n *\n * 승인이 주 동작이다 — 채운 단추 하나. 반려는 대등한 선택이지만 예외 경로라 테두리\n * 단추로 그 옆에 둔다.\n *\n * 「저장」은 이름이 뜻을 말하지 않았다. 이 단추가 저장하는 것은 **의견 하나**이고\n * (saveActivityApproval 은 id 와 comment 만 보낸다), 판단은 미뤄 둔다. 그리고 그 결과는\n * 목록에서 「메모 있음」 표식으로 나타난다(activity-cells 의 approvalMarks).\n * 메모 저장은 아래 의견 상자 옆에 두고, 이 줄에는 승인·반려만 둔다.\n */\n [\n {\n title: i18next.t('button.approve'),\n action: this.approveActivityApproval.bind(this),\n icon: 'approval',\n emphasis: { raised: true, outlined: false, dense: false, danger: false }\n },\n\n {\n title: i18next.t('button.reject'),\n action: this.rejectActivityApproval.bind(this),\n icon: 'assignment_return',\n emphasis: { raised: false, outlined: true, dense: false, danger: false }\n },\n\n ]\n // activityApproval: this.activityApproval\n }\n }\n\n private media?: MediaQueryList\n private onBeside = () => (this.beside = !!this.media?.matches)\n\n connectedCallback() {\n super.connectedCallback()\n this.media = window.matchMedia(BESIDE)\n this.onBeside()\n this.media.addEventListener('change', this.onBeside)\n }\n\n disconnectedCallback() {\n this.media?.removeEventListener('change', this.onBeside)\n super.disconnectedCallback()\n }\n\n render() {\n /* Reading and failing both drew nothing at all, so a failure looked like an empty screen. */\n return html`<div class=\"page\">\n <ox-page-state\n .loading=${!this.loaded && !this.loadError}\n .error=${this.loadError}\n @retry=${() => this.lastId && this.fetchActivityApproval(this.lastId)}\n >${\n this.activityThread\n ? this.renderApproval()\n : this.loaded\n ? html`<div empty>\n <span class=\"title\">${i18next.t('text.no activity thread info')}</span>\n <span class=\"why\">${i18next.t('text.no-activity-thread-why')}</span>\n </div>`\n : nothing\n }</ox-page-state\n >\n </div>`\n }\n\n private renderApproval() {\n const { terminatedAt } = this.activityApproval\n const editable = !terminatedAt\n\n return html`\n <div class=\"main\">\n <activity-approval-ribon .activityApproval=${this.activityApproval}></activity-approval-ribon>\n ${this.activityContent()}\n </div>\n <!--\n 보일 항목이 하나도 없으면 타임라인을 편다 — 그때는 화면이 「아래 타임라인의 상신 사유와\n 이력을 보고 판단하세요」로 여기를 가리키므로, 접혀 있으면 그 안내가 거짓말이 된다.\n\n 옆에 설 자리가 있을 때도 편다. 접힌 줄을 눌러 펴는 동작이, 그 줄이 이미 보이는 자리에\n 있으면 하는 일이 없다.\n -->\n <activity-thread-timeline\n class=\"aside\"\n .activityThread=${this.activityThread}\n ?open=${!this.hasFields() || this.beside}\n short-form\n ></activity-thread-timeline>\n ${\n editable\n ? html`\n <div comment class=\"main\">\n <section>\n <h2>${i18next.t('field.wl-comment')}</h2>\n <textarea\n placeholder=${String(\n i18next.t(\n this.isReference\n ? 'text.write-your-comment-as-reference'\n : 'text.explain the reason for approval/rejection'\n )\n )}\n .value=${this.activityApproval?.comment}\n @input=${(e: Event) => {\n this.activityApproval.comment = (e.target as HTMLTextAreaElement).value\n }}\n @change=${(e: Event) => {\n this.activityApproval.comment = (e.target as HTMLTextAreaElement).value\n }}\n ></textarea>\n <!--\n 반려는 의견이 필수다(rejectActivityApproval). 전에는 그 규칙을 **누른 뒤에** 토스트로\n 알렸다. 되돌릴 수 없는 것과 뜻이 다른 동작은 하기 전에 말한다.\n\n A reference reads a different line: this one names the seat, because the approve\n and reject buttons are absent for them and a reader whose buttons are missing\n goes looking for them.\n -->\n <p class=\"hint\">\n ${i18next.t(this.isReference ? 'text.reference-comment-hint' : 'text.approval-comment-hint')}\n </p>\n\n <!--\n 의견을 저장하는 단추는 의견 상자 옆이다(ADR-0060 결정 9 보탬). 동작 줄에는\n 결정만 둔다 — 전에는 승인 · 반려와 같은 줄에 같은 무게로 서서 셋 다 결정처럼\n 읽혔고, 좁은 화면에서는 넘침 메뉴로 밀려 반려가 숨을 수 있었다.\n -->\n <div class=\"comment-actions\">\n <button @click=${this.saveActivityApproval.bind(this)}>\n <md-icon>edit_note</md-icon>${i18next.t('button.save-note')}\n </button>\n </div>\n </section>\n </div>\n `\n : nothing\n }\n `\n }\n\n /** 화면에 낼 항목이 하나라도 있나. 타임라인을 펴 둘지 정하는 자리가 쓴다. */\n hasFields(): boolean {\n const model = this.activityThread?.activityInstance?.activity?.model\n\n return inputSpecOf(model).length > 0 || outputSpecOf(model).length > 0\n }\n\n activityContent() {\n switch (this.activityThread?.activityInstance?.viewType) {\n case 'template':\n return this.hostedContent(this.templateContent())\n case 'generated':\n return this.generatedContent()\n /* board 는 전체 화면을 쓰는 캔버스라 카드에 담지 않는다(hostedContent 머리말). */\n case 'board':\n return this.boardContent()\n case 'custom-element': {\n /*\n * The wrapper is for the element, not for the notice.\n *\n * hostedContent puts the content in a card so every ui type sits at the same place with the\n * same frame. The not-loaded notice is already a card — wrapping it drew a card inside a\n * card, two borders deep, and its margins landed unevenly: 16 above and none below, inside\n * a box that had its own padding. The generated branch returns the same notice unwrapped\n * and looks right.\n */\n const { viewSource: tagName } = this.activityThread?.activityInstance || {}\n\n /*\n * Naming no element is not the same as naming one that is missing.\n *\n * A template that stops providing a screen clears the field — installActivity does not\n * blank what a template leaves undefined, so clearing is written as null. That left\n * viewType saying custom-element with nothing named, and this branch answered \"the\n * screen could not be loaded\": a failure, for an activity that simply has no screen of its\n * own. Those read as different things to whoever is approving, and the second one is not\n * true.\n *\n * With nothing named, the generated screen is what this activity has — and it draws a\n * decision when the activity declares one, which the two plant forms that hit this do.\n */\n if (!tagName) {\n return this.generatedContent()\n }\n\n const element = this.customElementContent()\n\n return element ? this.hostedContent(element) : this.screenNotLoaded()\n }\n case 'page':\n case 'external':\n return html``\n default:\n return this.generatedContent()\n }\n }\n\n /**\n * 화면 종류가 달라도 **같은 자리에 같은 모양으로 놓인다.**\n *\n * worklist 는 네 가지로 내용을 그린다(generated · custom-element · template · board). 그중\n * generated 만 절 카드 안에 있었고 나머지 셋은 면 위에 맨몸으로 놓였다. 그래서 같은 결재 화면이\n * 액티비티에 따라 카드였다가 아니었다가 했다 — 아래의 타임라인 · 의견은 늘 카드라 위계도 어긋났다.\n *\n * board 는 뺀다. 전체 폭 · 전체 높이를 쓰는 캔버스라 카드 안에 넣으면 여백이 두 겹이 되고 크기가\n * 어긋난다.\n *\n * 카드는 여백을 두지 않는다(padding: 0). 안에 들어오는 것이 자기 조판을 갖고 있고, 두 겹으로\n * 주면 안쪽 내용이 카드 가운데로 밀린다.\n */\n hostedContent(content: unknown) {\n return html`<section hosted>${content}</section>`\n }\n\n templateContent() {\n if (!this.activityApproval) {\n return html``\n }\n\n const { viewSource: template } = this.activityThread?.activityInstance\n return unsafeHTML(template)\n }\n\n /**\n * 결재자가 보는 내용 — **전부 읽기 전용이다.**\n *\n * 전에는 「Output」 절을 편집기로 그리고 변경을 activityApproval.output 에 적었다. 그런데\n * approveActivityApproval / rejectActivityApproval 은 id 와 comment 만 보낸다 — 그 값은 어디에도\n * 가지 않는다. 결재자가 적을 수 있는 것은 의견 하나이고, 그것은 화면 아래 칸이 받는다.\n */\n /**\n * The decision the activity declared, or nothing when it declared none.\n *\n * Returning nothing is how generatedContent knows to fall back to the model. An activity with no\n * decisionKeys draws exactly what it drew before.\n *\n * A slot whose path led nowhere is skipped rather than drawn empty. Older instances of an activity\n * predate a field the declaration names and an empty\n * bar on the screen would read as \"there is no reason\" when the truth is that this record has no\n * such value. What cannot be shown is left out; what is shown is only what the payload actually\n * carries.\n *\n * `reason` is the exception. Missing and blank mean the same thing to whoever is reading — nobody\n * wrote one — so both draw the grey line rather than disappearing. A reason that silently vanishes\n * would let an approver assume it was never asked for.\n */\n decisionContent() {\n const { activityInstance } = this.activityThread || {}\n const slots = decisionSlots(activityInstance?.activity?.decisionKeys, activityInstance?.input)\n\n if (!slots.length) {\n return nothing\n }\n\n const headline = slotOfRole(slots, 'headline')\n const subject = slotOfRole(slots, 'subject')\n const reason = slotOfRole(slots, 'reason')\n const consequences = slotsOfRole(slots, 'consequence').filter(slot => slot.found)\n const evidence = slotsOfRole(slots, 'evidence').filter(slot => slot.found)\n const currentState = slotOfRole(slots, 'currentState')\n const reversibility = slotOfRole(slots, 'reversibility')\n\n const drawable =\n [headline, subject, currentState].some(slot => slot?.found) ||\n !!reason ||\n consequences.length ||\n evidence.length ||\n reversibilityOf(reversibility) !== undefined\n\n /*\n * Every declared slot missed this payload. Saying so is not this screen's job — falling back to\n * the model at least shows the values that are there, and the reader is no worse off than before\n * the declaration existed.\n */\n if (!drawable) {\n return nothing\n }\n\n return keyed(\n this.activityApproval.id,\n html`\n <section decision>\n ${headline?.found ? html`<div class=\"headline\">${this.slotText(headline)}</div>` : ''}\n ${subject?.found ? html`<div class=\"subject\">${this.slotText(subject)}</div>` : ''}\n ${\n reason\n ? html`\n <div class=\"block\">\n <div class=\"label\">${i18next.t('field.reason')}</div>\n <div class=\"said ${this.hasWords(reason) ? '' : 'none'}\">\n ${this.hasWords(reason) ? this.slotText(reason) : i18next.t('text.no-reason-given')}\n </div>\n </div>\n `\n : ''\n }\n ${this.renderReversibility(reversibility)}\n ${\n evidence.length\n ? html`\n <div class=\"block\">\n <div class=\"label\">${i18next.t('label.what-it-was-judged-from')}</div>\n ${evidence.map(\n slot => html`\n <div class=\"evidence\">\n ${declaredLabel(slot) ? html`<div class=\"label\">${declaredLabel(slot)}</div>` : ''}\n <div>${this.slotText(slot)}</div>\n </div>\n `\n )}\n </div>\n `\n : ''\n }\n ${\n currentState?.found\n ? html`\n <div class=\"current-state\">\n <div class=\"label\">${declaredLabel(currentState) || i18next.t('label.current-state')}</div>\n <div>${this.slotText(currentState)}</div>\n </div>\n `\n : ''\n }\n ${\n consequences.length\n ? html`\n <div class=\"block\">\n <div class=\"label\">${i18next.t('label.what-changes')}</div>\n ${consequences.map(\n slot => html`\n <div class=\"consequence\">\n ${declaredLabel(slot) ? html`<div class=\"label\">${declaredLabel(slot)}</div>` : ''}\n <div>${this.slotText(slot)}</div>\n </div>\n `\n )}\n </div>\n `\n : ''\n }\n </section>\n `\n )\n }\n\n /**\n * Whether approving can be undone, drawn as the frame around the decision.\n *\n * Only an explicit true or false draws. Anything else — the path led nowhere, or the value is not\n * a boolean — draws nothing, because saying \"this can be undone\" on a screen that does not know is\n * the one mistake this line must never make.\n *\n * This is not rejectionIsFinal. That one says whether **rejecting** ends the case for good; this\n * says whether **approving** can be taken back. The twin actuation carries both, which is exactly\n * why they must not share a sentence.\n */\n private renderReversibility(slot?: DecisionSlot) {\n const said = reversibilityOf(slot)\n\n if (!said) {\n if (reversibilityIsMalformed(slot)) {\n /*\n * Declared, and the value is there, but not the shape this role takes — a bare sentence\n * where { reversible, caveat } belongs. Nothing is drawn, because this side cannot tell\n * which way it goes; the log is so a developer does not have to find it by hand, which is\n * how it was found the first time.\n */\n console.error(`[worklist] reversibility at \"${slot?.inputPath}\" is not { reversible, caveat } — nothing drawn`)\n }\n\n return ''\n }\n\n /*\n * The module's own sentence when it wrote one. 「버린 자재는 돌아오지 않습니다」 says why, which\n * the plain wording cannot; the boolean beside it is what decides how heavily this is drawn.\n */\n return html`\n <div class=\"reversibility\" ?irreversible=${!said.reversible}>\n <md-icon>${said.kind === 'compensable' ? 'history' : said.reversible ? 'undo' : 'lock'}</md-icon>\n <span\n >${\n said.caveat || (said.kind === 'compensable'\n ? i18next.t('text.approval-is-compensable', { defaultValue: 'The original decision remains. A subsequent corrective action is required.' })\n : i18next.t(said.reversible ? 'text.approval-is-reversible' : 'text.approval-is-irreversible'))\n }</span\n >\n </div>\n `\n }\n\n /**\n * One slot's value as something to read.\n *\n * A scalar prints with its unit when one was declared. Anything shaped — an object, a list — goes\n * through fieldValue, the same laying-out the model sections use, so a payload reads the same way\n * wherever it appears and a four-level object does not become one long line.\n *\n * ── The sentence is built here, not frozen when the request was raised ──\n * Eighteen doors write a Korean sentence at issue and the issue snapshots it, so it stands in the\n * language of whoever raised it and an approver reading another language never catches up. Where\n * a door declares `valueTKey`, the sentence is made at reading time instead, out of the values\n * the request did freeze.\n *\n * **A door that has not moved yet draws exactly what it drew before.** That is what lets plant\n * move them one at a time instead of eighteen at once, and it covers a door that moved into a\n * locale nobody has written yet: an empty `defaultValue` means a missing key falls through to the\n * frozen sentence rather than printing `approval.…` on the card.\n */\n private slotText(slot: DecisionSlot): unknown {\n const said = this.sentenceOf(slot)\n\n if (said) {\n return said\n }\n\n const scalar = slot.value !== null && slot.value !== undefined && typeof slot.value !== 'object'\n\n if (scalar && slot.unit) {\n return `${slot.value} ${slot.unit}`\n }\n\n return scalar ? String(slot.value) : fieldValue(slot.value)\n }\n\n /** The sentence this slot declares, when it declares one and the locale has it. */\n private sentenceOf(slot: DecisionSlot): string {\n if (!slot.valueTKey) {\n return ''\n }\n\n return String(i18next.t(slot.valueTKey, { ...(slot.params || {}), defaultValue: '' }) || '')\n }\n\n /** Did a person actually write something here. Whitespace is not words. */\n private hasWords(slot: DecisionSlot): boolean {\n return typeof slot.value === 'string' ? slot.value.trim().length > 0 : slot.found && slot.value != null\n }\n\n generatedContent(): DirectiveResult {\n if (!this.activityApproval) {\n return html``\n }\n\n const { output, activityInstance } = this.activityThread || {}\n const { input } = activityInstance || {}\n\n /*\n * The declaration goes first. It answers \"what happens if I approve\", which is the question this\n * screen exists for; the model answers \"what are the values\", which is the fallback.\n */\n const decision = this.decisionContent()\n\n if (decision !== nothing) {\n return decision as DirectiveResult\n }\n\n const model = activityInstance?.activity?.model\n const inputSpec = inputSpecOf(model)\n const outputSpec = outputSpecOf(model)\n\n /*\n * **결재 화면에서 내용이 비어 있는 것은 경고다.**\n *\n * 업무 상세라면 「적을 것이 없다」로 끝난다. 여기서는 다르다 — 무엇을 승인하는지 모르는 채로\n * 승인 · 반려 버튼이 그대로 눌린다. 그 사실을 화면이 말하고, 대신 무엇을 보고 판단할지 알려\n * 준다(타임라인의 상신 사유).\n */\n if (!inputSpec.length && !outputSpec.length) {\n return keyed(\n this.activityApproval.id,\n html`<section empty>\n <span class=\"title\">${i18next.t('text.no-approval-content')}</span>\n <span class=\"why\">${i18next.t('text.no-approval-content-why')}</span>\n </section>`\n )\n }\n\n return keyed(\n this.activityApproval.id,\n html`\n ${\n inputSpec.length\n ? html`\n <section readonly>\n <h2>${i18next.t('label.activity-input')}</h2>\n ${renderGiven(inputSpec, input)}\n </section>\n `\n : ''\n }\n ${\n outputSpec.length\n ? html`\n <section readonly>\n <h2>${i18next.t('field.artifact')}</h2>\n ${renderGiven(outputSpec, output)}\n </section>\n `\n : ''\n }\n `\n )\n }\n\n boardContent(): DirectiveResult {\n if (!this.activityApproval) {\n return html``\n }\n\n const { output, activityInstance } = this.activityThread\n const { viewSource: boardId, input } = activityInstance || {}\n\n if (!this.board || this.board.id !== boardId) {\n this.board = { id: boardId }\n }\n\n return keyed(\n this.activityApproval.id,\n html`\n <ox-board-viewer\n style=\"background-color: white;\"\n .board=${this.board}\n .provider=${provider}\n .values=${{ ...input, ...output }}\n hide-fullscreen\n hide-navigation\n ></ox-board-viewer>\n `\n )\n }\n\n /**\n * The element the activity names, or a plain statement that it could not be loaded.\n *\n * document.createElement answers something for any name, so an activity pointing at an element\n * nobody registered used to produce an empty card. The approver got a screen with nothing on it\n * and the approve button still worked — plant shipped two such forms in one day and found them by\n * reading the file list, not by opening the screen.\n *\n * The reader is told what to judge by instead. Same shape as the empty generated screen: what is\n * missing, and where to look — the submission reason and the history in the timeline below.\n *\n * ── Waiting for a late definition ─────────────────────────────────────────\n * An element can be registered after this page renders, and answering \"not loaded\" forever in\n * that case would be a screen inventing a defect. whenDefined resolves whenever it arrives and\n * the page redraws; a name nobody ever registers simply never resolves, and the statement stands.\n */\n customElementContent() {\n if (!this.activityApproval) {\n return html``\n }\n\n const { output, activityInstance } = this.activityThread\n const { viewSource: tagName, input } = activityInstance || {}\n\n if (!customElements.get(tagName)) {\n customElements.whenDefined(tagName).then(() => this.requestUpdate())\n\n /* The caller draws the notice — unwrapped, because the notice is its own card. */\n return undefined\n }\n\n const element = document.createElement(tagName)\n element.id = 'custom-content'\n element.activityId = activityInstance.activity.id\n element.input = input\n element.output = output\n element.activityApproval = this.activityApproval\n\n return element\n }\n\n /** The activity names a screen and nothing registered it. Says what to judge by instead. */\n private screenNotLoaded() {\n return html`<section empty>\n <span class=\"title\">${i18next.t('text.approval-view-not-loaded')}</span>\n <span class=\"why\">${i18next.t('text.no-approval-content-why')}</span>\n </section>`\n }\n\n @query('ox-board-viewer') oxBoardViewer: any\n\n getOutputFromBoard() {\n const outputSpec = outputSpecOf(this.activityThread?.activityInstance?.activity?.model)\n const data = this.oxBoardViewer.getSceneValues()\n\n return outputSpec.reduce((output, spec) => {\n output[spec.name] = data[spec.name]\n return output\n }, {})\n }\n\n /**\n * 화면이 열리면 **의견 칸에 캐럿을 둔다.**\n *\n * 결재자가 이 화면에서 적는 것은 의견 하나다. 나머지는 전부 읽는 것이고, 판단은 아래 버튼이\n * 받는다. 그러면 열자마자 커서가 있어야 하는 자리는 하나뿐이다.\n *\n * 조건 둘을 지킨다.\n *\n * 활성 탭일 때만 MDI 라 뒤에 있는 탭이 응답을 받으면 앞 탭의 입력을 빼앗는다\n * 한 건에 한 번 갱신마다 focus 를 부르면 사용자가 옮긴 커서를 계속 되돌린다\n *\n * preventScroll 을 준다 — 화면을 아래로 끌어내리면 정작 무엇을 승인하는지가 위로 사라진다.\n */\n private focusedFor?: string\n\n private focusComment() {\n const id = this.activityApproval?.id\n\n if (!id || !this.active || this.activityApproval?.terminatedAt || this.focusedFor === id) {\n return\n }\n\n this.commentInput?.focus({ preventScroll: true })\n this.focusedFor = id\n }\n\n updated(changes) {\n if (changes.has('activityApproval')) {\n this.updateContext()\n this.focusComment()\n }\n }\n\n async pageUpdated(changes, lifecycle, changedBefore) {\n if (this.active && lifecycle.resourceId) {\n // Entity 360 changes only the query address. Do not reload the same approval and erase its unsaved opinion.\n if (this.lastId === lifecycle.resourceId && !('active' in changes)) return\n this.fetchActivityApproval(lifecycle.resourceId)\n }\n }\n\n async fetchActivityApproval(id) {\n const read = ++this.approvalRead\n if (this.lastId !== id) {\n this.activityApproval = undefined\n this.activityThread = undefined\n this.loaded = false\n }\n this.lastId = id\n this.loadError = null\n\n let response: any\n try {\n response = await client.query({\n query: gql`\n query activityApproval($id: String!) { activityApproval(id: $id) ${ActivityApprovalFetchResult}\n }\n `,\n variables: { id }\n })\n } catch (error) {\n if (read !== this.approvalRead) return\n this.loadError = error\n return\n }\n\n if (read !== this.approvalRead) return\n if (response.errors?.length || !response.data?.activityApproval) {\n this.loadError = response.errors?.length\n ? response.errors\n : new Error(i18next.t('error.activity-approval not found', { activityApproval: id }))\n return\n }\n\n this.activityApproval = response.data.activityApproval\n this.loaded = true\n this.activityThread = this.activityApproval.activityThread\n\n this.updateContext()\n }\n\n /*\n * The approval declared that it cannot be undone: ask once, as a destructive confirmation, before\n * sending it. Reversible and undeclared approvals go straight through, as before.\n */\n async confirmIfIrreversible(): Promise<boolean> {\n const { activityInstance } = this.activityThread || {}\n const slots = decisionSlots(activityInstance?.activity?.decisionKeys, activityInstance?.input)\n const gate = approvalGate(slotOfRole(slots, 'reversibility'))\n\n if (!gate.ask) {\n return true\n }\n\n return await OxPrompt.open({\n type: 'warning',\n title: i18next.t('text.approval-is-irreversible'),\n text: gate.caveat,\n /* From the declaration, not a literal: this screen does not decide what is irreversible. */\n confirmButton: { text: i18next.t('button.approve'), destructive: gate.ask },\n cancelButton: { text: i18next.t('button.cancel') }\n })\n }\n\n async rejectActivityApproval() {\n var { id, comment } = this.activityApproval\n\n if (!comment) {\n document.dispatchEvent(new CustomEvent('notify', { detail: { message: i18next.t('text.enter a comment') } }))\n this.commentInput.focus()\n return\n }\n\n const outcome = await ranMutation(() =>\n client.mutate({\n mutation: gql`\n mutation ($id: String!, $comment: String!) { rejectActivityApproval(id: $id, comment: $comment) ${ActivityApprovalFetchResult}\n }\n `,\n variables: { id, comment }\n })\n )\n\n if (outcome.ok) {\n this.activityApproval = outcome.data.rejectActivityApproval\n this.activityThread = this.activityApproval.activityThread\n\n this.updateContext()\n\n document.dispatchEvent(\n new CustomEvent('notify', { detail: { message: i18next.t('text.task_is_rejected_successfully') } })\n )\n }\n }\n\n async approveActivityApproval() {\n var { id, comment } = this.activityApproval\n\n if (!(await this.confirmIfIrreversible())) {\n return\n }\n\n const outcome = await ranMutation(() =>\n client.mutate({\n mutation: gql`\n mutation ($id: String!, $comment: String) { approveActivityApproval(id: $id, comment: $comment) ${ActivityApprovalFetchResult}\n }\n `,\n variables: { id, comment }\n })\n )\n\n if (outcome.ok) {\n this.activityApproval = outcome.data.approveActivityApproval\n this.activityThread = this.activityApproval.activityThread\n\n this.updateContext()\n\n document.dispatchEvent(\n new CustomEvent('notify', { detail: { message: i18next.t('text.task_is_approved_successfully') } })\n )\n }\n }\n\n async saveActivityApproval() {\n var { id, comment } = this.activityApproval\n\n const outcome = await ranMutation(() =>\n client.mutate({\n mutation: gql`\n mutation ($id: String!, $comment: String) { saveActivityApproval(id: $id, comment: $comment) ${ActivityApprovalFetchResult}\n }\n `,\n variables: { id, comment }\n })\n )\n\n if (outcome.ok) {\n this.activityApproval = outcome.data.saveActivityApproval\n this.activityThread = this.activityApproval.activityThread\n\n this.updateContext()\n\n document.dispatchEvent(\n new CustomEvent('notify', { detail: { message: i18next.t('text.task_is_saved_successfully') } })\n )\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"activity-approval-page.js","sourceRoot":"","sources":["../../../client/pages/activity-approval/activity-approval-page.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@operato/list-state/ox-page-state.js'\nimport '@operato/property-editor/ox-properties-dynamic-view.js'\nimport '@operato/board/ox-board-viewer.js'\nimport '../../components/activity-approval-ribon.js'\nimport '../../components/activity-thread-timeline.js'\n\nimport gql from 'graphql-tag'\nimport { TemplateResult, css, html, nothing, unsafeCSS } from 'lit'\nimport { DirectiveResult } from 'lit-html/directive.js'\nimport { unsafeHTML } from 'lit-html/directives/unsafe-html.js'\nimport { customElement, query, state } from 'lit/decorators.js'\nimport { keyed } from 'lit/directives/keyed.js'\nimport { client } from '@operato/graphql'\nimport { OxPrompt } from '@operato/popup'\nimport { i18next, localize } from '@operato/i18n'\nimport { ranMutation } from '../../components/say-outcome.js'\nimport { approvalGate } from './approval-gate.js'\nimport { PageView } from '@operato/shell'\nimport { DocumentPageStyles, ScrollbarStyles } from '@operato/styles'\nimport { provider } from '@operato/board/board-provider.js'\nimport { isReferenceStep } from '@things-factory/organization/dist-client'\n\nimport {\n ActivityFieldsStyles,\n declaredLabel,\n fieldValue,\n inputSpecOf,\n outputSpecOf,\n renderGiven\n} from '../../components/activity-fields.js'\nimport {\n DecisionSlot,\n decisionSlots,\n reversibilityIsMalformed,\n reversibilityOf,\n COMPENSABLE,\n slotOfRole,\n slotsOfRole\n} from '../../components/decision-slots.js'\n\nconst ActivityApprovalFetchResult = `\\\n{ id\n judgment\n comment\n approver { id\n name\n }\n round\n order\n createdAt\n terminatedAt\n activityThread { state\n dueAt\n assignedAt\n assignee { id\n name\n }\n updatedAt\n terminatedAt\n output\n activityApprovals { round\n order\n approver { name\n }\n judgment\n comment\n createdAt\n updatedAt\n terminatedAt\n }\n activityThreadHistories { transaction\n reason\n updater { name\n }\n updatedAt\n state\n output\n }\n activityInstance { id\n name\n description\n state\n activity { id\n label\n labelTKey\n decisionKeys { role\n inputPath\n tKey\n unit\n }\n model { name\n description\n active\n tag\n inout\n type\n unit\n options\n quantifier\n spec\n hidden\n }\n }\n approvalLine { type\n value\n participation\n approver { id\n name\n description\n controlNo\n }\n }\n thumbnail\n input\n output\n viewType\n viewSource\n dueAt\n createdAt\n } \n }\n}`\n\nexport const ActivityThreadStatus = {\n Unassigned: 'unassigned',\n Assigned: 'assigned',\n Started: 'started',\n Delegated: 'delegated',\n Ended: 'ended',\n Aborted: 'aborted'\n}\n\n/** 타임라인이 옆에 서는 폭 — 읽는 단 1080 + 사이 24 + 최소 판 360 + page 좌우 40. */\nconst BESIDE = '(min-width: 1504px)'\n\n@customElement('activity-approval-page')\nexport class ActivityApprovalPage extends localize(i18next)(PageView) {\n static styles = [\n /* The frame owns the page shell and the reading column (ADR-0060 decision 8). */\n DocumentPageStyles,\n ScrollbarStyles,\n ActivityFieldsStyles,\n css`\n :host {\n color: var(--ui-ink, #17242c);\n font-size: var(--ui-t-body, 13px);\n }\n\n /*\n * 넓을 때만 타임라인이 옆에 선다 — **읽는 단은 줄이지 않는다.**\n *\n * 결재자가 판단에 쓰는 것(결정 · 사유 · 의견)이 왼쪽이고, 참고하는 것(타임라인)이 오른쪽이다.\n * 처음 낸 안은 읽는 단 1080 안에서 둘로 쪼개는 것이었는데, 그러면 결정 카드가 1014 에서\n * 520 으로 줄어든다 — 주된 것을 좁혀 참고용 자리를 만드는 꼴이라 물렀다.\n *\n * 그래서 **읽는 단을 넘는 여분이 한 판만큼 있을 때만** 편다:\n *\n * 1080(읽는 단) + 24(사이) + 360(최소 판) + 40(page 좌우 여백) = 1504\n *\n * 그 아래에서는 한 단이고 차례가 그대로다 — 결재선 · 결정 · 타임라인 · 의견. 좁은 화면이\n * 기본이고 넓은 화면이 덤이다.\n */\n @media ${unsafeCSS(BESIDE)} {\n .page {\n max-width: none;\n display: grid;\n grid-template-columns: var(--ui-measure, 1080px) minmax(360px, 1fr);\n column-gap: 24px;\n align-items: start;\n }\n\n /*\n * 두 판의 자리는 문서 차례가 아니라 격자가 정한다. 문서 차례는 좁은 화면 그대로 두어야\n * 하고(결재선 · 결정 · 타임라인 · 의견), 넓은 화면에서만 타임라인이 오른쪽으로 빠진다.\n */\n .main {\n grid-column: 1;\n min-width: 0;\n }\n\n /* 옆에 선 타임라인은 읽는 동안 따라온다 — 결정과 맞대어 보는 값이다. */\n .aside {\n grid-column: 2;\n grid-row: 1 / span 3;\n position: sticky;\n top: 0;\n }\n }\n\n .comment-actions {\n display: flex;\n justify-content: flex-end;\n }\n\n .comment-actions button {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n height: var(--form-action-height, 32px);\n padding: 0 12px;\n border: 1px solid var(--ui-line, var(--md-sys-color-outline-variant));\n border-radius: var(--ui-r-small, 6px);\n background: transparent;\n color: var(--ui-ink, var(--md-sys-color-on-surface));\n font: inherit;\n font-size: var(--ui-t-body, 13px);\n cursor: pointer;\n }\n\n .comment-actions md-icon {\n font-size: 16px;\n --md-icon-size: 16px;\n }\n\n /* 카드가 담아 주므로 안에 들어오는 화면은 자기 면과 여백을 갖지 않는다. */\n /*\n * 줄어들지 않는다 — 줄어들면 본문이 잘린다.\n *\n * flex 항목인데 크기를 안 정해 두면 기본이 flex: 0 1 auto 다. **1 이 줄어든다는 뜻이다** —\n * 세로가 모자라면 브라우저가 이 칸부터 줄이는데, 여기엔 카드 모서리를 따라 자르려고\n * overflow: hidden 이 걸려 있어서 줄어든 만큼이 **스크롤도 없이 사라진다.**\n *\n * 완료 조회에서 실측했다(1400x620): 칸이 242px 로 줄고 내용은 362px 이라 「업무 내용」 절이\n * 대부분 잘렸고, 잘린 자리 바로 아래에서 타임라인이 시작하니 타임라인이 본문을 덮은 것처럼\n * 보였다. 세로가 넉넉하면 줄일 일이 없어 안 드러난다.\n *\n * 안에 오는 것은 문서다. 높이를 정해 주어야 하는 것은 board 뿐이고 board 는 이 칸을 거치지\n * 않는다(activityContent 참조). 문서는 제 높이로 서고, 모자라면 페이지 전체가 스크롤한다 —\n * :host 의 overflow: auto 가 그 자리다.\n */\n section[hosted] {\n flex: none;\n padding: 0;\n overflow: hidden;\n }\n\n ox-board-viewer {\n width: 100%;\n height: 100%;\n }\n\n activity-thread-timeline {\n margin: var(--spacing-large, 16px);\n }\n\n /*\n * 의견 칸 — 이 화면에서 **결재자가 유일하게 적는 자리**다.\n *\n * 화면 아래에 붙어 있어(sticky) 목록을 내려 읽는 동안에도 보인다. 그 자리를 지키되 모양은\n * 다른 절과 같은 카드로 둔다. 전에는 회색 면(surface-variant)에 다른 테두리라 이 화면에서\n * 혼자 다른 부품처럼 보였다.\n */\n /*\n * 의견 — 이 화면에서 **결재자가 유일하게 적는 자리**다.\n *\n * 화면 아래에 붙어 있어(sticky) 내용을 내려 읽는 동안에도 보인다. 그 자리를 지키되 모양은\n * 다른 절과 같은 카드로 둔다. 전에는 회색 면(surface-variant)에 다른 테두리라 이 화면에서\n * 혼자 다른 부품처럼 보였다.\n */\n div[comment] {\n position: sticky;\n bottom: 0;\n padding-bottom: var(--spacing-large, 16px);\n background: var(--ui-bg, #f0f2f4);\n }\n\n /*\n * 이 화면에서 **사용자가 유일하게 손대는 자리**라는 것을 모양으로 말한다.\n *\n * 나머지 절은 전부 읽는 것이고, 판단은 아래 툴바의 단추가 받는다. 그런데 의견 칸이 읽는\n * 절들과 똑같은 카드라 「내 차례가 어디인지」가 화면에 없었다. 왼쪽에 강조색 띠를 둔다 —\n * sticky 라 툴바 바로 위에 붙으므로 띠와 단추가 한 덩어리로 읽힌다.\n */\n div[comment] section {\n border-left: 3px solid var(--ui-accent, #00727e);\n }\n\n div[comment] textarea {\n display: block;\n box-sizing: border-box;\n width: 100%;\n height: 76px;\n padding: 10px 12px;\n background: var(--ui-panel, #ffffff);\n border: 1px solid var(--ui-line, #d5dce0);\n border-radius: var(--ui-r-small, 6px);\n color: var(--ui-ink, #17242c);\n font: inherit;\n font-size: var(--ui-t-body, 13px);\n resize: none;\n outline: none;\n }\n\n div[comment] textarea:focus {\n border-color: var(--ui-accent, #00727e);\n }\n\n div[comment] .hint {\n margin: 7px 0 0;\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-faint, #8a959c);\n word-break: keep-all;\n }\n\n /*\n * The decision, drawn from the activity's declaration.\n *\n * The order is the declaration's and the weight descends with it: what is being asked reads\n * first at title size, then what it is about, then the words a person wrote, then what changes\n * on approval, and last why it came up at all.\n */\n section[decision] .headline {\n font-size: var(--ui-t-headline, 19px);\n font-weight: 600;\n line-height: 1.35;\n color: var(--ui-ink, #17242c);\n word-break: keep-all;\n }\n\n section[decision] .subject {\n margin-top: 3px;\n font-family: var(--ui-mono, ui-monospace, Menlo, monospace);\n font-size: var(--ui-t-body, 13px);\n color: var(--ui-muted, #57676f);\n }\n\n section[decision] .block {\n margin-top: 14px;\n }\n\n section[decision] .block > .label {\n margin-bottom: 4px;\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-muted, #57676f);\n }\n\n /* What a person wrote is set apart, because it is the only part nobody computed. */\n section[decision] .said {\n padding: 9px 11px;\n border-left: 3px solid var(--ui-line, #d5dce0);\n background: var(--ui-line-soft, #f2f5f7);\n color: var(--ui-ink, #17242c);\n word-break: break-word;\n }\n\n section[decision] .none {\n color: var(--ui-faint, #8a959c);\n }\n\n section[decision] .consequence {\n margin-top: 8px;\n padding: 9px 11px;\n border: 1px solid var(--ui-line, #d5dce0);\n border-radius: var(--ui-r-small, 6px);\n }\n\n section[decision] .consequence > .label {\n margin-bottom: 3px;\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-muted, #57676f);\n }\n\n /*\n * Whether approving can be undone reads as the frame around the decision, not as one of its\n * consequences. Among them it carries the same weight as \"four orders get pushed back\", and\n * on an approval that moves equipment it is the heaviest line on the screen.\n */\n section[decision] .reversibility {\n display: flex;\n align-items: center;\n gap: 6px;\n\n margin-top: 12px;\n padding: 8px 11px;\n border-radius: var(--ui-r-small, 6px);\n\n background: var(--ui-line-soft, #f2f5f7);\n color: var(--ui-muted, #57676f);\n font-size: var(--ui-t-small, 11.5px);\n }\n\n section[decision] .reversibility[irreversible] {\n background: var(--md-sys-color-error-container, #ffdad6);\n color: var(--md-sys-color-on-error-container, #410002);\n font-weight: 600;\n }\n\n section[decision] .reversibility md-icon {\n font-size: 1.15em;\n }\n\n /*\n * What the decision was made from. Set flatter than the consequences — it is the record the\n * approver checks, not something the approval brings about.\n */\n section[decision] .evidence {\n margin-top: 8px;\n padding: 7px 0 7px 11px;\n border-left: 2px solid var(--ui-line, #d5dce0);\n }\n\n section[decision] .evidence > .label {\n margin-bottom: 2px;\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-muted, #57676f);\n }\n\n /*\n * What is true right now sits apart from what the approval would change. Sharing the box the\n * consequences use would say the approval causes these readings.\n */\n section[decision] .current-state {\n margin-top: 14px;\n padding: 9px 11px;\n border: 1px dashed var(--ui-line, #d5dce0);\n border-radius: var(--ui-r-small, 6px);\n }\n\n section[decision] .current-state > .label {\n margin-bottom: 3px;\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-muted, #57676f);\n }\n `\n ]\n\n @state() activityApproval: any\n /**\n * 타임라인이 옆에 설 만큼 넓은가.\n *\n * 폭은 CSS 가 정하지만 타임라인을 펴는 것은 속성이라 CSS 로 안 된다. 같은 값을 두 곳에 적지\n * 않도록 물음은 하나이고(`BESIDE`), 그 물음을 CSS 와 여기가 함께 쓴다.\n */\n @state() private beside = false\n\n /**\n * 조회를 마쳤나 — **「아직 안 읽었다」와 「없다」는 다르다.**\n *\n * 전에는 값이 없으면 곧바로 「업무활동 정보가 없습니다」를 그렸다. 그런데 이 화면은 열리자마자\n * 조회를 시작하므로, 응답이 오기 전까지 **없다고 단언하는 화면**이 보인다. 목록에서 줄을 눌러\n * 들어온 사람은 방금 본 건이 없다는 말을 먼저 읽는다.\n *\n * 조회가 끝난 뒤에만 그 말을 한다. 그 전에는 아무 말도 하지 않는다.\n */\n @state() loaded = false\n /** Why the approval could not be read - ox-page-state says it, with retry (ADR-0060 decision 8). */\n @state() loadError: unknown = null\n private lastId?: string\n private approvalRead = 0\n\n @state() activityThread: any\n @state() board: any\n\n @query('textarea') commentInput!: HTMLTextAreaElement\n\n /**\n * Is the reader seated here as a reference — someone who comments and does not decide?\n *\n * The rule comes from organization's client types, the same function its own approval-line editor\n * and list use, so the two wire spellings of the enum are handled in one place. The approval line\n * answers rather than ActivityApproval.participation, matching what the server refuses on\n * (isReferenceOrder in approval-line.ts): the line decides who comes next, so letting the row\n * decide who may judge would leave two authorities with nothing to settle a disagreement.\n *\n * The value comes back false while the query is still out. That is the same answer as for an\n * approver, so the buttons show for a moment on a reference's screen — pressing one is refused by\n * the server, which is why the server closed first.\n */\n get isReference(): boolean {\n const approvalLine = this.activityThread?.activityInstance?.approvalLine\n const order = Number(this.activityApproval?.order ?? 0)\n\n return isReferenceStep(approvalLine?.[order - 1])\n }\n\n get context() {\n const judgment = this.activityApproval?.judgment\n\n return {\n /*\n * 이 화면이 어떤 틀인가 — 동작 줄이 이것을 읽고 자기 오른쪽 끝을 내용에 맞춘다\n * (ADR-0060 결정 9 보탬). 안 적으면 창 폭이고, 그것은 작업면의 모양이다.\n */\n frame: 'document',\n title: this.lifecycle?.params?.['title'] || i18next.t('title.activity approval'),\n help: 'worklist/activity-approval',\n /*\n * 이 화면은 메뉴에 없다 — 「내 결재함」에서 줄을 눌러 들어온다. 그래서 왼쪽 메뉴가 가리킬\n * 항목이 없고, 표시가 사라져 들어온 사람이 자기가 어디에 있는지 잃는다.\n *\n * 어느 목록에서 열리는지는 이 화면이 안다. 그것을 적어 둔다.\n */\n menu: 'approval-pending-list',\n /*\n * A reference is offered neither approve nor reject.\n *\n * ActivityApprovalJudgment has carried Closed for this seat from the start — \"the case ended\"\n * rather than a judgment — so a reference row was never meant to end in approved or rejected.\n * The buttons were offered anyway, and the server accepted them until 2026-09-09.\n *\n * What stays is the note, because a comment is the whole point of the seat, and close. The\n * hint under the textarea says which seat this is; buttons that simply vanish leave the\n * reader looking for them.\n */\n actions: judgment\n ? []\n : this.isReference\n ? []\n : /*\n * ── 결정 단추의 순서와 무게 (ADR-0060 결정 9 보탬) ─────────────\n * 전에는 반려가 첫 자리였고, 승인 · 반려 · 저장이 모두 같은 모양이었다. 되돌리기 어려운\n * 것이 앞에 서고 무엇이 주된 흐름인지 화면이 말하지 않았다.\n *\n * 승인이 주 동작이다 — 채운 단추 하나. 반려는 대등한 선택이지만 예외 경로라 테두리\n * 단추로 그 옆에 둔다.\n *\n * 「저장」은 이름이 뜻을 말하지 않았다. 이 단추가 저장하는 것은 **의견 하나**이고\n * (saveActivityApproval 은 id 와 comment 만 보낸다), 판단은 미뤄 둔다. 그리고 그 결과는\n * 목록에서 「메모 있음」 표식으로 나타난다(activity-cells 의 approvalMarks).\n * 메모 저장은 아래 의견 상자 옆에 두고, 이 줄에는 승인·반려만 둔다.\n */\n [\n {\n title: i18next.t('button.approve'),\n action: this.approveActivityApproval.bind(this),\n icon: 'approval',\n emphasis: { raised: true, outlined: false, dense: false, danger: false }\n },\n\n {\n title: i18next.t('button.reject'),\n action: this.rejectActivityApproval.bind(this),\n icon: 'assignment_return',\n emphasis: { raised: false, outlined: true, dense: false, danger: false }\n },\n\n ]\n // activityApproval: this.activityApproval\n }\n }\n\n private media?: MediaQueryList\n private onBeside = () => (this.beside = !!this.media?.matches)\n\n connectedCallback() {\n super.connectedCallback()\n this.media = window.matchMedia(BESIDE)\n this.onBeside()\n this.media.addEventListener('change', this.onBeside)\n }\n\n disconnectedCallback() {\n this.media?.removeEventListener('change', this.onBeside)\n super.disconnectedCallback()\n }\n\n render() {\n /* Reading and failing both drew nothing at all, so a failure looked like an empty screen. */\n return html`<div class=\"page\">\n <ox-page-state\n .loading=${!this.loaded && !this.loadError}\n .error=${this.loadError}\n @retry=${() => this.lastId && this.fetchActivityApproval(this.lastId)}\n >${\n this.activityThread\n ? this.renderApproval()\n : this.loaded\n ? html`<div empty>\n <span class=\"title\">${i18next.t('text.no activity thread info')}</span>\n <span class=\"why\">${i18next.t('text.no-activity-thread-why')}</span>\n </div>`\n : nothing\n }</ox-page-state\n >\n </div>`\n }\n\n private renderApproval() {\n const { terminatedAt } = this.activityApproval\n const editable = !terminatedAt\n\n return html`\n <div class=\"main\">\n <activity-approval-ribon .activityApproval=${this.activityApproval}></activity-approval-ribon>\n ${this.activityContent()}\n </div>\n <!--\n 보일 항목이 하나도 없으면 타임라인을 편다 — 그때는 화면이 「아래 타임라인의 상신 사유와\n 이력을 보고 판단하세요」로 여기를 가리키므로, 접혀 있으면 그 안내가 거짓말이 된다.\n\n 옆에 설 자리가 있을 때도 편다. 접힌 줄을 눌러 펴는 동작이, 그 줄이 이미 보이는 자리에\n 있으면 하는 일이 없다.\n -->\n <activity-thread-timeline\n class=\"aside\"\n .activityThread=${this.activityThread}\n ?open=${!this.hasFields() || this.beside}\n short-form\n ></activity-thread-timeline>\n ${\n editable\n ? html`\n <div comment class=\"main\">\n <section>\n <h2>${i18next.t('field.wl-comment')}</h2>\n <textarea\n placeholder=${String(\n i18next.t(\n this.isReference\n ? 'text.write-your-comment-as-reference'\n : 'text.explain the reason for approval/rejection'\n )\n )}\n .value=${this.activityApproval?.comment}\n @input=${(e: Event) => {\n this.activityApproval.comment = (e.target as HTMLTextAreaElement).value\n }}\n @change=${(e: Event) => {\n this.activityApproval.comment = (e.target as HTMLTextAreaElement).value\n }}\n ></textarea>\n <!--\n 반려는 의견이 필수다(rejectActivityApproval). 전에는 그 규칙을 **누른 뒤에** 토스트로\n 알렸다. 되돌릴 수 없는 것과 뜻이 다른 동작은 하기 전에 말한다.\n\n A reference reads a different line: this one names the seat, because the approve\n and reject buttons are absent for them and a reader whose buttons are missing\n goes looking for them.\n -->\n <p class=\"hint\">\n ${i18next.t(this.isReference ? 'text.reference-comment-hint' : 'text.approval-comment-hint')}\n </p>\n\n <!--\n 의견을 저장하는 단추는 의견 상자 옆이다(ADR-0060 결정 9 보탬). 동작 줄에는\n 결정만 둔다 — 전에는 승인 · 반려와 같은 줄에 같은 무게로 서서 셋 다 결정처럼\n 읽혔고, 좁은 화면에서는 넘침 메뉴로 밀려 반려가 숨을 수 있었다.\n -->\n <div class=\"comment-actions\">\n <button @click=${this.saveActivityApproval.bind(this)}>\n <md-icon>edit_note</md-icon>${i18next.t('button.save-note')}\n </button>\n </div>\n </section>\n </div>\n `\n : nothing\n }\n `\n }\n\n /** 화면에 낼 항목이 하나라도 있나. 타임라인을 펴 둘지 정하는 자리가 쓴다. */\n hasFields(): boolean {\n const model = this.activityThread?.activityInstance?.activity?.model\n\n return inputSpecOf(model).length > 0 || outputSpecOf(model).length > 0\n }\n\n activityContent() {\n switch (this.activityThread?.activityInstance?.viewType) {\n case 'template':\n return this.hostedContent(this.templateContent())\n case 'generated':\n return this.generatedContent()\n /* board 는 전체 화면을 쓰는 캔버스라 카드에 담지 않는다(hostedContent 머리말). */\n case 'board':\n return this.boardContent()\n case 'custom-element': {\n /*\n * The wrapper is for the element, not for the notice.\n *\n * hostedContent puts the content in a card so every ui type sits at the same place with the\n * same frame. The not-loaded notice is already a card — wrapping it drew a card inside a\n * card, two borders deep, and its margins landed unevenly: 16 above and none below, inside\n * a box that had its own padding. The generated branch returns the same notice unwrapped\n * and looks right.\n */\n const { viewSource: tagName } = this.activityThread?.activityInstance || {}\n\n /*\n * Naming no element is not the same as naming one that is missing.\n *\n * A template that stops providing a screen clears the field — installActivity does not\n * blank what a template leaves undefined, so clearing is written as null. That left\n * viewType saying custom-element with nothing named, and this branch answered \"the\n * screen could not be loaded\": a failure, for an activity that simply has no screen of its\n * own. Those read as different things to whoever is approving, and the second one is not\n * true.\n *\n * With nothing named, the generated screen is what this activity has — and it draws a\n * decision when the activity declares one, which the two plant forms that hit this do.\n */\n if (!tagName) {\n return this.generatedContent()\n }\n\n const element = this.customElementContent()\n\n return element ? this.hostedContent(element) : this.screenNotLoaded()\n }\n case 'page':\n case 'external':\n return html``\n default:\n return this.generatedContent()\n }\n }\n\n /**\n * 화면 종류가 달라도 **같은 자리에 같은 모양으로 놓인다.**\n *\n * worklist 는 네 가지로 내용을 그린다(generated · custom-element · template · board). 그중\n * generated 만 절 카드 안에 있었고 나머지 셋은 면 위에 맨몸으로 놓였다. 그래서 같은 결재 화면이\n * 액티비티에 따라 카드였다가 아니었다가 했다 — 아래의 타임라인 · 의견은 늘 카드라 위계도 어긋났다.\n *\n * board 는 뺀다. 전체 폭 · 전체 높이를 쓰는 캔버스라 카드 안에 넣으면 여백이 두 겹이 되고 크기가\n * 어긋난다.\n *\n * 카드는 여백을 두지 않는다(padding: 0). 안에 들어오는 것이 자기 조판을 갖고 있고, 두 겹으로\n * 주면 안쪽 내용이 카드 가운데로 밀린다.\n */\n hostedContent(content: unknown) {\n return html`<section hosted>${content}</section>`\n }\n\n templateContent() {\n if (!this.activityApproval) {\n return html``\n }\n\n const { viewSource: template } = this.activityThread?.activityInstance\n return unsafeHTML(template)\n }\n\n /**\n * 결재자가 보는 내용 — **전부 읽기 전용이다.**\n *\n * 전에는 「Output」 절을 편집기로 그리고 변경을 activityApproval.output 에 적었다. 그런데\n * approveActivityApproval / rejectActivityApproval 은 id 와 comment 만 보낸다 — 그 값은 어디에도\n * 가지 않는다. 결재자가 적을 수 있는 것은 의견 하나이고, 그것은 화면 아래 칸이 받는다.\n */\n /**\n * The decision the activity declared, or nothing when it declared none.\n *\n * Returning nothing is how generatedContent knows to fall back to the model. An activity with no\n * decisionKeys draws exactly what it drew before.\n *\n * A slot whose path led nowhere is skipped rather than drawn empty. Older instances of an activity\n * predate a field the declaration names and an empty\n * bar on the screen would read as \"there is no reason\" when the truth is that this record has no\n * such value. What cannot be shown is left out; what is shown is only what the payload actually\n * carries.\n *\n * `reason` is the exception. Missing and blank mean the same thing to whoever is reading — nobody\n * wrote one — so both draw the grey line rather than disappearing. A reason that silently vanishes\n * would let an approver assume it was never asked for.\n */\n decisionContent() {\n const { activityInstance } = this.activityThread || {}\n const slots = decisionSlots(activityInstance?.activity?.decisionKeys, activityInstance?.input)\n\n if (!slots.length) {\n return nothing\n }\n\n const headline = slotOfRole(slots, 'headline')\n const subject = slotOfRole(slots, 'subject')\n const reason = slotOfRole(slots, 'reason')\n const consequences = slotsOfRole(slots, 'consequence').filter(slot => slot.found)\n const evidence = slotsOfRole(slots, 'evidence').filter(slot => slot.found)\n const currentState = slotOfRole(slots, 'currentState')\n const reversibility = slotOfRole(slots, 'reversibility')\n\n const drawable =\n [headline, subject, currentState].some(slot => slot?.found) ||\n !!reason ||\n consequences.length ||\n evidence.length ||\n reversibilityOf(reversibility) !== undefined\n\n /*\n * Every declared slot missed this payload. Saying so is not this screen's job — falling back to\n * the model at least shows the values that are there, and the reader is no worse off than before\n * the declaration existed.\n */\n if (!drawable) {\n return nothing\n }\n\n return keyed(\n this.activityApproval.id,\n html`\n <section decision>\n ${headline?.found ? html`<div class=\"headline\">${this.slotText(headline)}</div>` : ''}\n ${subject?.found ? html`<div class=\"subject\">${this.slotText(subject)}</div>` : ''}\n ${\n reason\n ? html`\n <div class=\"block\">\n <div class=\"label\">${i18next.t('field.reason')}</div>\n <div class=\"said ${this.hasWords(reason) ? '' : 'none'}\">\n ${this.hasWords(reason) ? this.slotText(reason) : i18next.t('text.no-reason-given')}\n </div>\n </div>\n `\n : ''\n }\n ${this.renderReversibility(reversibility)}\n ${\n evidence.length\n ? html`\n <div class=\"block\">\n <div class=\"label\">${i18next.t('label.what-it-was-judged-from')}</div>\n ${evidence.map(\n slot => html`\n <div class=\"evidence\">\n ${declaredLabel(slot) ? html`<div class=\"label\">${declaredLabel(slot)}</div>` : ''}\n <div>${this.slotText(slot)}</div>\n </div>\n `\n )}\n </div>\n `\n : ''\n }\n ${\n currentState?.found\n ? html`\n <div class=\"current-state\">\n <div class=\"label\">${declaredLabel(currentState) || i18next.t('label.current-state')}</div>\n <div>${this.slotText(currentState)}</div>\n </div>\n `\n : ''\n }\n ${\n consequences.length\n ? html`\n <div class=\"block\">\n <div class=\"label\">${i18next.t('label.what-changes')}</div>\n ${consequences.map(\n slot => html`\n <div class=\"consequence\">\n ${declaredLabel(slot) ? html`<div class=\"label\">${declaredLabel(slot)}</div>` : ''}\n <div>${this.slotText(slot)}</div>\n </div>\n `\n )}\n </div>\n `\n : ''\n }\n </section>\n `\n )\n }\n\n /**\n * Whether approving can be undone, drawn as the frame around the decision.\n *\n * Only an explicit true or false draws. Anything else — the path led nowhere, or the value is not\n * a boolean — draws nothing, because saying \"this can be undone\" on a screen that does not know is\n * the one mistake this line must never make.\n *\n * This is not rejectionIsFinal. That one says whether **rejecting** ends the case for good; this\n * says whether **approving** can be taken back. The twin actuation carries both, which is exactly\n * why they must not share a sentence.\n */\n private renderReversibility(slot?: DecisionSlot) {\n const said = reversibilityOf(slot)\n\n if (!said) {\n if (reversibilityIsMalformed(slot)) {\n /*\n * Declared, and the value is there, but not the shape this role takes — a bare sentence\n * where { reversible, caveat } belongs. Nothing is drawn, because this side cannot tell\n * which way it goes; the log is so a developer does not have to find it by hand, which is\n * how it was found the first time.\n */\n console.error(`[worklist] reversibility at \"${slot?.inputPath}\" is not { reversible, caveat } — nothing drawn`)\n }\n\n return ''\n }\n\n /*\n * The module's own sentence when it wrote one. 「버린 자재는 돌아오지 않습니다」 says why, which\n * the plain wording cannot; the boolean beside it is what decides how heavily this is drawn.\n */\n return html`\n <div class=\"reversibility\" ?irreversible=${!said.reversible}>\n <md-icon>${said.kind === COMPENSABLE ? 'history' : said.reversible ? 'undo' : 'lock'}</md-icon>\n <span\n >${\n said.caveat || (said.kind === COMPENSABLE\n ? i18next.t('text.approval-is-compensable', { defaultValue: 'The original decision remains. A subsequent corrective action is required.' })\n : i18next.t(said.reversible ? 'text.approval-is-reversible' : 'text.approval-is-irreversible'))\n }</span\n >\n </div>\n `\n }\n\n /**\n * One slot's value as something to read.\n *\n * A scalar prints with its unit when one was declared. Anything shaped — an object, a list — goes\n * through fieldValue, the same laying-out the model sections use, so a payload reads the same way\n * wherever it appears and a four-level object does not become one long line.\n *\n * ── The sentence is built here, not frozen when the request was raised ──\n * Eighteen doors write a Korean sentence at issue and the issue snapshots it, so it stands in the\n * language of whoever raised it and an approver reading another language never catches up. Where\n * a door declares `valueTKey`, the sentence is made at reading time instead, out of the values\n * the request did freeze.\n *\n * **A door that has not moved yet draws exactly what it drew before.** That is what lets plant\n * move them one at a time instead of eighteen at once, and it covers a door that moved into a\n * locale nobody has written yet: an empty `defaultValue` means a missing key falls through to the\n * frozen sentence rather than printing `approval.…` on the card.\n */\n private slotText(slot: DecisionSlot): unknown {\n const said = this.sentenceOf(slot)\n\n if (said) {\n return said\n }\n\n const scalar = slot.value !== null && slot.value !== undefined && typeof slot.value !== 'object'\n\n if (scalar && slot.unit) {\n return `${slot.value} ${slot.unit}`\n }\n\n return scalar ? String(slot.value) : fieldValue(slot.value)\n }\n\n /** The sentence this slot declares, when it declares one and the locale has it. */\n private sentenceOf(slot: DecisionSlot): string {\n if (!slot.valueTKey) {\n return ''\n }\n\n return String(i18next.t(slot.valueTKey, { ...(slot.params || {}), defaultValue: '' }) || '')\n }\n\n /** Did a person actually write something here. Whitespace is not words. */\n private hasWords(slot: DecisionSlot): boolean {\n return typeof slot.value === 'string' ? slot.value.trim().length > 0 : slot.found && slot.value != null\n }\n\n generatedContent(): DirectiveResult {\n if (!this.activityApproval) {\n return html``\n }\n\n const { output, activityInstance } = this.activityThread || {}\n const { input } = activityInstance || {}\n\n /*\n * The declaration goes first. It answers \"what happens if I approve\", which is the question this\n * screen exists for; the model answers \"what are the values\", which is the fallback.\n */\n const decision = this.decisionContent()\n\n if (decision !== nothing) {\n return decision as DirectiveResult\n }\n\n const model = activityInstance?.activity?.model\n const inputSpec = inputSpecOf(model)\n const outputSpec = outputSpecOf(model)\n\n /*\n * **결재 화면에서 내용이 비어 있는 것은 경고다.**\n *\n * 업무 상세라면 「적을 것이 없다」로 끝난다. 여기서는 다르다 — 무엇을 승인하는지 모르는 채로\n * 승인 · 반려 버튼이 그대로 눌린다. 그 사실을 화면이 말하고, 대신 무엇을 보고 판단할지 알려\n * 준다(타임라인의 상신 사유).\n */\n if (!inputSpec.length && !outputSpec.length) {\n return keyed(\n this.activityApproval.id,\n html`<section empty>\n <span class=\"title\">${i18next.t('text.no-approval-content')}</span>\n <span class=\"why\">${i18next.t('text.no-approval-content-why')}</span>\n </section>`\n )\n }\n\n return keyed(\n this.activityApproval.id,\n html`\n ${\n inputSpec.length\n ? html`\n <section readonly>\n <h2>${i18next.t('label.activity-input')}</h2>\n ${renderGiven(inputSpec, input)}\n </section>\n `\n : ''\n }\n ${\n outputSpec.length\n ? html`\n <section readonly>\n <h2>${i18next.t('field.artifact')}</h2>\n ${renderGiven(outputSpec, output)}\n </section>\n `\n : ''\n }\n `\n )\n }\n\n boardContent(): DirectiveResult {\n if (!this.activityApproval) {\n return html``\n }\n\n const { output, activityInstance } = this.activityThread\n const { viewSource: boardId, input } = activityInstance || {}\n\n if (!this.board || this.board.id !== boardId) {\n this.board = { id: boardId }\n }\n\n return keyed(\n this.activityApproval.id,\n html`\n <ox-board-viewer\n style=\"background-color: white;\"\n .board=${this.board}\n .provider=${provider}\n .values=${{ ...input, ...output }}\n hide-fullscreen\n hide-navigation\n ></ox-board-viewer>\n `\n )\n }\n\n /**\n * The element the activity names, or a plain statement that it could not be loaded.\n *\n * document.createElement answers something for any name, so an activity pointing at an element\n * nobody registered used to produce an empty card. The approver got a screen with nothing on it\n * and the approve button still worked — plant shipped two such forms in one day and found them by\n * reading the file list, not by opening the screen.\n *\n * The reader is told what to judge by instead. Same shape as the empty generated screen: what is\n * missing, and where to look — the submission reason and the history in the timeline below.\n *\n * ── Waiting for a late definition ─────────────────────────────────────────\n * An element can be registered after this page renders, and answering \"not loaded\" forever in\n * that case would be a screen inventing a defect. whenDefined resolves whenever it arrives and\n * the page redraws; a name nobody ever registers simply never resolves, and the statement stands.\n */\n customElementContent() {\n if (!this.activityApproval) {\n return html``\n }\n\n const { output, activityInstance } = this.activityThread\n const { viewSource: tagName, input } = activityInstance || {}\n\n if (!customElements.get(tagName)) {\n customElements.whenDefined(tagName).then(() => this.requestUpdate())\n\n /* The caller draws the notice — unwrapped, because the notice is its own card. */\n return undefined\n }\n\n const element = document.createElement(tagName)\n element.id = 'custom-content'\n element.activityId = activityInstance.activity.id\n element.input = input\n element.output = output\n element.activityApproval = this.activityApproval\n\n return element\n }\n\n /** The activity names a screen and nothing registered it. Says what to judge by instead. */\n private screenNotLoaded() {\n return html`<section empty>\n <span class=\"title\">${i18next.t('text.approval-view-not-loaded')}</span>\n <span class=\"why\">${i18next.t('text.no-approval-content-why')}</span>\n </section>`\n }\n\n @query('ox-board-viewer') oxBoardViewer: any\n\n getOutputFromBoard() {\n const outputSpec = outputSpecOf(this.activityThread?.activityInstance?.activity?.model)\n const data = this.oxBoardViewer.getSceneValues()\n\n return outputSpec.reduce((output, spec) => {\n output[spec.name] = data[spec.name]\n return output\n }, {})\n }\n\n /**\n * 화면이 열리면 **의견 칸에 캐럿을 둔다.**\n *\n * 결재자가 이 화면에서 적는 것은 의견 하나다. 나머지는 전부 읽는 것이고, 판단은 아래 버튼이\n * 받는다. 그러면 열자마자 커서가 있어야 하는 자리는 하나뿐이다.\n *\n * 조건 둘을 지킨다.\n *\n * 활성 탭일 때만 MDI 라 뒤에 있는 탭이 응답을 받으면 앞 탭의 입력을 빼앗는다\n * 한 건에 한 번 갱신마다 focus 를 부르면 사용자가 옮긴 커서를 계속 되돌린다\n *\n * preventScroll 을 준다 — 화면을 아래로 끌어내리면 정작 무엇을 승인하는지가 위로 사라진다.\n */\n private focusedFor?: string\n\n private focusComment() {\n const id = this.activityApproval?.id\n\n if (!id || !this.active || this.activityApproval?.terminatedAt || this.focusedFor === id) {\n return\n }\n\n this.commentInput?.focus({ preventScroll: true })\n this.focusedFor = id\n }\n\n updated(changes) {\n if (changes.has('activityApproval')) {\n this.updateContext()\n this.focusComment()\n }\n }\n\n async pageUpdated(changes, lifecycle, changedBefore) {\n if (this.active && lifecycle.resourceId) {\n // Entity 360 changes only the query address. Do not reload the same approval and erase its unsaved opinion.\n if (this.lastId === lifecycle.resourceId && !('active' in changes)) return\n this.fetchActivityApproval(lifecycle.resourceId)\n }\n }\n\n async fetchActivityApproval(id) {\n const read = ++this.approvalRead\n if (this.lastId !== id) {\n this.activityApproval = undefined\n this.activityThread = undefined\n this.loaded = false\n }\n this.lastId = id\n this.loadError = null\n\n let response: any\n try {\n response = await client.query({\n query: gql`\n query activityApproval($id: String!) { activityApproval(id: $id) ${ActivityApprovalFetchResult}\n }\n `,\n variables: { id }\n })\n } catch (error) {\n if (read !== this.approvalRead) return\n this.loadError = error\n return\n }\n\n if (read !== this.approvalRead) return\n if (response.errors?.length || !response.data?.activityApproval) {\n this.loadError = response.errors?.length\n ? response.errors\n : new Error(i18next.t('error.activity-approval not found', { activityApproval: id }))\n return\n }\n\n this.activityApproval = response.data.activityApproval\n this.loaded = true\n this.activityThread = this.activityApproval.activityThread\n\n this.updateContext()\n }\n\n /*\n * The approval declared that it cannot be undone: ask once, as a destructive confirmation, before\n * sending it. Reversible and undeclared approvals go straight through, as before.\n */\n async confirmIfIrreversible(): Promise<boolean> {\n const { activityInstance } = this.activityThread || {}\n const slots = decisionSlots(activityInstance?.activity?.decisionKeys, activityInstance?.input)\n const gate = approvalGate(slotOfRole(slots, 'reversibility'))\n\n if (!gate.ask) {\n return true\n }\n\n return await OxPrompt.open({\n type: 'warning',\n title: i18next.t('text.approval-is-irreversible'),\n text: gate.caveat,\n /* From the declaration, not a literal: this screen does not decide what is irreversible. */\n confirmButton: { text: i18next.t('button.approve'), destructive: gate.ask },\n cancelButton: { text: i18next.t('button.cancel') }\n })\n }\n\n async rejectActivityApproval() {\n var { id, comment } = this.activityApproval\n\n if (!comment) {\n document.dispatchEvent(new CustomEvent('notify', { detail: { message: i18next.t('text.enter a comment') } }))\n this.commentInput.focus()\n return\n }\n\n const outcome = await ranMutation(() =>\n client.mutate({\n mutation: gql`\n mutation ($id: String!, $comment: String!) { rejectActivityApproval(id: $id, comment: $comment) ${ActivityApprovalFetchResult}\n }\n `,\n variables: { id, comment }\n })\n )\n\n if (outcome.ok) {\n this.activityApproval = outcome.data.rejectActivityApproval\n this.activityThread = this.activityApproval.activityThread\n\n this.updateContext()\n\n document.dispatchEvent(\n new CustomEvent('notify', { detail: { message: i18next.t('text.task_is_rejected_successfully') } })\n )\n }\n }\n\n async approveActivityApproval() {\n var { id, comment } = this.activityApproval\n\n if (!(await this.confirmIfIrreversible())) {\n return\n }\n\n const outcome = await ranMutation(() =>\n client.mutate({\n mutation: gql`\n mutation ($id: String!, $comment: String) { approveActivityApproval(id: $id, comment: $comment) ${ActivityApprovalFetchResult}\n }\n `,\n variables: { id, comment }\n })\n )\n\n if (outcome.ok) {\n this.activityApproval = outcome.data.approveActivityApproval\n this.activityThread = this.activityApproval.activityThread\n\n this.updateContext()\n\n document.dispatchEvent(\n new CustomEvent('notify', { detail: { message: i18next.t('text.task_is_approved_successfully') } })\n )\n }\n }\n\n async saveActivityApproval() {\n var { id, comment } = this.activityApproval\n\n const outcome = await ranMutation(() =>\n client.mutate({\n mutation: gql`\n mutation ($id: String!, $comment: String) { saveActivityApproval(id: $id, comment: $comment) ${ActivityApprovalFetchResult}\n }\n `,\n variables: { id, comment }\n })\n )\n\n if (outcome.ok) {\n this.activityApproval = outcome.data.saveActivityApproval\n this.activityThread = this.activityApproval.activityThread\n\n this.updateContext()\n\n document.dispatchEvent(\n new CustomEvent('notify', { detail: { message: i18next.t('text.task_is_saved_successfully') } })\n )\n }\n }\n}\n"]}
|
|
@@ -5,12 +5,20 @@ import type { DecisionSlot } from '../../components/decision-slots.js';
|
|
|
5
5
|
* The screen already draws the lock band from that slot ("once approved, this cannot be undone") and
|
|
6
6
|
* then approved on a single click. Saying it cannot be undone and not asking is the mismatch.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
* reversible: false
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
8
|
+
* The rule (ADR-0064 2-1, ruling 2026-09-25):
|
|
9
|
+
* reversible: false, kind compensable do not ask - the original stays and a new approval corrects
|
|
10
|
+
* it; the band says so, and the approver reads it there
|
|
11
|
+
* reversible: false, kind absent, irreversible, or unknown
|
|
12
|
+
* ask, as a destructive confirmation (cancel gets the first focus)
|
|
13
|
+
* a kind this screen does not know, or one that contradicts the boolean
|
|
14
|
+
* ask - when unsure, lean to the stronger side (decision 1)
|
|
15
|
+
* reversible: true, kind absent or reversible
|
|
16
|
+
* do not ask
|
|
17
|
+
* no declaration, or a value that is not { reversible: boolean }
|
|
18
|
+
* do not ask - and do not claim it is reversible either
|
|
19
|
+
*
|
|
20
|
+
* Read from the raw value, not from reversibilityOf: that one draws nothing for a word it does not know,
|
|
21
|
+
* and here the word it does not know is exactly the case that has to ask.
|
|
14
22
|
*/
|
|
15
23
|
export type ApprovalGate = {
|
|
16
24
|
ask: false;
|
|
@@ -1,9 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { COMPENSABLE, REVERSIBILITY_KINDS, REVERSIBLE } from '../../components/decision-slots.js';
|
|
2
2
|
export function approvalGate(slot) {
|
|
3
|
-
const
|
|
4
|
-
if (
|
|
5
|
-
return
|
|
3
|
+
const value = slot?.found === false ? undefined : slot?.value;
|
|
4
|
+
if (!value || typeof value !== 'object' || typeof value.reversible !== 'boolean') {
|
|
5
|
+
return { ask: false };
|
|
6
6
|
}
|
|
7
|
-
|
|
7
|
+
const kind = value.kind;
|
|
8
|
+
const caveat = typeof value.caveat === 'string' && value.caveat.trim() ? value.caveat : undefined;
|
|
9
|
+
const ask = () => (caveat ? { ask: true, caveat } : { ask: true });
|
|
10
|
+
if (kind === undefined) {
|
|
11
|
+
return value.reversible ? { ask: false } : ask();
|
|
12
|
+
}
|
|
13
|
+
if (!REVERSIBILITY_KINDS.includes(kind)) {
|
|
14
|
+
return ask();
|
|
15
|
+
}
|
|
16
|
+
if (value.reversible) {
|
|
17
|
+
return kind === REVERSIBLE ? { ask: false } : ask();
|
|
18
|
+
}
|
|
19
|
+
return kind === COMPENSABLE ? { ask: false } : ask();
|
|
8
20
|
}
|
|
9
21
|
//# sourceMappingURL=approval-gate.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approval-gate.js","sourceRoot":"","sources":["../../../client/pages/activity-approval/approval-gate.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"approval-gate.js","sourceRoot":"","sources":["../../../client/pages/activity-approval/approval-gate.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAA;AAyBjG,MAAM,UAAU,YAAY,CAAC,IAAmB;IAC9C,MAAM,KAAK,GAAQ,IAAI,EAAE,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAA;IAElE,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACjF,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,CAAA;IACvB,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAA;IACvB,MAAM,MAAM,GAAG,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAA;IACjG,MAAM,GAAG,GAAG,GAAiB,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAA;IAEhF,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAA;IAClD,CAAC;IAED,IAAI,CAAE,mBAAyC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/D,OAAO,GAAG,EAAE,CAAA;IACd,CAAC;IAED,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QACrB,OAAO,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAA;IACrD,CAAC;IAED,OAAO,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAA;AACtD,CAAC","sourcesContent":["import type { DecisionSlot } from '../../components/decision-slots.js'\nimport { COMPENSABLE, REVERSIBILITY_KINDS, REVERSIBLE } from '../../components/decision-slots.js'\n\n/**\n * Whether approving has to be confirmed first - read from the activity's own `reversibility` slot.\n *\n * The screen already draws the lock band from that slot (\"once approved, this cannot be undone\") and\n * then approved on a single click. Saying it cannot be undone and not asking is the mismatch.\n *\n * The rule (ADR-0064 2-1, ruling 2026-09-25):\n * reversible: false, kind compensable do not ask - the original stays and a new approval corrects\n * it; the band says so, and the approver reads it there\n * reversible: false, kind absent, irreversible, or unknown\n * ask, as a destructive confirmation (cancel gets the first focus)\n * a kind this screen does not know, or one that contradicts the boolean\n * ask - when unsure, lean to the stronger side (decision 1)\n * reversible: true, kind absent or reversible\n * do not ask\n * no declaration, or a value that is not { reversible: boolean }\n * do not ask - and do not claim it is reversible either\n *\n * Read from the raw value, not from reversibilityOf: that one draws nothing for a word it does not know,\n * and here the word it does not know is exactly the case that has to ask.\n */\nexport type ApprovalGate = { ask: false } | { ask: true; caveat?: string }\n\nexport function approvalGate(slot?: DecisionSlot): ApprovalGate {\n const value: any = slot?.found === false ? undefined : slot?.value\n\n if (!value || typeof value !== 'object' || typeof value.reversible !== 'boolean') {\n return { ask: false }\n }\n\n const kind = value.kind\n const caveat = typeof value.caveat === 'string' && value.caveat.trim() ? value.caveat : undefined\n const ask = (): ApprovalGate => (caveat ? { ask: true, caveat } : { ask: true })\n\n if (kind === undefined) {\n return value.reversible ? { ask: false } : ask()\n }\n\n if (!(REVERSIBILITY_KINDS as readonly string[]).includes(kind)) {\n return ask()\n }\n\n if (value.reversible) {\n return kind === REVERSIBLE ? { ask: false } : ask()\n }\n\n return kind === COMPENSABLE ? { ask: false } : ask()\n}\n"]}
|
package/dist-client/route.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default function route(page: string): "
|
|
1
|
+
export default function route(page: string): "approval-pending-list" | "done-list-calendar" | "activity-catalog" | "activity-detail" | "work-doors-page" | "todo-list" | "done-list" | "approval-done-list" | "draft-list" | "pickable-list" | "starter-list" | "reporter-list" | "activity-list" | "activity" | "activity-instance-start" | "activity-thread" | "activity-thread-view" | "activity-approval" | "activity-instance-list" | "activity-instance-search" | "activity-template-list" | "activity-store" | "activity-thread-list" | "activity-approval-list" | "installable-activity-list" | "activity-stats-list" | undefined;
|