@things-factory/worklist 10.1.32 → 10.1.34
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 +1 -0
- package/dist-client/components/decision-slots.js +3 -0
- package/dist-client/components/decision-slots.js.map +1 -1
- package/dist-client/components/state-ribon.js +16 -1
- package/dist-client/components/state-ribon.js.map +1 -1
- package/dist-client/pages/activity-approval/activity-approval-page.js +7 -5
- package/dist-client/pages/activity-approval/activity-approval-page.js.map +1 -1
- package/dist-client/pages/activity-thread/activity-thread-page.d.ts +1 -1
- package/dist-client/pages/activity-thread/activity-thread-page.js +4 -7
- package/dist-client/pages/activity-thread/activity-thread-page.js.map +1 -1
- package/dist-client/pages/activity-thread/activity-thread-view-page.d.ts +1 -11
- package/dist-client/pages/activity-thread/activity-thread-view-page.js +7 -10
- package/dist-client/pages/activity-thread/activity-thread-view-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
|
@@ -129,8 +129,11 @@ export function reversibilityOf(slot) {
|
|
|
129
129
|
if (!value || typeof value !== 'object' || typeof value.reversible !== 'boolean') {
|
|
130
130
|
return undefined;
|
|
131
131
|
}
|
|
132
|
+
if (value.kind !== undefined && (!['reversible', 'compensable', 'irreversible'].includes(value.kind) || (value.kind === 'reversible') !== value.reversible))
|
|
133
|
+
return undefined;
|
|
132
134
|
return {
|
|
133
135
|
reversible: value.reversible,
|
|
136
|
+
...(value.kind ? { kind: value.kind } : {}),
|
|
134
137
|
...(typeof value.caveat === 'string' && value.caveat.trim() ? { caveat: value.caveat } : {})
|
|
135
138
|
};
|
|
136
139
|
}
|
|
@@ -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; 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\n return {\n reversible: value.reversible,\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":"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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"]}
|
|
@@ -123,9 +123,24 @@ export const StateRibonStyles = css `
|
|
|
123
123
|
/*
|
|
124
124
|
* 한 줄로 자른다. 이 자리는 머리 띠라 높이가 고정이고, 설명이 길면 아래 내용을 밀어낸다.
|
|
125
125
|
* 전체 글은 정보 아이콘이 여는 overlay 가 갖는다.
|
|
126
|
+
*
|
|
127
|
+
* ── 늘어나지 않는다 (2026-09-20, :4000 실측) ──────────────────────────
|
|
128
|
+
* flex: 1 은 남는 자리를 전부 설명이 가져가게 한다. 그러면 그 뒤의 결재선 · 담당자가 띠의
|
|
129
|
+
* 오른쪽 끝으로 밀린다. 1024px 창의 결재 화면에서 잰 값이다:
|
|
130
|
+
*
|
|
131
|
+
* 설명 「화면 발행 승인」 제 너비 75px → 늘어나서 417px
|
|
132
|
+
* 결재선 왼쪽 끝 x=407 → x=750
|
|
133
|
+
* 본문이 시작하는 자리 x=254
|
|
134
|
+
*
|
|
135
|
+
* 결재선은 이 화면에서 「지금 누구 차례인가」를 말하는 유일한 자리인데, 본문을 훑어 내려가는
|
|
136
|
+
* 눈이 지나는 왼쪽 축에서 496px 떨어져 있었다. 창이 넓어질수록 더 멀어진다 — 설명이 가져가는
|
|
137
|
+
* 자리가 창 폭을 따라 늘기 때문이다.
|
|
138
|
+
*
|
|
139
|
+
* 늘리지 않으면 셋이 왼쪽부터 차례로 선다(상태 · 설명 · 결재선). 긴 설명에서는 여전히 잘린다 —
|
|
140
|
+
* min-width: 0 과 ellipsis 가 이미 있고, 그때는 결재선이 예전 자리로 돌아간다.
|
|
126
141
|
*/
|
|
127
142
|
.desc {
|
|
128
|
-
flex: 1;
|
|
143
|
+
flex: 0 1 auto;
|
|
129
144
|
min-width: 0;
|
|
130
145
|
overflow: hidden;
|
|
131
146
|
text-overflow: ellipsis;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state-ribon.js","sourceRoot":"","sources":["../../client/components/state-ribon.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,CAAA
|
|
1
|
+
{"version":3,"file":"state-ribon.js","sourceRoot":"","sources":["../../client/components/state-ribon.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8KlC,CAAA","sourcesContent":["import { css } from 'lit'\n\n/**\n * 업무 · 결재 화면의 머리 띠 — **세 화면이 한 모양을 쓴다.**\n *\n * activity-thread-ribon · activity-approval-ribon · activity-instance-ribon 이 같은 자리에 같은\n * 것을 보인다(상태 · 설명 · 결재선 또는 담당자). 그런데 셋이 각자 100여 줄의 CSS 를 갖고 있었고,\n * 상태색 여덟 개가 세 벌로 복사되어 있었다.\n *\n * ── 채운 띠를 걷은 이유 — 글자가 안 읽혔다 ─────────────────────────────────\n * 전에는 상태마다 다른 색으로 띠 전체를 채우고 그 위에 흰 글자를 얹었다. 색은 하드코딩된 hex 여덟\n * 개였고, --md-sys-color-on-primary(대개 흰색)를 글자색으로 썼다. contrast 를 계산하면 12px 글자\n * 기준 AA(4.5:1)에 네 개가 못 미친다.\n *\n * started #56af45 2.76 ← 작업자가 가장 오래 보는 상태\n * ended #02acae 2.79\n * rejected #f27429 2.87\n * submitted #428df3 3.31\n * unassigned #5f7184 5.02 (통과)\n * escalated #595de5 5.09 (통과)\n * aborted #cb3a33 5.01 (통과)\n * delegated #8654b0 5.37 (통과)\n *\n * 색을 여덟 개 다시 고르는 대신 **띠를 채우지 않는다.** SurfaceStyles 가 정한 규칙과 같다 —\n * 흰 면에 1px 선을 두르고, 색은 상태 앞의 점 하나가 말한다. 글자는 --ui-ink 라 어느 상태에서든\n * 읽히고, 점은 UI 요소라 3:1 기준으로 판단한다.\n *\n * ── 색 여덟 개를 뜻 여섯 개로 ──────────────────────────────────────────────\n * 하드코딩된 hex 는 앱 팔레트를 따르지 않는다. worklist 는 framework 패키지라 operato-plant ·\n * operato-gangsters · operato-dataset 이 모두 이 화면을 쓰는데, 어느 앱에서도 그 앱 색이 아니었다.\n * 토큰으로 바꾸면 앱마다 자기 라이트 · 다크 테마를 따라온다.\n *\n * unassigned · assigned --ui-muted 아직 시작하지 않았다\n * started --ui-accent 내 손에서 진행 중이다\n * submitted · escalated · delegated --ui-info 남의 판단을 기다린다\n * rejected --ui-warn 되돌아왔다. 다시 해야 한다\n * ended · approved --ui-good 끝났다\n * aborted --ui-crit 중단됐다\n *\n * 색만으로 뜻을 전하지 않는다 — 상태 이름을 항상 함께 적는다.\n */\nexport const StateRibonStyles = css`\n /*\n * ── 면을 갖지 않는다 (2026-09-06) ─────────────────────────────────────\n * 채운 색 띠를 걷어내면서 흰 면(--ui-panel)으로 바꿨는데, 그것이 카드와 **같은 색**이었다.\n * 그래서 한 화면에 흰 블록이 넷이 되고(머리 띠 · 내용 · 타임라인 · 의견) 위계가 사라졌다.\n * 실측: 결재 화면의 네 블록이 전부 rgb(255,255,255) 에 1px 테두리였다.\n *\n * 이 띠가 말하는 것(상태 · 설명 · 결재선)은 전부 보조다. 사용자가 먼저 묻는 것은 「무엇을\n * 승인하나」이지 「지금 몇 단계인가」가 아니다.\n *\n * 그래서 페이지 바탕이 비치게 두고 아래 구분선도 없앤다. 흰 면은 「읽을 내용」에만 남는다 —\n * 그 규칙이 서면 카드가 곧 본문이라는 것이 모양만으로 읽힌다.\n */\n :host {\n display: flex;\n align-items: center;\n gap: var(--spacing-medium, 8px);\n\n /* office control height, the same for mouse and finger (WCAG AA target, user decision 2026-09-17) */\n min-height: 36px;\n padding: 0 var(--spacing-large, 16px);\n box-sizing: border-box;\n\n color: var(--ui-ink, #17242c);\n font-size: var(--ui-t-body, 13px);\n }\n\n /* ── 상태 ────────────────────────────────────────────────────────────── */\n .state {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n flex: none;\n font-weight: 600;\n white-space: nowrap;\n }\n\n /*\n * 상태색을 쓰는 자리는 이 점 하나다. 기본값은 --ui-muted — 서버가 아직 모르는 상태 이름이\n * 오더라도 점이 사라지지 않고 회색으로 남는다(색이 없으면 상태가 없는 것처럼 보인다).\n */\n .dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: var(--worklist-state-color, var(--ui-muted, #57676f));\n }\n\n :host([state='unassigned']),\n :host([state='assigned']),\n :host([state='draft']) {\n --worklist-state-color: var(--ui-muted, #57676f);\n }\n\n :host([state='started']),\n :host([state='issued']),\n :host([state='posted']) {\n --worklist-state-color: var(--ui-accent, #00727e);\n }\n\n :host([state='submitted']),\n :host([state='escalated']),\n :host([state='delegated']),\n :host([state='pending']) {\n --worklist-state-color: var(--ui-info, #2b72ad);\n }\n\n :host([state='rejected']) {\n --worklist-state-color: var(--ui-warn, #9f5e02);\n }\n\n :host([state='ended']),\n :host([state='approved']) {\n --worklist-state-color: var(--ui-good, #297d39);\n }\n\n :host([state='aborted']),\n :host([state='deprecated']) {\n --worklist-state-color: var(--ui-crit, #d14946);\n }\n\n /* ── 설명 ────────────────────────────────────────────────────────────── */\n /*\n * 한 줄로 자른다. 이 자리는 머리 띠라 높이가 고정이고, 설명이 길면 아래 내용을 밀어낸다.\n * 전체 글은 정보 아이콘이 여는 overlay 가 갖는다.\n *\n * ── 늘어나지 않는다 (2026-09-20, :4000 실측) ──────────────────────────\n * flex: 1 은 남는 자리를 전부 설명이 가져가게 한다. 그러면 그 뒤의 결재선 · 담당자가 띠의\n * 오른쪽 끝으로 밀린다. 1024px 창의 결재 화면에서 잰 값이다:\n *\n * 설명 「화면 발행 승인」 제 너비 75px → 늘어나서 417px\n * 결재선 왼쪽 끝 x=407 → x=750\n * 본문이 시작하는 자리 x=254\n *\n * 결재선은 이 화면에서 「지금 누구 차례인가」를 말하는 유일한 자리인데, 본문을 훑어 내려가는\n * 눈이 지나는 왼쪽 축에서 496px 떨어져 있었다. 창이 넓어질수록 더 멀어진다 — 설명이 가져가는\n * 자리가 창 폭을 따라 늘기 때문이다.\n *\n * 늘리지 않으면 셋이 왼쪽부터 차례로 선다(상태 · 설명 · 결재선). 긴 설명에서는 여전히 잘린다 —\n * min-width: 0 과 ellipsis 가 이미 있고, 그때는 결재선이 예전 자리로 돌아간다.\n */\n .desc {\n flex: 0 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--ui-muted, #57676f);\n }\n\n /* ── 정보 아이콘 ─────────────────────────────────────────────────────── */\n /*\n * 상태 바로 뒤에 둔다. 전에는 띠의 오른쪽 끝, 결재선 바로 왼쪽에 있었다 — 그 자리에서는\n * **결재선의 일부처럼** 읽힌다(결재선을 펴 보는 단추인가 하고 누르게 된다).\n *\n * 이 아이콘이 여는 것은 이 건의 기본 정보이고, 그 판의 첫 줄이 상태다. 상태 옆에 붙여\n * 두 개가 한 덩어리로 읽히게 한다 — 앞의 gap 만 좁혀서 붙이고 뒤는 띠의 기본 간격을 쓴다.\n */\n .info {\n flex: none;\n margin-left: calc(var(--spacing-medium, 8px) * -1 + 4px);\n cursor: pointer;\n color: var(--ui-muted, #57676f);\n font-size: 18px;\n }\n\n .info:hover {\n color: var(--ui-ink, #17242c);\n }\n\n /* 키보드로 온 것이 보여야 한다 — 이 아이콘은 role=button 이라 탭 순서에 든다. */\n .info:focus-visible {\n outline: 2px solid var(--ui-accent, #00727e);\n outline-offset: 2px;\n border-radius: 50%;\n color: var(--ui-ink, #17242c);\n }\n\n /* ── 담당자 ──────────────────────────────────────────────────────────── */\n .who {\n display: inline-flex;\n align-items: baseline;\n gap: 5px;\n flex: none;\n white-space: nowrap;\n }\n\n .who-label {\n font-size: var(--ui-t-small, 11.5px);\n color: var(--ui-muted, #57676f);\n }\n\n /* ── 결재선 ──────────────────────────────────────────────────────────── */\n approval-line-brief {\n flex: none;\n font-size: var(--ui-t-small, 11.5px);\n }\n\n /*\n * 좁은 화면 — 결재선과 담당자를 감춘다. 상태와 설명이 먼저다.\n *\n * 결재선은 단계마다 44px 이상을 쓴다. 세 단계면 이음선까지 200px 을 넘고, 460px 화면에서는\n * 설명이 들어갈 자리가 남지 않는다.\n */\n @media only screen and (max-width: 460px) {\n :host {\n padding: 0 var(--spacing-medium, 8px);\n }\n\n .who,\n approval-line-brief {\n display: none;\n }\n }\n`\n"]}
|
|
@@ -476,7 +476,7 @@ let ActivityApprovalPage = class ActivityApprovalPage extends localize(i18next)(
|
|
|
476
476
|
: this.isReference
|
|
477
477
|
? []
|
|
478
478
|
: /*
|
|
479
|
-
* ──
|
|
479
|
+
* ── 결정 단추의 순서와 무게 (ADR-0060 결정 9 보탬) ─────────────
|
|
480
480
|
* 전에는 반려가 첫 자리였고, 승인 · 반려 · 저장이 모두 같은 모양이었다. 되돌리기 어려운
|
|
481
481
|
* 것이 앞에 서고 무엇이 주된 흐름인지 화면이 말하지 않았다.
|
|
482
482
|
*
|
|
@@ -485,8 +485,8 @@ let ActivityApprovalPage = class ActivityApprovalPage extends localize(i18next)(
|
|
|
485
485
|
*
|
|
486
486
|
* 「저장」은 이름이 뜻을 말하지 않았다. 이 단추가 저장하는 것은 **의견 하나**이고
|
|
487
487
|
* (saveActivityApproval 은 id 와 comment 만 보낸다), 판단은 미뤄 둔다. 그리고 그 결과는
|
|
488
|
-
* 목록에서 「메모 있음」 표식으로 나타난다(activity-cells 의 approvalMarks).
|
|
489
|
-
*
|
|
488
|
+
* 목록에서 「메모 있음」 표식으로 나타난다(activity-cells 의 approvalMarks).
|
|
489
|
+
* 메모 저장은 아래 의견 상자 옆에 두고, 이 줄에는 승인·반려만 둔다.
|
|
490
490
|
*/
|
|
491
491
|
[
|
|
492
492
|
{
|
|
@@ -802,9 +802,11 @@ let ActivityApprovalPage = class ActivityApprovalPage extends localize(i18next)(
|
|
|
802
802
|
*/
|
|
803
803
|
return html `
|
|
804
804
|
<div class="reversibility" ?irreversible=${!said.reversible}>
|
|
805
|
-
<md-icon>${said.reversible ? 'undo' : 'lock'}</md-icon>
|
|
805
|
+
<md-icon>${said.kind === 'compensable' ? 'history' : said.reversible ? 'undo' : 'lock'}</md-icon>
|
|
806
806
|
<span
|
|
807
|
-
>${said.caveat ||
|
|
807
|
+
>${said.caveat || (said.kind === 'compensable'
|
|
808
|
+
? i18next.t('text.approval-is-compensable', { defaultValue: 'The original decision remains. A subsequent corrective action is required.' })
|
|
809
|
+
: i18next.t(said.reversible ? 'text.approval-is-reversible' : 'text.approval-is-irreversible'))}</span
|
|
808
810
|
>
|
|
809
811
|
</div>
|
|
810
812
|
`;
|
|
@@ -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\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 * ── 세 단추의 순서와 무게 (2026-09-06) ──────────────────────────\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 @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.reversible ? 'undo' : 'lock'}</md-icon>\n <span\n >${\n said.caveat || 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 this.fetchActivityApproval(lifecycle.resourceId)\n }\n }\n\n async fetchActivityApproval(id) {\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 this.loadError = error\n return\n }\n\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 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\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 @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 this.fetchActivityApproval(lifecycle.resourceId)\n }\n }\n\n async fetchActivityApproval(id) {\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 this.loadError = error\n return\n }\n\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"]}
|
|
@@ -202,14 +202,11 @@ let ActivityThreadPage = class ActivityThreadPage extends localize(i18next)(Page
|
|
|
202
202
|
title: i18next.t('button.restart'),
|
|
203
203
|
action: this._restartActivityThread.bind(this),
|
|
204
204
|
...CommonButtonStyles.save
|
|
205
|
-
},
|
|
206
|
-
{
|
|
207
|
-
title: i18next.t('button.close'),
|
|
208
|
-
action: () => {
|
|
209
|
-
history.back();
|
|
210
|
-
},
|
|
211
|
-
...CommonButtonStyles.back
|
|
212
205
|
}
|
|
206
|
+
/*
|
|
207
|
+
* 나가기는 동작 줄에 두지 않는다 (ADR-0060 결정 9 보탬 판정 2). 탭의 × 가 이미 하는 일이고,
|
|
208
|
+
* 같은 곳으로 가는 문을 둘 두면 어느 쪽이 무엇을 하는지 사람이 눌러 봐야 안다.
|
|
209
|
+
*/
|
|
213
210
|
].filter(Boolean /* truthy only */)
|
|
214
211
|
// activityThread: this.activityThread
|
|
215
212
|
};
|