@things-factory/auth-ui 10.1.220 → 10.1.223
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/bootstrap.js +1 -1
- package/dist-client/bootstrap.js.map +1 -1
- package/dist-client/pages/role/role-privilege-page.d.ts +7 -5
- package/dist-client/pages/role/role-privilege-page.js +45 -22
- package/dist-client/pages/role/role-privilege-page.js.map +1 -1
- package/dist-client/pages/role/role-task-review.test.js +19 -5
- package/dist-client/pages/role/role-task-review.test.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/translations/en.json +3 -2
- package/translations/es.json +3 -2
- package/translations/ja.json +3 -2
- package/translations/ko.json +3 -2
- package/translations/ms.json +3 -2
- package/translations/zh.json +3 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role-privilege-page.js","sourceRoot":"","sources":["../../../client/pages/role/role-privilege-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/input/ox-input-search.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, nothing } from 'lit'\nimport type { CSSResultGroup } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { client, gqlContext } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\n\nimport { KNOWN_AXES, axisLabel } from '../../utils/privilege-axis.js'\nimport { openPopup } from '@operato/layout'\nimport { OxPrompt } from '@operato/popup/ox-prompt.js'\nimport { PageView } from '@operato/shell'\nimport { CommonHeaderStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles'\nimport { getDomain } from '@things-factory/auth-base/dist-client'\n\nimport { filterPrivilegeModules, selectedVisibleModule } from '../../components/role-privilege-picker-state.js'\nimport { pairOf, pairsToAdd, pairsToRemove } from '../../components/role-task-edit.js'\n\n/*\n * The axis vocabulary moved to `utils/privilege-axis.ts`, and it no longer translates.\n * `query` used to print as 「읽기」 and `mutation` as 「쓰기」, which is false in the six\n * declarations where the axis and the GraphQL operation disagree on purpose. That file\n * carries the reasoning and the decided end state.\n */\n\ntype Cell = { id: string; axis: string; deprecated: boolean; description?: string }\ntype Module = { category: string; cells: Cell[] }\ntype Grant = { category: string; axes: string[] }\ntype TaskCoverage = { taskId: string; module: string; status: 'all' | 'some' | 'none' | 'broken'; held: Grant[]; missing: Grant[]; undeclared: Grant[] }\ntype Coverage = { tasks: TaskCoverage[]; unclassified: Grant[] }\ntype TemplateChoice = { id: string; name: string; definitionErrors: string[] }\ntype Gap = { templateId: string; templateName: string; missing: Grant[]; missingCount: number; extra: Grant[]; extraCount: number }\n\n/** What the body of the page shows: the privileges one by one, the role by task, or the role against a template. */\ntype View = 'privileges' | 'tasks' | 'template'\n\nconst GRANT_FIELDS = 'category axes'\n\n/**\n * 한 역할이 무엇을 할 수 있나 — **켜진 것이 문장으로 본문에 선다.**\n *\n * ── 사용자 평가가 이 화면을 두 번 뒤집었다 ──────────────────────────────────\n * **「화면으로부터 배울 수 있는 것이 없고, 혼란스럽고, 어렵고, 불편하다.」**\n *\n * 처음에는 팝업 안의 `모듈 × 읽기·쓰기` 격자였다. 격자는 「이 모듈에서 무엇이 켜졌나」에\n * 답하는데, 관리자가 묻는 것은 **「이 역할이 무엇을 하나」**다. 격자로 그걸 읽으려면 모듈\n * 46개를 한 번씩 열어야 하고, 역할을 부여하는 사람은 부여하기 **전에** 그 답이 필요하다.\n *\n * 그래서 뒤집었다(UX 레인 §2). 켜진 것을 문장으로 늘어놓는 것이 본문이고, 격자는 **더할 때\n * 쓰는 도구**로 자리를 옮겼다(`renderAdd`). 버린 것은 없다.\n *\n * ── 문장은 어디서 오나 ──────────────────────────────────────────────────────\n * ```\n * 1 Privilege.description 서버가 채우면 그것이 이긴다 — 배포마다 다를 수 있는 사실은 서버가 안다\n * 2 privilege.allows.<category>.<axis> UX 레인이 쓴 글 (57 모듈 · 104 문장, ko·en)\n * 3 「아직 설명이 없습니다」 둘 다 없으면. 지어내지 않는다\n * ```\n *\n * 1번은 아직 **전부 비어 있다** — 부팅 동기화가 `{ category, name }` 둘만 저장한다\n * (`auth-base/server/index.ts:34-36`). 그래서 지금 화면에 나오는 문장은 전부 2번이다.\n *\n * ── 저장 범위는 서버가 준 목록이다 ──────────────────────────────────────────\n * 옛 편집기는 `querySelectorAll('input:checked')` 로 보냈다. 「더하기」 판에서 다른 모듈로\n * 옮기면 앞 모듈의 체크박스는 **DOM 에 없다** — 그 방식이면 모듈을 옮겨 다니는 것만으로\n * 권한이 조용히 떨어진다. 여기서는 `rolePrivileges` 가 준 전체가 범위이고 켠 것은\n * `assigned` 에만 있다. 화면이 무엇을 그렸는지와 무관하다.\n */\n@customElement('role-privilege-page')\nexport class RolePrivilegePage extends localize(i18next)(PageView) {\n static styles: CSSResultGroup = [\n /* The frame owns the page shell; the two panes are the content it sizes (ADR-0060 decision 8). */\n WorkSurfacePageStyles,\n ScrollbarStyles,\n CommonHeaderStyles,\n css`\n :host {\n color: var(--md-sys-color-on-surface);\n }\n\n [body] {\n display: grid;\n /* 왼쪽은 이름만 담으므로 고정, 오른쪽이 문장을 받으므로 남는 폭을 준다. */\n grid-template-columns: minmax(200px, 280px) 1fr;\n gap: var(--spacing-large, 12px);\n padding: var(--spacing-large, 12px);\n }\n\n @media screen and (max-width: 780px) {\n [body] {\n /* 좁으면 위아래로. 고른 모듈이 아래에 오는 것이 읽는 순서와 같다. */\n grid-template-columns: 1fr;\n grid-template-rows: auto 1fr;\n }\n }\n\n [panel] {\n display: flex;\n flex-direction: column;\n min-height: 0;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: var(--md-sys-shape-corner-medium, 10px);\n background-color: var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));\n overflow: hidden;\n }\n\n [panel] > header {\n display: flex;\n align-items: baseline;\n gap: 8px;\n padding: 10px 12px;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n color: var(--md-sys-color-on-surface-variant);\n font-size: 0.8rem;\n }\n\n [panel] > header [count] {\n color: var(--md-sys-color-outline);\n }\n\n [scroll] {\n flex: 1;\n min-height: 0;\n overflow: auto;\n }\n\n /*\n 걸러 보는 칸은 ox-input-search 다.\n\n 여기 md-filled-text-field 를 썼다가 사용자에게 「전체적인 스타일에 어울리지 않는다」를\n 들었다. 그것은 회색 채움 상자에 라벨이 떠 있는 **폼 칸**이고, 이 자리는 **찾는 칸**이다.\n\n 근거는 이 화면 자신에 있다 — 역할 목록의 검색 칸이 이 부품이다.\n [읽었음] @operato/data-grist/dist/src/filters/filters-form.js:153\n\n ⚠ 폼 칸도 md-* 를 안 쓴다 — 사용자가 2026-09-11 에 역할 화면 전체로 못 박았다\n (「너무 크고 스타일이 안 어울린다」). native input 에 FormFieldStyles 를 얹는다.\n 그 블록이 정확히 이 일을 하려고 있는 것이다(shadow 경계를 넘는 토큰 연결).\n */\n ox-input-search {\n margin: 8px 12px;\n width: calc(100% - 24px);\n }\n\n /* ── 왼쪽: 모듈 ──────────────────────────────────────────────────────── */\n ul[modules] {\n list-style: none;\n margin: 0;\n padding: 0;\n }\n\n ul[modules] li {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 8px;\n padding: 8px 12px;\n cursor: pointer;\n border-left: 3px solid transparent;\n }\n\n ul[modules] li:hover {\n background-color: var(--md-sys-color-surface-variant);\n }\n\n /* 고른 것은 면과 왼쪽 선으로 말한다 — 글자색만 바꾸면 야간에 안 보인다. */\n ul[modules] li[selected] {\n border-left-color: var(--md-sys-color-primary);\n background-color: var(--md-sys-color-secondary-container);\n color: var(--md-sys-color-on-secondary-container);\n }\n\n ul[modules] li [on] {\n font-size: 0.78rem;\n color: var(--md-sys-color-primary);\n }\n\n ul[modules] li[selected] [on] {\n color: var(--md-sys-color-on-secondary-container);\n }\n\n ul[modules] li [none] {\n font-size: 0.78rem;\n color: var(--md-sys-color-outline);\n }\n\n /* ── 오른쪽: 그 모듈에서 되는 일 ────────────────────────────────────── */\n ul[axes] {\n list-style: none;\n margin: 0;\n padding: 0;\n }\n\n ul[axes] li {\n display: grid;\n grid-template-columns: auto 1fr;\n gap: 4px 10px;\n padding: 12px 14px;\n border-bottom: 1px solid var(--md-sys-color-surface-variant);\n cursor: pointer;\n }\n\n ul[axes] li:last-child {\n border-bottom: none;\n }\n\n ul[axes] label {\n font-size: 0.95rem;\n font-weight: 600;\n cursor: pointer;\n }\n\n ul[axes] li[ghost] label {\n cursor: default;\n }\n\n ul[axes] li[ghost],\n ul[axes] li[readonly] {\n cursor: default;\n }\n\n [picker-footer] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--spacing-medium, 8px);\n padding: var(--spacing-medium, 8px) var(--spacing-large, 12px);\n border-top: 1px solid var(--md-sys-color-outline-variant);\n color: var(--md-sys-color-on-surface-variant);\n font-size: 0.85rem;\n }\n\n [picker-footer] button {\n flex: 0 0 auto;\n padding: 8px 16px;\n border: 0;\n border-radius: var(--md-sys-shape-corner-small, 6px);\n color: var(--md-sys-color-on-primary);\n background-color: var(--md-sys-color-primary);\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n }\n\n [picker-footer] button:disabled {\n cursor: default;\n opacity: 0.45;\n }\n\n /* 설명은 **본문**이다 — tooltip 이 아니다. 체크박스 아래 칸을 비워 두고 거기 놓는다. */\n ul[axes] [says] {\n grid-column: 2;\n color: var(--md-sys-color-on-surface-variant);\n font-size: 0.85rem;\n line-height: 1.55;\n }\n\n ul[axes] [says][missing] {\n color: var(--md-sys-color-outline);\n font-style: italic;\n }\n\n ul[axes] li[ghost] label {\n text-decoration: line-through;\n color: var(--md-sys-color-outline);\n }\n\n /*\n Which role is this? The header alone was not enough - it is one line of chrome above a\n page full of module names, and a role carries a description that says what the job is.\n Name and description sit in the body, where the reader is already looking.\n */\n [subject] {\n margin: var(--spacing-large, 12px) var(--spacing-large, 12px) 0;\n }\n\n [subject] [name] {\n font-size: 1.15rem;\n font-weight: 640;\n }\n\n [subject] [says] {\n margin-top: 2px;\n font-size: 0.88rem;\n line-height: 1.55;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n [subject] [says][missing] {\n color: var(--md-sys-color-outline);\n font-style: italic;\n }\n\n /*\n What is on, all together. The two panels answer \"what can this role do in one module\";\n they cannot answer \"what does this role do\", because that reading needs 46 clicks.\n */\n [granted] {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n margin: var(--spacing-medium, 8px) var(--spacing-large, 12px) 0;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: var(--md-sys-shape-corner-medium, 10px);\n background-color: var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));\n }\n\n [granted] > header {\n display: flex;\n align-items: baseline;\n gap: 8px;\n padding: 8px 12px;\n color: var(--md-sys-color-on-surface-variant);\n font-size: 0.8rem;\n }\n\n [granted] > header [count] {\n color: var(--md-sys-color-outline);\n }\n\n /*\n ⚠ 여기 display: flex 와 flex-wrap: wrap 이 있었다 — 칩으로 그리던 시절의 규칙이다.\n 마크업을 줄 목록으로 바꾼 뒤에도 남아서 **문장이 한 줄씩 서지 않고 칩처럼 옆으로\n 흘렀다.** 내 ul[lines] 는 display 를 선언하지 않으므로 맞설 것이 없었다.\n 마크업을 갈 때는 그 마크업만 쓰던 CSS 를 함께 지운다.\n\n 높이도 바뀐다. max-height: 22vh 는 격자 두 판이 늘 아래에 펼쳐져 있던 시절의 값이다.\n 지금은 **이 목록이 본문**이므로(§2) 남는 높이를 여기가 받고, 59줄은 여기서 흐른다.\n */\n [granted] ul {\n flex: 1;\n min-height: 0;\n overflow: auto;\n }\n\n [granted] [empty] {\n padding: 0 12px 10px;\n }\n\n [note] {\n margin: var(--spacing-large, 12px);\n padding: 10px 12px;\n border-radius: var(--md-sys-shape-corner-small, 6px);\n font-size: 0.85rem;\n line-height: 1.55;\n background-color: var(--md-sys-color-surface-variant);\n color: var(--md-sys-color-on-surface-variant);\n }\n\n [note][attention] {\n background-color: var(--md-sys-color-error-container);\n color: var(--md-sys-color-on-error-container);\n }\n\n /*\n ── 할 수 있는 일 (본문) ────────────────────────────────────────────────\n 읽는 것이 먼저이므로 글자를 본문 크기로 둔다. 격자의 0.85rem 보다 크다.\n */\n ul[lines] {\n list-style: none;\n margin: 0;\n padding: 0 12px;\n }\n\n ul[lines] li {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 9px 2px;\n border-bottom: 1px solid var(--md-sys-color-surface-variant);\n }\n\n ul[lines] li:last-child {\n border-bottom: none;\n }\n\n ul[lines] [line] {\n flex: 1;\n font-size: 0.95rem;\n line-height: 1.5;\n }\n\n /* 문장이 없어서 이름으로 지은 줄 — 문장과 구별되게 조용히 둔다. */\n ul[lines] [named] {\n color: var(--md-sys-color-on-surface-variant);\n }\n\n ul[lines] li[ghost] [line] {\n text-decoration: line-through;\n color: var(--md-sys-color-outline);\n }\n\n ul[lines] [tag] {\n margin-left: 8px;\n font-size: 0.78rem;\n text-decoration: none;\n color: var(--md-sys-color-outline);\n }\n\n /* 떼기 — 줄마다 있으므로 조용하고, 그 줄에 머물 때 드러난다. */\n button[drop] {\n flex: none;\n display: inline-flex;\n padding: 3px;\n color: var(--md-sys-color-outline);\n background-color: transparent;\n border: none;\n border-radius: var(--md-sys-shape-corner-full, 999px);\n cursor: pointer;\n }\n button[drop] md-icon {\n --md-icon-size: 18px;\n }\n ul[lines] li:hover button[drop] {\n color: var(--md-sys-color-error);\n background-color: var(--md-sys-color-error-container);\n }\n\n button[add] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n margin: 10px 12px 12px;\n padding: 8px 14px 8px 10px;\n font: var(--label-font, inherit);\n font-size: 0.85rem;\n color: var(--md-sys-color-on-secondary-container);\n background-color: var(--md-sys-color-secondary-container);\n border: none;\n border-radius: var(--md-sys-shape-corner-full, 999px);\n cursor: pointer;\n }\n button[add] md-icon {\n --md-icon-size: 18px;\n }\n\n button[close] {\n margin-left: auto;\n display: inline-flex;\n padding: 2px;\n color: var(--md-sys-color-on-surface-variant);\n background-color: transparent;\n border: none;\n cursor: pointer;\n }\n button[close] md-icon {\n --md-icon-size: 18px;\n }\n\n [empty] {\n padding: var(--spacing-large, 12px);\n color: var(--md-sys-color-on-surface-variant);\n font-size: 0.85rem;\n }\n\n /*\n The three readings of one role - quiet text buttons in the panel's header line, the chosen one underlined.\n They change what the panel shows, not what the role holds, so they are not boxed like an action.\n */\n [granted] > header nav {\n margin-left: auto;\n display: flex;\n gap: 2px;\n }\n\n [granted] > header nav button {\n padding: 2px 8px;\n border: none;\n border-bottom: 2px solid transparent;\n background: none;\n font: inherit;\n font-size: 0.8rem;\n color: var(--md-sys-color-on-surface-variant);\n cursor: pointer;\n }\n\n [granted] > header nav button[selected] {\n border-bottom-color: var(--md-sys-color-primary);\n color: var(--md-sys-color-on-surface);\n font-weight: 600;\n }\n\n [review] {\n flex: 1;\n min-height: 0;\n overflow: auto;\n padding: 0 12px 12px;\n }\n\n [review] h4 {\n display: flex;\n align-items: baseline;\n gap: 8px;\n margin: 14px 0 4px;\n font-size: 0.85rem;\n font-weight: 600;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n [review] h4 [count] {\n font-weight: 400;\n color: var(--md-sys-color-outline);\n }\n\n ul[tasks] {\n list-style: none;\n margin: 0;\n padding: 0;\n }\n\n ul[tasks] > li {\n padding: 9px 2px;\n border-bottom: 1px solid var(--md-sys-color-surface-variant);\n }\n\n ul[tasks] > li:last-child {\n border-bottom: none;\n }\n\n ul[tasks] [task-name] {\n font-size: 0.95rem;\n font-weight: 600;\n }\n\n ul[tasks] [task-says],\n ul[tasks] [task-missing] {\n margin-top: 2px;\n font-size: 0.85rem;\n line-height: 1.55;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n ul[tasks] [task-missing] {\n color: var(--md-sys-color-outline);\n }\n\n ul[tasks][quiet] > li {\n padding: 5px 2px;\n font-size: 0.88rem;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n [state] {\n margin-left: 8px;\n padding: 0 6px;\n border-radius: var(--md-sys-shape-corner-full, 999px);\n font-size: 0.75rem;\n font-weight: 400;\n background-color: var(--md-sys-color-surface-container-highest);\n color: var(--md-sys-color-on-surface-variant);\n }\n\n [state][all] {\n background-color: var(--md-sys-color-secondary-container);\n color: var(--md-sys-color-on-secondary-container);\n }\n\n [state][broken] {\n background-color: var(--md-sys-color-error-container);\n color: var(--md-sys-color-on-error-container);\n }\n\n ul[sentences] {\n list-style: none;\n margin: 0;\n padding: 0;\n }\n\n ul[sentences] li {\n padding: 5px 2px;\n font-size: 0.9rem;\n line-height: 1.5;\n }\n\n button[quiet] {\n padding: 4px 0;\n border: none;\n background: none;\n font: inherit;\n font-size: 0.85rem;\n color: var(--md-sys-color-primary);\n cursor: pointer;\n }\n\n button[quiet]:disabled {\n color: var(--md-sys-color-outline);\n cursor: default;\n }\n\n [review] select {\n margin-top: 10px;\n padding: 4px 6px;\n font: inherit;\n font-size: 0.88rem;\n color: var(--md-sys-color-on-surface);\n background-color: var(--md-sys-color-surface);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: var(--md-sys-shape-corner-small, 6px);\n }\n\n [review] [stale] {\n margin-top: 10px;\n font-size: 0.82rem;\n color: var(--md-sys-color-outline);\n }\n `\n ]\n\n /** 셸이 `role-privileges/<id>` 의 뒤를 `lifecycle.resourceId` 로 준다. */\n @property({ type: Object }) lifecycle: any\n\n /*\n * 이름을 `role` 로 두면 안 된다 — `HTMLElement` 가 ARIA 반영으로 `role` 을 이미 갖고 있어서\n * private 필드로 가리면 `CustomElementClass` 에 안 맞는다(TS1238·TS2415).\n */\n @state() private subject: any = {}\n @state() private modules: Module[] = []\n @state() private assigned: Set<string> = new Set()\n @state() private holders: any[] = []\n @state() private selected: string = ''\n\n @state() private keyword: string = ''\n @state() private mySubdomain: string = ''\n\n @state() private fetchState: 'loading' | 'failed' | 'loaded' = 'loading'\n /** What the server said when the read failed - the screen shows that sentence, not a guess. */\n @state() private loadError: unknown = null\n @state() private saving: boolean = false\n @state() private dirty: boolean = false\n\n @state() private view: View = 'privileges'\n /** The role laid against every task - computed by the server from the saved privileges (`Role.taskCoverage`). */\n @state() private coverage: Coverage = { tasks: [], unclassified: [] }\n @state() private showUnheldTasks: boolean = false\n /** null until asked for; [] when this person may not read templates (`roleTemplates` asks user:mutation). */\n @state() private templates: TemplateChoice[] | null = null\n @state() private templateId: string = ''\n @state() private gap: Gap | null = null\n\n /**\n * 부모 도메인의 역할은 여기서 못 고친다 — `updateRole` 의 범위가 `domain.id` 하나다.\n * 목록이 상속으로 보여 주므로 관리자가 여기까지 온다. 읽기 전용으로 두고 어디로 가야 하는지\n * 말한다. (프로필의 `domain` 에 `id` 가 없어서 `subdomain` 으로 견준다.)\n */\n private get inherited(): boolean {\n const subdomain = this.subject?.domain?.subdomain\n return Boolean(subdomain && this.mySubdomain && subdomain !== this.mySubdomain)\n }\n\n private get savable(): boolean {\n return this.fetchState === 'loaded' && !this.saving && !this.inherited\n }\n\n private get visibleModules(): Module[] {\n return filterPrivilegeModules(this.modules, this.keyword, moduleLabel)\n }\n\n /**\n * `role-privilege-picker` 는 이 페이지의 상태를 읽어 그린다. 둘은 서로 다른 shadow root 이므로\n * 여기 state 만 바꾸면 picker 는 다시 그릴 이유를 알지 못한다. 검색·모듈 선택은 그 누락 때문에\n * 눌러도 화면이 그대로였다.\n */\n private refreshPicker() {\n this.picker?.requestUpdate()\n }\n\n private selectModule(category: string) {\n if (this.selected === category) {\n return\n }\n\n this.selected = category\n this.refreshPicker()\n }\n\n /**\n * ox-input-search 의 `change` 는 입력을 끝내고 포커스를 옮길 때만 온다. 모듈을 찾는 칸은\n * 입력하는 즉시 좁혀져야 하므로 shadow 경계를 넘어오는 native input 이벤트도 함께 받는다.\n */\n private updateKeyword(event: Event) {\n const nativeInput = event.composedPath().find(node => node instanceof HTMLInputElement) as\n HTMLInputElement | undefined\n const keyword = nativeInput?.value ?? (event.target as any)?.value ?? ''\n\n this.keyword = keyword\n\n /* 검색 결과에 현재 선택 모듈이 없으면 첫 결과를 바로 연다. 오른쪽에 이전 모듈의 축이\n 남아 있으면 검색이 안 된 것처럼 보이기 때문이다. */\n this.selected = selectedVisibleModule(this.visibleModules, this.selected)\n\n this.refreshPicker()\n }\n\n /*\n * The page is named after the screen, not after the role it happens to be showing.\n *\n * It used to carry `this.subject.name`, and the reader then saw \"Administrator\" three times\n * on one screen: the tab, the page header, and the subject block in the body. Worse, the page\n * had no name of its own - nothing said what this screen is for.\n *\n * A record-valued title does have precedent here (appliance, app-binding, application in this\n * package, and board-ui's play list - 4 of the 6 object-form titles), but those pages are the\n * record's own detail. This one is one facet of a role, and the role is already named in the\n * body, where a description can sit beside it.\n */\n get context() {\n return {\n title: { icon: 'vpn_key', text: i18next.t('title.role privileges') },\n help: 'auth/roles',\n actions: this.inherited\n ? []\n : [\n {\n title: i18next.t('button.save'),\n disabled: !this.dirty || !this.savable,\n disabledTitle: this.saving ? '권한을 저장하고 있습니다.' : '권한을 바꾼 뒤 저장할 수 있습니다.',\n action: this.onSave.bind(this)\n }\n ]\n }\n }\n\n render() {\n /*\n * Loading, refused and failed are one part now (ADR-0060 decision 8). The note this screen drew\n * said \"failed, please try again\" whatever had happened: a privilege refusal - likely here, of\n * all screens - read as a breakage, and it offered no way to read it again.\n */\n return html`<ox-page-state\n .loading=${this.fetchState === 'loading'}\n .error=${this.loadError}\n @retry=${() => this.fetch()}\n >${this.renderRole()}</ox-page-state\n >`\n }\n\n private renderRole() {\n return html`\n ${\n this.inherited\n ? html`<div note attention>\n ${i18next.t('text.role belongs to x domain and is edited there', {\n x: this.subject?.domain?.name || this.subject?.domain?.subdomain\n })}\n </div>`\n : nothing\n }\n ${this.renderSubject()} ${this.renderHolders()} ${this.renderGranted()}\n `\n }\n\n /**\n * 어느 역할을 고치고 있나, 그리고 그것이 무엇을 위한 것인가.\n *\n * 머리말은 이름만 들고 있었고, **직무를 말하는 한 줄인 설명은 목록에만 있었다.** 링크로\n * 들어온 사람은 `Administrator` 와 `PLANT-MANAGER` 를 낱말 말고는 구별할 방법이 없다.\n *\n * 설명이 없는 역할은 없다고 말한다. 다음에 할 일은 그것을 쓰는 것이고, 자리는 목록 격자다.\n */\n private renderSubject() {\n if (this.fetchState !== 'loaded') {\n return nothing\n }\n\n return html`\n <div subject>\n <div name>${this.subject?.name || i18next.t('label.role')}</div>\n ${\n this.subject?.description\n ? html`<div says>${this.subject.description}</div>`\n : html`<div says missing>${i18next.t('text.no description for this role')}</div>`\n }\n </div>\n `\n }\n\n /**\n * **이 역할이 할 수 있는 일 — 이것이 본문이다.**\n *\n * ── 뒤집은 이유 (UX 레인 §2, 사용자 평가) ──────────────────────────────────\n * 격자는 「이 모듈에서 무엇이 켜졌나」에 답한다. 관리자가 묻는 것은 **「이 역할이 무엇을\n * 하나」**이고, 격자로 그것을 읽으려면 모듈 46개를 한 번씩 열어야 한다. 역할을 부여하는\n * 사람은 부여하기 **전에** 그 답이 필요하다.\n *\n * 그래서 켜진 것을 문장으로 늘어놓는 것이 본문이 되고, 격자는 **더할 때 쓰는 도구**로\n * 자리를 옮긴다(`renderAdd`). 문장이 줄마다 있으므로 배우는 일이 따로 없다.\n *\n * ── 켜진 것만 낸다 ─────────────────────────────────────────────────────────\n * 전체를 여기 또 그리면 같은 46 모듈이 화면에 두 번 선다. 전체는 「더하기」 판이 갖는다.\n *\n * ── 글이 없을 때 ───────────────────────────────────────────────────────────\n * 「더하기」 판에서는 문장이 없으면 **없다고 말해도 된다** — 체크박스 옆에 「읽기」라는\n * 라벨이 이미 있어서 무엇을 켜는지 알 수 있다.\n *\n * 여기서는 다르다. **이 줄이 곧 그 권한의 신분이다.** 없다고만 쓰면 무엇을 떼는지 모르는\n * 줄이 되므로, 그때는 `모듈 · 축` 으로 이름을 짓는다. 그것은 코드 낱말이 아니다 —\n * `privilege.category.*` 57개와 「읽기/쓰기」가 사람 말이다.\n */\n private renderGranted() {\n if (this.fetchState !== 'loaded') {\n return nothing\n }\n\n const granted = this.modules\n .flatMap(module => module.cells.map(cell => ({ category: module.category, cell })))\n .filter(entry => this.assigned.has(entry.cell.id))\n\n return html`\n <div granted>\n <header>\n ${i18next.t('field.granted privileges')}\n <span count>${granted.length}</span>\n <nav>\n ${this.viewButton('privileges', i18next.t('label.privileges view'))} ${this.viewButton('tasks', i18next.t('label.tasks view'))}\n ${this.viewButton('template', i18next.t('label.template view'))}\n </nav>\n </header>\n\n ${\n this.view === 'tasks'\n ? this.renderTasks()\n : this.view === 'template'\n ? this.renderTemplate()\n : html`${\n granted.length\n ? html`<ul lines>\n ${granted.map(({ category, cell }) => this.renderGrantedLine(category, cell))}\n </ul>`\n : html`<div empty>${i18next.t('text.nothing is on for this role yet')}</div>`\n }\n ${this.renderAdd()}`\n }\n </div>\n `\n }\n\n private viewButton(view: View, label: string) {\n return html`<button ?selected=${this.view === view} @click=${() => this.showView(view)}>${label}</button>`\n }\n\n private async showView(view: View) {\n this.view = view\n if (view === 'template' && this.templates === null) {\n await this.fetchTemplates()\n }\n }\n\n /**\n * **The role by task** (ADR-0139 decision 11) — which of each task's privileges this role holds, as the server\n * computes it from the saved privileges.\n *\n * It says what the role holds, nothing more: 「전부」 is not \"this person can do the work\" (another role, the\n * data's own scope or an approval line decide that too), and 「일부」 is not a fault — a privilege several tasks\n * share makes each of them 「일부」.\n *\n * A task can be added to the role or taken off it (the user, 2026-10-20). The role stores privileges, never tasks,\n * so either is a set of privileges, shown before it is saved. Taking a task off keeps what another task the role\n * holds whole still uses (`role-task-edit.ts`). Both save at once and read the coverage again, so they are not\n * offered over unsaved changes - the coverage is the saved role's.\n */\n private renderTasks() {\n const tasks = this.coverage.tasks\n const held = tasks.filter(task => task.status !== 'none').sort((a, b) => statusOrder(a.status) - statusOrder(b.status))\n const unheld = tasks.filter(task => task.status === 'none')\n\n return html`\n <div review>\n ${this.dirty ? html`<div stale>${i18next.t('text.counted from the saved privileges')}</div>` : nothing}\n ${\n held.length\n ? html`<ul tasks>\n ${held.map(task => this.renderTask(task))}\n </ul>`\n : html`<div empty>${i18next.t('text.this role holds no task')}</div>`\n }\n ${\n unheld.length\n ? html`<h4>\n ${i18next.t('label.tasks this role does not hold')}\n <span count>${unheld.length}</span>\n <button quiet @click=${() => (this.showUnheldTasks = !this.showUnheldTasks)}>\n ${this.showUnheldTasks ? i18next.t('button.fold') : i18next.t('button.unfold')}\n </button>\n </h4>\n ${\n this.showUnheldTasks\n ? html`<ul tasks quiet>\n ${unheld.map(task => html`<li>${taskName(task.taskId)} ${this.taskEditButton(task)}</li>`)}\n </ul>`\n : nothing\n }`\n : nothing\n }\n ${\n this.coverage.unclassified.length\n ? html`<h4>\n ${i18next.t('label.privileges no task names')}\n <span count>${countOf(this.coverage.unclassified)}</span>\n </h4>\n ${this.renderSentences(this.coverage.unclassified)}`\n : nothing\n }\n </div>\n `\n }\n\n /** 더하기 for a task the role lacks some or all of, 빼기 for one it holds some or all of. */\n private taskEditButton(task: TaskCoverage) {\n if (this.inherited || task.status === 'broken') return nothing\n const disabled = this.dirty || this.saving\n const why = this.dirty ? i18next.t('text.save before editing by task') : ''\n return html`\n ${\n task.status !== 'all'\n ? html`<button quiet ?disabled=${disabled} title=${why} @click=${() => this.editTask(task, 'add')}>${i18next.t('button.add task')}</button>`\n : nothing\n }\n ${\n task.status !== 'none'\n ? html`<button quiet ?disabled=${disabled} title=${why} @click=${() => this.editTask(task, 'remove')}>${i18next.t('button.remove task')}</button>`\n : nothing\n }\n `\n }\n\n /** Adds a task's missing privileges to the role, or takes its own off - after showing which - and saves. */\n private async editTask(task: TaskCoverage, mode: 'add' | 'remove') {\n if (this.dirty || this.saving || this.inherited) return\n\n const name = taskName(task.taskId)\n const { remove, kept } = mode === 'remove' ? pairsToRemove(task, this.coverage.tasks) : { remove: [], kept: [] }\n const pairs = mode === 'add' ? pairsToAdd(task) : remove\n const words = (list: string[]) => list.map(pair => this.sentenceOf(pair.split(':')[0], pair.split(':')[1])).join(', ')\n const keptLine = kept.length ? ` ${i18next.t('text.kept for other tasks', { list: words(kept) })}` : ''\n\n if (!pairs.length) {\n await OxPrompt.open({\n type: 'info',\n title: i18next.t('title.remove task'),\n text: i18next.t('text.nothing to remove for task', { task: name }) + keptLine,\n confirmButton: { text: i18next.t('button.confirm') }\n })\n return\n }\n\n const confirmed = await OxPrompt.open({\n type: 'info',\n title: i18next.t(mode === 'add' ? 'title.add task' : 'title.remove task'),\n text: i18next.t(mode === 'add' ? 'text.add task privileges' : 'text.remove task privileges', { task: name, count: pairs.length, list: words(pairs) }) + keptLine,\n confirmButton: { text: i18next.t(mode === 'add' ? 'button.add task' : 'button.remove task') },\n cancelButton: { text: i18next.t('button.cancel') }\n })\n if (!confirmed) return\n\n const idOf = new Map(this.modules.flatMap(module => module.cells.map(cell => [pairOf(module.category, cell.axis), cell.id] as [string, string])))\n const next = new Set(this.assigned)\n for (const pair of pairs) {\n const id = idOf.get(pair)\n if (id) mode === 'add' ? next.add(id) : next.delete(id)\n }\n\n this.assigned = next\n this.dirty = true\n await this.onSave()\n }\n\n private renderTask(task: TaskCoverage) {\n const held = countOf(task.held)\n const total = held + countOf(task.missing)\n const says = i18next.t(`task.${task.taskId}.description`, { defaultValue: '' })\n\n return html`\n <li>\n <div>\n <span task-name>${taskName(task.taskId)}</span>\n ${\n task.status === 'broken'\n ? html`<span state broken>${i18next.t('text.task names an undeclared privilege')}</span>`\n : task.status === 'all'\n ? html`<span state all>${i18next.t('text.task held whole')}</span>`\n : html`<span state>${i18next.t('text.task held in part', { held, total })}</span>`\n }\n ${this.taskEditButton(task)}\n </div>\n ${says ? html`<div task-says>${says}</div>` : nothing}\n ${\n task.status === 'some'\n ? html`<div task-missing>${i18next.t('text.not held x', { x: task.missing.flatMap(grant => grant.axes.map(axis => this.sentenceOf(grant.category, axis))).join(' · ') })}</div>`\n : nothing\n }\n </li>\n `\n }\n\n /**\n * **The role against a template the administrator picks** (ADR-0139 decision 11) — whatever the role is called.\n *\n * The template of the role's name is picked to begin with; a role named in the site's own words (경영지원, 인사급여)\n * has no such pair, and the administrator picks one. The role is never renamed to make a pair: an approval line\n * holds the role. What the role holds beyond the template is shown as wider, not wrong — the site may have widened it\n * on purpose — and nothing takes it away. What it lacks can be added; adding is the same top-up the template button\n * on the role list runs (`createRoleFromTemplate` onto this role's name).\n */\n private renderTemplate() {\n if (this.templates === null) {\n return html`<div review><div empty>${i18next.t('text.loading')}</div></div>`\n }\n\n if (!this.templates.length) {\n return html`<div review><div empty>${i18next.t('text.no template to compare with')}</div></div>`\n }\n\n const template = this.templates.find(one => one.id === this.templateId)\n const gap = this.gap\n\n return html`\n <div review>\n <select @change=${(event: Event) => this.pickTemplate((event.target as HTMLSelectElement).value)}>\n <option value=\"\" ?selected=${!this.templateId}>${i18next.t('text.pick a template to compare with')}</option>\n ${this.templates.map(one => html`<option value=${one.id} ?selected=${one.id === this.templateId}>${one.name}</option>`)}\n </select>\n ${this.dirty ? html`<div stale>${i18next.t('text.counted from the saved privileges')}</div>` : nothing}\n ${\n template?.definitionErrors.length\n ? html`<div note attention>${i18next.t('text.template cannot be applied because', { list: template.definitionErrors.join(' · ') })}</div>`\n : nothing\n }\n ${\n gap\n ? html`\n <h4>\n ${i18next.t('label.missing against the template')}\n <span count>${gap.missingCount}</span>\n ${\n gap.missingCount && !this.inherited\n ? html`<button quiet ?disabled=${this.dirty || this.saving || Boolean(template?.definitionErrors.length)} @click=${() => this.addMissing()}>\n ${i18next.t('button.add')}\n </button>`\n : nothing\n }\n </h4>\n ${gap.missingCount ? this.renderSentences(gap.missing) : nothing}\n <h4>\n ${i18next.t('label.wider than the template')}\n <span count>${gap.extraCount}</span>\n </h4>\n ${gap.extraCount ? this.renderSentences(gap.extra) : nothing}\n `\n : nothing\n }\n </div>\n `\n }\n\n private renderSentences(grants: Grant[]) {\n return html`<ul sentences>\n ${grants.flatMap(grant => grant.axes.map(axis => html`<li>${this.sentenceOf(grant.category, axis)}</li>`))}\n </ul>`\n }\n\n /** A privilege in words — the sentence the privileges view draws, or its module and axis when there is none. */\n private sentenceOf(category: string, axis: string): string {\n const cell = this.modules.find(module => module.category === category)?.cells.find(one => one.axis === axis)\n return (cell && axisText(category, cell)) || `${moduleLabel(category)} · ${axisLabel(axis)}`\n }\n\n private async fetchTemplates() {\n const response = await client.query({\n query: gql`\n query {\n roleTemplates {\n id\n name\n definitionErrors\n }\n }\n `,\n context: gqlContext()\n })\n\n /* Reading templates asks the same door creating a role does; without it there is nothing to compare with. */\n const templates: TemplateChoice[] = response.errors?.length ? [] : response.data?.roleTemplates || []\n this.templates = templates\n const paired = templates.find(one => one.name === this.subject?.name)\n await this.pickTemplate(paired?.id || '')\n }\n\n private async pickTemplate(templateId: string) {\n this.templateId = templateId\n this.gap = null\n if (!templateId) return\n\n const response = await client.query({\n query: gql`\n query ($filters: [Filter!], $templateId: String) {\n roles(filters: $filters) {\n items {\n id\n templateGap(templateId: $templateId) {\n templateId\n templateName\n missing { ${GRANT_FIELDS} }\n missingCount\n extra { ${GRANT_FIELDS} }\n extraCount\n }\n }\n }\n }\n `,\n variables: { filters: [{ name: 'id', operator: 'eq', value: this.subject.id }], templateId },\n context: gqlContext()\n })\n\n if (response.errors?.length) {\n document.dispatchEvent(new CustomEvent('notify', { detail: { level: 'error', message: response.errors[0]?.message } }))\n return\n }\n\n if (this.templateId === templateId) this.gap = response.data?.roles?.items?.[0]?.templateGap || null\n }\n\n /** Adds what the role lacks against the picked template — after showing what it adds. */\n private async addMissing() {\n const gap = this.gap\n if (!gap?.missingCount || this.dirty || this.inherited) return\n\n const list = gap.missing.flatMap(grant => grant.axes.map(axis => this.sentenceOf(grant.category, axis))).join(', ')\n const confirmed = await OxPrompt.open({\n type: 'info',\n title: i18next.t('title.add template privileges'),\n text: i18next.t('text.add template privileges', { template: gap.templateName, count: gap.missingCount, list }),\n confirmButton: { text: i18next.t('button.add') },\n cancelButton: { text: i18next.t('button.cancel') }\n })\n if (!confirmed) return\n\n const response = await client.mutate({\n mutation: gql`\n mutation ($templateId: String!, $name: String) {\n createRoleFromTemplate(templateId: $templateId, name: $name) {\n id\n }\n }\n `,\n variables: { templateId: gap.templateId, name: this.subject.name },\n context: gqlContext()\n })\n\n if (response.errors?.length) {\n document.dispatchEvent(new CustomEvent('notify', { detail: { level: 'error', message: response.errors[0]?.message } }))\n return\n }\n\n document.dispatchEvent(new CustomEvent('notify', { detail: { message: i18next.t('text.data_updated_successfully') } }))\n await this.fetch()\n await this.pickTemplate(gap.templateId)\n }\n\n /**\n * 한 줄 — 문장과 [떼기].\n *\n * 유령(이 배포의 어떤 코드도 요구하지 않는 권한)은 취소선으로 보이고 **떼는 것만** 된다.\n * 이미 붙어 있으니 뗄 수 있어야 하고, 붙여도 아무 일이 없으니 다시 켤 이유가 없다.\n */\n private renderGrantedLine(category: string, cell: Cell) {\n const written = axisText(category, cell)\n\n return html`\n <li ?ghost=${cell.deprecated}>\n <div line>\n ${written || html`<span named>${moduleLabel(category)} · ${axisLabel(cell.axis)}</span>`}\n ${cell.deprecated ? html`<span tag>${i18next.t('text.privilege no longer declared')}</span>` : nothing}\n </div>\n\n ${\n this.inherited\n ? nothing\n : html`<button drop title=${i18next.t('button.drop')} @click=${() => this.onToggle(cell.id, false)}>\n <md-icon>close</md-icon>\n </button>`\n }\n </li>\n `\n }\n\n /**\n * 「+ 할 수 있는 일 더하기」 — 격자가 여기 들어간다.\n *\n * 열어 두지 않는 이유는 하나다 — **처음 읽는 것이 「이 역할이 무엇을 하나」여야 한다.**\n * 46 모듈이 늘 펼쳐져 있으면 그 답이 화면 아래로 밀린다.\n *\n * 상속 역할에는 내주지 않는다. 여기서 저장할 수 없으므로 켜 보게 하는 것이 헛수고다 —\n * 대신 위쪽 안내가 어느 도메인에서 고치는지 말한다.\n */\n private renderAdd() {\n if (this.inherited || this.fetchState !== 'loaded') {\n return nothing\n }\n\n if (!this.modules.length) {\n return html`<div note attention>${i18next.t('text.no privilege declared in this deployment')}</div>`\n }\n\n return html`\n <button add @click=${() => this.openAdd()}>\n <md-icon>add</md-icon>${i18next.t('button.add what it can do')}\n </button>\n `\n }\n\n /**\n * 고르는 판을 **넓은 팝업**으로 연다.\n *\n * 사용자 말씀이다 — 「'할 수 있는 일 더하기' 작동을 팝업으로 넓게 할 수 있었으면 어떤가?\n * 지금은 좁아보이고」. 페이지 안에 펼치면 본문(할 수 있는 일)이 아래로 밀리고, 남은 폭에서\n * 모듈 46개와 축 목록을 함께 봐야 한다. 고르는 동안은 **고르는 것만** 보는 것이 맞다.\n *\n * ```\n * [읽었음] ox-floating-overlay.ts:126-134 [size=large] width 100% · height 100%\n * ```\n *\n * `escapable` 을 건드리지 않는다 — 켠 것은 팝업이 아니라 **이 페이지의 `assigned`** 에 바로\n * 들어가므로 닫든 ESC 를 누르든 잃는 것이 없다. 물어볼 것이 없는 판에 물으면 그 물음이 값을\n * 잃는다. 저장은 여전히 툴바가 한다.\n *\n * ⚠ 부품을 **동적으로 들인다.** 그 부품은 스타일 한 벌을 이 클래스에서 가져가므로, 위에서\n * 정적으로 import 하면 순환이 된다. 여는 순간에 들이면 그 고리가 끊긴다.\n */\n private async openAdd() {\n await import('../../components/role-privilege-picker.js')\n\n const popup = openPopup(\n html`<role-privilege-picker .page=${this} @close=${() => popup.close()}></role-privilege-picker>`,\n {\n backdrop: true,\n size: 'large',\n title: i18next.t('button.add what it can do')\n }\n )\n }\n\n /**\n * 고르는 판의 몸체 — **팝업이 이것을 그린다.**\n *\n * 판을 부품 안으로 옮기지 않는다. 모듈 접기·축 줄·유령 규칙이 전부 이 페이지의 상태\n * (`modules`·`assigned`·`selected`·`keyword`)를 보기 때문이고, 옮기면 **상태가 두 벌**이 된다.\n * 두 벌이 되는 순간 어느 쪽이 저장 범위인지 흐려진다 — 옛 편집기가 `querySelectorAll` 로\n * 범위를 만들던 결함이 정확히 그 모양이었다.\n */\n renderPickerBody() {\n return html`\n <div body class=\"page-fill\">\n <div panel>\n <header>\n ${i18next.t('field.module')}\n <span count>${this.modules.length}</span>\n </header>\n <ox-input-search\n .placeholder=${i18next.t('text.filter by module name')}\n .value=${this.keyword}\n @input=${(event: Event) => this.updateKeyword(event)}\n @change=${(event: Event) => this.updateKeyword(event)}\n ></ox-input-search>\n <div scroll>${this.renderModules()}</div>\n </div>\n\n <div panel>\n <header>\n ${this.selected ? moduleLabel(this.selected) : i18next.t('field.privileges')}\n <span count>${i18next.t('text.x of y on', { x: this.assigned.size, y: this.cellCount() })}</span>\n </header>\n <div scroll>${this.renderAxes()}</div>\n </div>\n </div>\n <div picker-footer>\n <span>\n ${\n this.saving\n ? '권한을 저장하고 있습니다.'\n : this.dirty\n ? '변경 사항이 있습니다. 저장해야 적용됩니다.'\n : '권한을 바꾼 뒤 저장할 수 있습니다.'\n }\n </span>\n <button ?disabled=${!this.savable || !this.dirty} @click=${() => this.onSave()}>\n ${this.saving ? '저장 중…' : i18next.t('button.save')}\n </button>\n </div>\n `\n }\n\n /** 팝업이 열려 있는 동안 자기를 등록한다 — 토글이 그것을 다시 그리게 한다. */\n picker?: { requestUpdate: () => void }\n\n /**\n * 이 역할을 가진 사람.\n *\n * 권한을 켜기 **전에** 누가 영향을 받는지 보이는 자리다. 아무도 안 가진 역할을 고치는 것과\n * 40명이 가진 역할을 고치는 것은 다른 일인데, 옛 화면은 그 둘이 똑같이 보였다.\n */\n private renderHolders() {\n if (this.fetchState !== 'loaded') {\n return nothing\n }\n\n return this.holders.length\n ? html`<div note>\n ${i18next.t('text.x people hold this role', { x: this.holders.length })} —\n ${this.holders.map(h => h.name || h.email).join(', ')}\n </div>`\n : html`<div note>${i18next.t('text.no one holds this role yet')}</div>`\n }\n\n private renderModules() {\n if (this.fetchState === 'loading') {\n return html`<div empty>${i18next.t('text.loading')}</div>`\n }\n\n if (!this.modules.length) {\n return html`<div empty>${i18next.t('text.no privilege declared in this deployment')}</div>`\n }\n\n if (!this.visibleModules.length) {\n return html`<div empty>${i18next.t('text.nothing matches x', { x: this.keyword })}</div>`\n }\n\n return html`\n <ul modules>\n ${this.visibleModules.map(module => {\n const on = module.cells.filter(cell => this.assigned.has(cell.id)).length\n\n return html`\n <li ?selected=${module.category === this.selected} @click=${() => this.selectModule(module.category)}>\n <span>${moduleLabel(module.category)}</span>\n ${\n on\n ? html`<span on>${i18next.t('text.x on', { x: on })}</span>`\n : html`<span none>${i18next.t('text.none on')}</span>`\n }\n </li>\n `\n })}\n </ul>\n `\n }\n\n private renderAxes() {\n if (this.fetchState === 'loading') {\n return html`<div empty>${i18next.t('text.loading')}</div>`\n }\n\n const module = this.modules.find(m => m.category === this.selected)\n if (!module) {\n /* 아무것도 안 고른 상태 — 무엇을 해야 하는지 말한다. */\n return html`<div empty>${i18next.t('text.pick a module on the left')}</div>`\n }\n\n return html`\n <ul axes>\n ${module.cells.map(cell => {\n const on = this.assigned.has(cell.id)\n\n return html`\n <li\n ?ghost=${cell.deprecated}\n ?readonly=${this.inherited || (cell.deprecated && !on)}\n @click=${(event: MouseEvent) => this.toggleCellFromRow(event, cell, on)}\n >\n <input\n type=\"checkbox\"\n id=\"cell-${cell.id}\"\n .checked=${on}\n ?disabled=${this.inherited || (cell.deprecated && !on)}\n @change=${(e: Event) => this.onToggle(cell.id, (e.target as HTMLInputElement).checked)}\n />\n <label for=\"cell-${cell.id}\">${axisLabel(cell.axis)}</label>\n\n ${\n cell.deprecated\n ? html`<div says>${i18next.t('text.privilege no longer declared')}</div>`\n : axisSays(module.category, cell) ||\n html`<div says missing>${i18next.t('text.no description for this privilege')}</div>`\n }\n </li>\n `\n })}\n </ul>\n `\n }\n\n private cellCount(): number {\n return this.modules.reduce((sum, module) => sum + module.cells.length, 0)\n }\n\n async pageUpdated(changes: any, lifecycle: any) {\n if (this.active) {\n this.lifecycle = lifecycle\n await this.fetch()\n }\n }\n\n /**\n * 한 번의 질의로 **전체 권한과 이 역할이 가진 것**을 함께 읽는다(`rolePrivileges(roleId:)`).\n *\n * 역할 이름과 가진 사람은 `roles(filters: id)` 로 같은 문서에서 받는다 — 옛 편집기는 역할을\n * **이름**으로 다시 찾았고, 부모 도메인에 같은 이름이 있으면 남의 역할을 그렸다.\n */\n private async fetch() {\n const roleId = this.lifecycle?.resourceId\n if (!roleId) {\n this.fetchState = 'failed'\n this.loadError = new Error(i18next.t('text.no role to show'))\n return\n }\n\n this.fetchState = 'loading'\n this.loadError = null\n this.mySubdomain = (await getDomain())?.subdomain || ''\n\n const response = await client.query({\n query: gql`\n query ($roleId: String!, $filters: [Filter!]) {\n rolePrivileges(roleId: $roleId) {\n id\n name\n category\n description\n assigned\n deprecated\n }\n roles(filters: $filters) {\n items {\n id\n name\n description\n domain {\n id\n name\n subdomain\n }\n users {\n id\n name\n email\n }\n taskCoverage {\n tasks {\n taskId\n module\n status\n held { ${GRANT_FIELDS} }\n missing { ${GRANT_FIELDS} }\n undeclared { ${GRANT_FIELDS} }\n }\n unclassified { ${GRANT_FIELDS} }\n }\n }\n }\n }\n `,\n variables: { roleId, filters: [{ name: 'id', operator: 'eq', value: roleId }] },\n context: gqlContext()\n })\n\n if (response.errors?.length) {\n /* 실패를 「권한이 하나도 없다」로 그리지 않는다 — 저장하면 실제로 그렇게 된다. */\n this.fetchState = 'failed'\n this.loadError = response.errors\n return\n }\n\n const role = response.data?.roles?.items?.[0]\n\n /*\n * A role id nobody has resolves with no error and no role, so the screen drew an empty\n * privilege list - a role that is gone looked exactly like a role that has been granted\n * nothing, and saving from there would have written that emptiness back.\n */\n if (!role?.id) {\n this.fetchState = 'failed'\n this.loadError = new Error(i18next.t('text.this role no longer exists'))\n return\n }\n\n this.subject = role\n this.holders = this.subject.users || []\n this.coverage = role.taskCoverage || { tasks: [], unclassified: [] }\n this.build(response.data?.rolePrivileges || [])\n this.fetchState = 'loaded'\n\n /*\n * The shell reads `context` once, when the page becomes active - before this fetch\n * resolves. Without this call the header keeps the fallback, so the screen showed\n * \"권한\" and never said which role was being edited.\n */\n this.updateContext()\n }\n\n /** 평평한 목록을 모듈로 접는다. 축 순서는 아는 것 먼저, 나머지는 이름 순. */\n private build(privileges: any[]) {\n const byCategory = new Map<string, Module>()\n const assigned = new Set<string>()\n\n for (const privilege of privileges) {\n const { id, name, category, description, deprecated } = privilege\n if (!id || !name || !category) {\n continue\n }\n\n if (privilege.assigned) {\n assigned.add(id)\n }\n\n const module = byCategory.get(category) || { category, cells: [] as Cell[] }\n module.cells.push({ id, axis: name, deprecated: Boolean(deprecated), description })\n byCategory.set(category, module)\n }\n\n for (const module of byCategory.values()) {\n module.cells.sort((a, b) => axisOrder(a.axis) - axisOrder(b.axis) || a.axis.localeCompare(b.axis))\n }\n\n this.modules = [...byCategory.values()].sort((a, b) =>\n moduleLabel(a.category).localeCompare(moduleLabel(b.category))\n )\n this.assigned = assigned\n this.dirty = false\n\n /* 고른 것이 없으면 첫 모듈을 연다 — 빈 오른쪽 판을 처음 화면으로 두지 않는다. */\n if (!this.modules.find(m => m.category === this.selected)) {\n this.selected = this.modules[0]?.category || ''\n }\n }\n\n private onToggle(id: string, checked: boolean) {\n if (this.assigned.has(id) === checked) {\n return\n }\n\n const next = new Set(this.assigned)\n checked ? next.add(id) : next.delete(id)\n this.assigned = next\n this.dirty = true\n this.updateContext()\n\n /* 팝업은 자기 상태를 안 들고 이 페이지를 본다 — 열려 있으면 다시 그리라고 알린다. */\n this.refreshPicker()\n }\n\n /** 라벨·설명을 포함한 행 전체를 누를 수 있게 한다. checkbox와 키보드 Space는 native change가 맡는다. */\n private toggleCellFromRow(event: MouseEvent, cell: Cell, on: boolean) {\n if (this.inherited || (cell.deprecated && !on) || event.target instanceof HTMLInputElement) {\n return\n }\n\n /* label의 기본 동작이 checkbox change를 한 번 더 보내지 않게 막는다. */\n event.preventDefault()\n this.onToggle(cell.id, !on)\n }\n\n get addressDirty(): boolean { return this.dirty }\n\n get deactivation(): boolean | Parameters<typeof OxPrompt.open>[0] {\n if (!this.dirty) return true\n return {\n title: '저장하지 않은 변경 사항',\n text: '수정된 권한이 있습니다. 저장하지 않고 이 페이지를 나가시겠습니까?',\n confirmButton: { text: '저장하지 않고 나가기' },\n cancelButton: { text: i18next.t('button.cancel') }\n }\n }\n\n /**\n * 저장.\n *\n * **범위는 `modules` 전체다.** 왼쪽에서 다른 모듈을 고르면 앞 모듈의 체크박스는 DOM 에\n * 없는데, 그것을 훑어 보내던 것이 옛 편집기가 권한을 조용히 떼던 자리다.\n *\n * `privileges` 만 보낸다 — 이름과 설명은 목록 격자가 소유한다.\n */\n private async onSave() {\n if (!this.savable) {\n return\n }\n\n this.saving = true\n this.updateContext()\n\n try {\n const response = await client.mutate({\n mutation: gql`\n mutation ($id: String!, $patch: RolePatch!) {\n updateRole(id: $id, patch: $patch) {\n id\n }\n }\n `,\n variables: {\n id: this.subject.id,\n patch: { privileges: [...this.assigned].map(id => ({ id })) }\n },\n context: gqlContext()\n })\n\n if (response.errors?.length) {\n document.dispatchEvent(\n new CustomEvent('notify', { detail: { level: 'error', message: response.errors[0]?.message } })\n )\n return\n }\n\n document.dispatchEvent(\n new CustomEvent('notify', { detail: { message: i18next.t('text.data_updated_successfully') } })\n )\n\n /* 유령을 뗐으면 목록에서 사라져야 하고, 켠 것이 요약에 반영돼야 한다. */\n await this.fetch()\n this.dirty = false\n } finally {\n this.saving = false\n this.updateContext()\n }\n }\n}\n\n/**\n * 모듈 이름을 사람 말로.\n *\n * `privilege.category.<category>` 키가 있으면 그것을 쓰고, 없으면 **코드 낱말을 그대로** 낸다 —\n * 지어내지 않는다. 지금 46개 중 하나(`system`)만 채워져 있고, UX 레인이 나머지를 쓰고 있다.\n */\nfunction moduleLabel(category: string): string {\n const key = `privilege.category.${category}`\n return i18next.t(key, { defaultValue: category })\n}\n\n/**\n * 「켜면 무엇을 할 수 있게 되나」 — **이 화면의 이유다.**\n *\n * 사용자 평가가 「화면으로부터 배울 수 있는 것이 없다」였고, 그 원인은 화면이 감춘 것이 아니라\n * 시스템에 그 문장이 없던 것이었다. 부팅 동기화가 `{ category, name }` 둘만 저장해서\n * (`auth-base/server/index.ts:34-36`) 713개 선언이 전부 빈 설명이었다.\n *\n * 그 글을 UX 레인이 `privilege.allows.<category>.<axis>` 로 썼다(57 모듈 · 104 문장, ko·en).\n * 번역은 디스크에서 서비스되므로 **배포를 기다리지 않고 붙는다.**\n *\n * ── 보는 순서 ───────────────────────────────────────────────────────────────\n * ```\n * 1 서버 칸 채워지면 그것이 이긴다 — 배포마다 다를 수 있는 사실은 서버가 안다\n * 2 우리가 쓴 글 privilege.allows.<category>.<axis>\n * 3 없다고 말한다 둘 다 없으면. 지어내지 않는다\n * ```\n *\n * ⚠ **`defaultValue: ''` 가 없으면 3번으로 못 떨어진다** — i18next 는 키가 없을 때 **키 이름을\n * 그대로** 돌려주므로 화면에 `privilege.allows.board.query` 가 찍힌다. 빈 문자열을 기본값으로\n * 줘서 「없음」이 거짓으로 판정되게 한다.\n */\nfunction axisText(category: string, cell: Cell): string {\n if (cell.description) {\n return cell.description\n }\n\n return i18next.t(`privilege.allows.${category}.${cell.axis}`, { defaultValue: '' })\n}\n\nfunction axisSays(category: string, cell: Cell) {\n const written = axisText(category, cell)\n return written ? html`<div says>${written}</div>` : null\n}\n\nfunction axisOrder(axis: string): number {\n const index = KNOWN_AXES.indexOf(axis)\n return index < 0 ? KNOWN_AXES.length : index\n}\n\n/** A task's name in the words of the module that declares it (`task.<id>.name`); the id when there are none. */\nfunction taskName(id: string): string {\n return i18next.t(`task.${id}.name`, { defaultValue: id })\n}\n\nfunction statusOrder(status: TaskCoverage['status']): number {\n return ['broken', 'all', 'some', 'none'].indexOf(status)\n}\n\nfunction countOf(grants: Grant[]): number {\n return grants.reduce((count, grant) => count + grant.axes.length, 0)\n}\n"]}
|
|
1
|
+
{"version":3,"file":"role-privilege-page.js","sourceRoot":"","sources":["../../../client/pages/role/role-privilege-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/input/ox-input-search.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, nothing } from 'lit'\nimport type { CSSResultGroup } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { client, gqlContext } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\n\nimport { KNOWN_AXES, axisLabel } from '../../utils/privilege-axis.js'\nimport { openPopup } from '@operato/layout'\nimport { OxPrompt } from '@operato/popup/ox-prompt.js'\nimport { PageView } from '@operato/shell'\nimport { CommonHeaderStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles'\nimport { getDomain } from '@things-factory/auth-base/dist-client'\n\nimport { filterPrivilegeModules, selectedVisibleModule } from '../../components/role-privilege-picker-state.js'\nimport { pairOf, pairsToAdd, pairsToRemove } from '../../components/role-task-edit.js'\n\n/*\n * The axis vocabulary moved to `utils/privilege-axis.ts`, and it no longer translates.\n * `query` used to print as 「읽기」 and `mutation` as 「쓰기」, which is false in the six\n * declarations where the axis and the GraphQL operation disagree on purpose. That file\n * carries the reasoning and the decided end state.\n */\n\ntype Cell = { id: string; axis: string; deprecated: boolean; description?: string }\ntype Module = { category: string; cells: Cell[] }\ntype Grant = { category: string; axes: string[] }\ntype TaskCoverage = { taskId: string; module: string; status: 'all' | 'some' | 'none' | 'broken'; held: Grant[]; missing: Grant[]; undeclared: Grant[] }\ntype Coverage = { tasks: TaskCoverage[]; unclassified: Grant[] }\ntype TemplateChoice = { id: string; name: string; definitionErrors: string[] }\ntype Gap = { templateId: string; templateName: string; missing: Grant[]; missingCount: number; extra: Grant[]; extraCount: number }\n\n/** What the body of the page shows: the privileges one by one, the role by task, or the role against a template. */\ntype View = 'privileges' | 'tasks' | 'template'\n\nconst GRANT_FIELDS = 'category axes'\n\n/**\n * 한 역할이 무엇을 할 수 있나 — **켜진 것이 문장으로 본문에 선다.**\n *\n * ── 사용자 평가가 이 화면을 두 번 뒤집었다 ──────────────────────────────────\n * **「화면으로부터 배울 수 있는 것이 없고, 혼란스럽고, 어렵고, 불편하다.」**\n *\n * 처음에는 팝업 안의 `모듈 × 읽기·쓰기` 격자였다. 격자는 「이 모듈에서 무엇이 켜졌나」에\n * 답하는데, 관리자가 묻는 것은 **「이 역할이 무엇을 하나」**다. 격자로 그걸 읽으려면 모듈\n * 46개를 한 번씩 열어야 하고, 역할을 부여하는 사람은 부여하기 **전에** 그 답이 필요하다.\n *\n * 그래서 뒤집었다(UX 레인 §2). 켜진 것을 문장으로 늘어놓는 것이 본문이고, 격자는 **더할 때\n * 쓰는 도구**로 자리를 옮겼다(`renderAdd`). 버린 것은 없다.\n *\n * ── 문장은 어디서 오나 ──────────────────────────────────────────────────────\n * ```\n * 1 Privilege.description 서버가 채우면 그것이 이긴다 — 배포마다 다를 수 있는 사실은 서버가 안다\n * 2 privilege.allows.<category>.<axis> UX 레인이 쓴 글 (57 모듈 · 104 문장, ko·en)\n * 3 「아직 설명이 없습니다」 둘 다 없으면. 지어내지 않는다\n * ```\n *\n * 1번은 아직 **전부 비어 있다** — 부팅 동기화가 `{ category, name }` 둘만 저장한다\n * (`auth-base/server/index.ts:34-36`). 그래서 지금 화면에 나오는 문장은 전부 2번이다.\n *\n * ── 저장 범위는 서버가 준 목록이다 ──────────────────────────────────────────\n * 옛 편집기는 `querySelectorAll('input:checked')` 로 보냈다. 「더하기」 판에서 다른 모듈로\n * 옮기면 앞 모듈의 체크박스는 **DOM 에 없다** — 그 방식이면 모듈을 옮겨 다니는 것만으로\n * 권한이 조용히 떨어진다. 여기서는 `rolePrivileges` 가 준 전체가 범위이고 켠 것은\n * `assigned` 에만 있다. 화면이 무엇을 그렸는지와 무관하다.\n */\n@customElement('role-privilege-page')\nexport class RolePrivilegePage extends localize(i18next)(PageView) {\n static styles: CSSResultGroup = [\n /* The frame owns the page shell; the two panes are the content it sizes (ADR-0060 decision 8). */\n WorkSurfacePageStyles,\n ScrollbarStyles,\n CommonHeaderStyles,\n css`\n :host {\n color: var(--md-sys-color-on-surface);\n }\n\n [body] {\n display: grid;\n /* 왼쪽은 이름만 담으므로 고정, 오른쪽이 문장을 받으므로 남는 폭을 준다. */\n grid-template-columns: minmax(200px, 280px) 1fr;\n gap: var(--spacing-large, 12px);\n padding: var(--spacing-large, 12px);\n }\n\n @media screen and (max-width: 780px) {\n [body] {\n /* 좁으면 위아래로. 고른 모듈이 아래에 오는 것이 읽는 순서와 같다. */\n grid-template-columns: 1fr;\n grid-template-rows: auto 1fr;\n }\n }\n\n [panel] {\n display: flex;\n flex-direction: column;\n min-height: 0;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: var(--md-sys-shape-corner-medium, 10px);\n background-color: var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));\n overflow: hidden;\n }\n\n [panel] > header {\n display: flex;\n align-items: baseline;\n gap: 8px;\n padding: 10px 12px;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n color: var(--md-sys-color-on-surface-variant);\n font-size: 0.8rem;\n }\n\n [panel] > header [count] {\n color: var(--md-sys-color-outline);\n }\n\n [scroll] {\n flex: 1;\n min-height: 0;\n overflow: auto;\n }\n\n /*\n 걸러 보는 칸은 ox-input-search 다.\n\n 여기 md-filled-text-field 를 썼다가 사용자에게 「전체적인 스타일에 어울리지 않는다」를\n 들었다. 그것은 회색 채움 상자에 라벨이 떠 있는 **폼 칸**이고, 이 자리는 **찾는 칸**이다.\n\n 근거는 이 화면 자신에 있다 — 역할 목록의 검색 칸이 이 부품이다.\n [읽었음] @operato/data-grist/dist/src/filters/filters-form.js:153\n\n ⚠ 폼 칸도 md-* 를 안 쓴다 — 사용자가 2026-09-11 에 역할 화면 전체로 못 박았다\n (「너무 크고 스타일이 안 어울린다」). native input 에 FormFieldStyles 를 얹는다.\n 그 블록이 정확히 이 일을 하려고 있는 것이다(shadow 경계를 넘는 토큰 연결).\n */\n ox-input-search {\n margin: 8px 12px;\n width: calc(100% - 24px);\n }\n\n /* ── 왼쪽: 모듈 ──────────────────────────────────────────────────────── */\n ul[modules] {\n list-style: none;\n margin: 0;\n padding: 0;\n }\n\n ul[modules] li {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 8px;\n padding: 8px 12px;\n cursor: pointer;\n border-left: 3px solid transparent;\n }\n\n ul[modules] li:hover {\n background-color: var(--md-sys-color-surface-variant);\n }\n\n /* 고른 것은 면과 왼쪽 선으로 말한다 — 글자색만 바꾸면 야간에 안 보인다. */\n ul[modules] li[selected] {\n border-left-color: var(--md-sys-color-primary);\n background-color: var(--md-sys-color-secondary-container);\n color: var(--md-sys-color-on-secondary-container);\n }\n\n ul[modules] li [on] {\n font-size: 0.78rem;\n color: var(--md-sys-color-primary);\n }\n\n ul[modules] li[selected] [on] {\n color: var(--md-sys-color-on-secondary-container);\n }\n\n ul[modules] li [none] {\n font-size: 0.78rem;\n color: var(--md-sys-color-outline);\n }\n\n /* ── 오른쪽: 그 모듈에서 되는 일 ────────────────────────────────────── */\n ul[axes] {\n list-style: none;\n margin: 0;\n padding: 0;\n }\n\n ul[axes] li {\n display: grid;\n grid-template-columns: auto 1fr;\n gap: 4px 10px;\n padding: 12px 14px;\n border-bottom: 1px solid var(--md-sys-color-surface-variant);\n cursor: pointer;\n }\n\n ul[axes] li:last-child {\n border-bottom: none;\n }\n\n ul[axes] label {\n font-size: 0.95rem;\n font-weight: 600;\n cursor: pointer;\n }\n\n ul[axes] li[ghost] label {\n cursor: default;\n }\n\n ul[axes] li[ghost],\n ul[axes] li[readonly] {\n cursor: default;\n }\n\n [picker-footer] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--spacing-medium, 8px);\n padding: var(--spacing-medium, 8px) var(--spacing-large, 12px);\n border-top: 1px solid var(--md-sys-color-outline-variant);\n color: var(--md-sys-color-on-surface-variant);\n font-size: 0.85rem;\n }\n\n [picker-footer] button {\n flex: 0 0 auto;\n padding: 8px 16px;\n border: 0;\n border-radius: var(--md-sys-shape-corner-small, 6px);\n color: var(--md-sys-color-on-primary);\n background-color: var(--md-sys-color-primary);\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n }\n\n [picker-footer] button:disabled {\n cursor: default;\n opacity: 0.45;\n }\n\n /* 설명은 **본문**이다 — tooltip 이 아니다. 체크박스 아래 칸을 비워 두고 거기 놓는다. */\n ul[axes] [says] {\n grid-column: 2;\n color: var(--md-sys-color-on-surface-variant);\n font-size: 0.85rem;\n line-height: 1.55;\n }\n\n ul[axes] [says][missing] {\n color: var(--md-sys-color-outline);\n font-style: italic;\n }\n\n ul[axes] li[ghost] label {\n text-decoration: line-through;\n color: var(--md-sys-color-outline);\n }\n\n /*\n Which role is this? The header alone was not enough - it is one line of chrome above a\n page full of module names, and a role carries a description that says what the job is.\n Name and description sit in the body, where the reader is already looking.\n */\n [subject] {\n margin: var(--spacing-large, 12px) var(--spacing-large, 12px) 0;\n }\n\n [subject] [name] {\n font-size: 1.15rem;\n font-weight: 640;\n }\n\n [subject] [says] {\n margin-top: 2px;\n font-size: 0.88rem;\n line-height: 1.55;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n [subject] [says][missing] {\n color: var(--md-sys-color-outline);\n font-style: italic;\n }\n\n /*\n What is on, all together. The two panels answer \"what can this role do in one module\";\n they cannot answer \"what does this role do\", because that reading needs 46 clicks.\n */\n [granted] {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n margin: var(--spacing-medium, 8px) var(--spacing-large, 12px) 0;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: var(--md-sys-shape-corner-medium, 10px);\n background-color: var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));\n }\n\n [granted] > header {\n display: flex;\n align-items: baseline;\n gap: 8px;\n padding: 8px 12px;\n color: var(--md-sys-color-on-surface-variant);\n font-size: 0.8rem;\n }\n\n [granted] > header [count] {\n color: var(--md-sys-color-outline);\n }\n\n /*\n ⚠ 여기 display: flex 와 flex-wrap: wrap 이 있었다 — 칩으로 그리던 시절의 규칙이다.\n 마크업을 줄 목록으로 바꾼 뒤에도 남아서 **문장이 한 줄씩 서지 않고 칩처럼 옆으로\n 흘렀다.** 내 ul[lines] 는 display 를 선언하지 않으므로 맞설 것이 없었다.\n 마크업을 갈 때는 그 마크업만 쓰던 CSS 를 함께 지운다.\n\n 높이도 바뀐다. max-height: 22vh 는 격자 두 판이 늘 아래에 펼쳐져 있던 시절의 값이다.\n 지금은 **이 목록이 본문**이므로(§2) 남는 높이를 여기가 받고, 59줄은 여기서 흐른다.\n */\n [granted] ul {\n flex: 1;\n min-height: 0;\n overflow: auto;\n }\n\n [granted] [empty] {\n padding: 0 12px 10px;\n }\n\n [note] {\n margin: var(--spacing-large, 12px);\n padding: 10px 12px;\n border-radius: var(--md-sys-shape-corner-small, 6px);\n font-size: 0.85rem;\n line-height: 1.55;\n background-color: var(--md-sys-color-surface-variant);\n color: var(--md-sys-color-on-surface-variant);\n }\n\n [note][attention] {\n background-color: var(--md-sys-color-error-container);\n color: var(--md-sys-color-on-error-container);\n }\n\n /*\n ── 할 수 있는 일 (본문) ────────────────────────────────────────────────\n 읽는 것이 먼저이므로 글자를 본문 크기로 둔다. 격자의 0.85rem 보다 크다.\n */\n ul[lines] {\n list-style: none;\n margin: 0;\n padding: 0 12px;\n }\n\n ul[lines] li {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 9px 2px;\n border-bottom: 1px solid var(--md-sys-color-surface-variant);\n }\n\n ul[lines] li:last-child {\n border-bottom: none;\n }\n\n ul[lines] [line] {\n flex: 1;\n font-size: 0.95rem;\n line-height: 1.5;\n }\n\n /* 문장이 없어서 이름으로 지은 줄 — 문장과 구별되게 조용히 둔다. */\n ul[lines] [named] {\n color: var(--md-sys-color-on-surface-variant);\n }\n\n ul[lines] li[ghost] [line] {\n text-decoration: line-through;\n color: var(--md-sys-color-outline);\n }\n\n ul[lines] [tag] {\n margin-left: 8px;\n font-size: 0.78rem;\n text-decoration: none;\n color: var(--md-sys-color-outline);\n }\n\n /* 떼기 — 줄마다 있으므로 조용하고, 그 줄에 머물 때 드러난다. */\n button[drop] {\n flex: none;\n display: inline-flex;\n padding: 3px;\n color: var(--md-sys-color-outline);\n background-color: transparent;\n border: none;\n border-radius: var(--md-sys-shape-corner-full, 999px);\n cursor: pointer;\n }\n button[drop] md-icon {\n --md-icon-size: 18px;\n }\n ul[lines] li:hover button[drop] {\n color: var(--md-sys-color-error);\n background-color: var(--md-sys-color-error-container);\n }\n\n button[add] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n margin: 10px 12px 12px;\n padding: 8px 14px 8px 10px;\n font: var(--label-font, inherit);\n font-size: 0.85rem;\n color: var(--md-sys-color-on-secondary-container);\n background-color: var(--md-sys-color-secondary-container);\n border: none;\n border-radius: var(--md-sys-shape-corner-full, 999px);\n cursor: pointer;\n }\n button[add] md-icon {\n --md-icon-size: 18px;\n }\n\n button[close] {\n margin-left: auto;\n display: inline-flex;\n padding: 2px;\n color: var(--md-sys-color-on-surface-variant);\n background-color: transparent;\n border: none;\n cursor: pointer;\n }\n button[close] md-icon {\n --md-icon-size: 18px;\n }\n\n [empty] {\n padding: var(--spacing-large, 12px);\n color: var(--md-sys-color-on-surface-variant);\n font-size: 0.85rem;\n }\n\n /*\n The three readings of one role - quiet text buttons in the panel's header line, the chosen one underlined.\n They change what the panel shows, not what the role holds, so they are not boxed like an action.\n */\n [granted] > header nav {\n margin-left: auto;\n display: flex;\n gap: 2px;\n }\n\n [granted] > header nav button {\n padding: 2px 8px;\n border: none;\n border-bottom: 2px solid transparent;\n background: none;\n font: inherit;\n font-size: 0.8rem;\n color: var(--md-sys-color-on-surface-variant);\n cursor: pointer;\n }\n\n [granted] > header nav button[selected] {\n border-bottom-color: var(--md-sys-color-primary);\n color: var(--md-sys-color-on-surface);\n font-weight: 600;\n }\n\n [review] {\n flex: 1;\n min-height: 0;\n overflow: auto;\n padding: 0 12px 12px;\n }\n\n [review] h4 {\n display: flex;\n align-items: baseline;\n gap: 8px;\n margin: 14px 0 4px;\n font-size: 0.85rem;\n font-weight: 600;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n [review] h4 [count] {\n font-weight: 400;\n color: var(--md-sys-color-outline);\n }\n\n ul[tasks] {\n list-style: none;\n margin: 0;\n padding: 0;\n }\n\n ul[tasks] > li {\n padding: 9px 2px;\n border-bottom: 1px solid var(--md-sys-color-surface-variant);\n }\n\n ul[tasks] > li:last-child {\n border-bottom: none;\n }\n\n ul[tasks] [task-name] {\n font-size: 0.95rem;\n font-weight: 600;\n }\n\n ul[tasks] [task-says],\n ul[tasks] [task-missing] {\n margin-top: 2px;\n font-size: 0.85rem;\n line-height: 1.55;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n ul[tasks] [task-missing] {\n color: var(--md-sys-color-outline);\n }\n\n ul[tasks][quiet] > li {\n padding: 5px 2px;\n font-size: 0.88rem;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n [state] {\n margin-left: 8px;\n padding: 0 6px;\n border-radius: var(--md-sys-shape-corner-full, 999px);\n font-size: 0.75rem;\n font-weight: 400;\n background-color: var(--md-sys-color-surface-container-highest);\n color: var(--md-sys-color-on-surface-variant);\n }\n\n [state][all] {\n background-color: var(--md-sys-color-secondary-container);\n color: var(--md-sys-color-on-secondary-container);\n }\n\n [state][broken] {\n background-color: var(--md-sys-color-error-container);\n color: var(--md-sys-color-on-error-container);\n }\n\n ul[sentences] {\n list-style: none;\n margin: 0;\n padding: 0;\n }\n\n ul[sentences] li {\n padding: 5px 2px;\n font-size: 0.9rem;\n line-height: 1.5;\n }\n\n button[quiet] {\n padding: 4px 0;\n border: none;\n background: none;\n font: inherit;\n font-size: 0.85rem;\n color: var(--md-sys-color-primary);\n cursor: pointer;\n }\n\n button[quiet]:disabled {\n color: var(--md-sys-color-outline);\n cursor: default;\n }\n\n [review] select {\n margin-top: 10px;\n padding: 4px 6px;\n font: inherit;\n font-size: 0.88rem;\n color: var(--md-sys-color-on-surface);\n background-color: var(--md-sys-color-surface);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: var(--md-sys-shape-corner-small, 6px);\n }\n\n [review] [stale] {\n margin-top: 10px;\n font-size: 0.82rem;\n color: var(--md-sys-color-outline);\n }\n `\n ]\n\n /** 셸이 `role-privileges/<id>` 의 뒤를 `lifecycle.resourceId` 로 준다. */\n @property({ type: Object }) lifecycle: any\n\n /*\n * 이름을 `role` 로 두면 안 된다 — `HTMLElement` 가 ARIA 반영으로 `role` 을 이미 갖고 있어서\n * private 필드로 가리면 `CustomElementClass` 에 안 맞는다(TS1238·TS2415).\n */\n @state() private subject: any = {}\n @state() private modules: Module[] = []\n @state() private assigned: Set<string> = new Set()\n @state() private holders: any[] = []\n @state() private selected: string = ''\n\n @state() private keyword: string = ''\n @state() private mySubdomain: string = ''\n\n @state() private fetchState: 'loading' | 'failed' | 'loaded' = 'loading'\n /** What the server said when the read failed - the screen shows that sentence, not a guess. */\n @state() private loadError: unknown = null\n @state() private saving: boolean = false\n @state() private dirty: boolean = false\n\n @state() private view: View = 'privileges'\n /** The role laid against every task - computed by the server from the saved privileges (`Role.taskCoverage`). */\n @state() private coverage: Coverage = { tasks: [], unclassified: [] }\n @state() private showUnheldTasks: boolean = false\n /** null until asked for; [] when this person may not read templates (`roleTemplates` asks user:mutation). */\n @state() private templates: TemplateChoice[] | null = null\n @state() private gap: Gap | null = null\n\n /**\n * 부모 도메인의 역할은 여기서 못 고친다 — `updateRole` 의 범위가 `domain.id` 하나다.\n * 목록이 상속으로 보여 주므로 관리자가 여기까지 온다. 읽기 전용으로 두고 어디로 가야 하는지\n * 말한다. (프로필의 `domain` 에 `id` 가 없어서 `subdomain` 으로 견준다.)\n */\n private get inherited(): boolean {\n const subdomain = this.subject?.domain?.subdomain\n return Boolean(subdomain && this.mySubdomain && subdomain !== this.mySubdomain)\n }\n\n private get savable(): boolean {\n return this.fetchState === 'loaded' && !this.saving && !this.inherited\n }\n\n private get visibleModules(): Module[] {\n return filterPrivilegeModules(this.modules, this.keyword, moduleLabel)\n }\n\n /**\n * `role-privilege-picker` 는 이 페이지의 상태를 읽어 그린다. 둘은 서로 다른 shadow root 이므로\n * 여기 state 만 바꾸면 picker 는 다시 그릴 이유를 알지 못한다. 검색·모듈 선택은 그 누락 때문에\n * 눌러도 화면이 그대로였다.\n */\n private refreshPicker() {\n this.picker?.requestUpdate()\n }\n\n private selectModule(category: string) {\n if (this.selected === category) {\n return\n }\n\n this.selected = category\n this.refreshPicker()\n }\n\n /**\n * ox-input-search 의 `change` 는 입력을 끝내고 포커스를 옮길 때만 온다. 모듈을 찾는 칸은\n * 입력하는 즉시 좁혀져야 하므로 shadow 경계를 넘어오는 native input 이벤트도 함께 받는다.\n */\n private updateKeyword(event: Event) {\n const nativeInput = event.composedPath().find(node => node instanceof HTMLInputElement) as\n HTMLInputElement | undefined\n const keyword = nativeInput?.value ?? (event.target as any)?.value ?? ''\n\n this.keyword = keyword\n\n /* 검색 결과에 현재 선택 모듈이 없으면 첫 결과를 바로 연다. 오른쪽에 이전 모듈의 축이\n 남아 있으면 검색이 안 된 것처럼 보이기 때문이다. */\n this.selected = selectedVisibleModule(this.visibleModules, this.selected)\n\n this.refreshPicker()\n }\n\n /*\n * The page is named after the screen, not after the role it happens to be showing.\n *\n * It used to carry `this.subject.name`, and the reader then saw \"Administrator\" three times\n * on one screen: the tab, the page header, and the subject block in the body. Worse, the page\n * had no name of its own - nothing said what this screen is for.\n *\n * A record-valued title does have precedent here (appliance, app-binding, application in this\n * package, and board-ui's play list - 4 of the 6 object-form titles), but those pages are the\n * record's own detail. This one is one facet of a role, and the role is already named in the\n * body, where a description can sit beside it.\n */\n get context() {\n return {\n title: { icon: 'vpn_key', text: i18next.t('title.role privileges') },\n help: 'auth/roles',\n actions: this.inherited\n ? []\n : [\n {\n title: i18next.t('button.save'),\n disabled: !this.dirty || !this.savable,\n disabledTitle: this.saving ? '권한을 저장하고 있습니다.' : '권한을 바꾼 뒤 저장할 수 있습니다.',\n action: this.onSave.bind(this)\n }\n ]\n }\n }\n\n render() {\n /*\n * Loading, refused and failed are one part now (ADR-0060 decision 8). The note this screen drew\n * said \"failed, please try again\" whatever had happened: a privilege refusal - likely here, of\n * all screens - read as a breakage, and it offered no way to read it again.\n */\n return html`<ox-page-state\n .loading=${this.fetchState === 'loading'}\n .error=${this.loadError}\n @retry=${() => this.fetch()}\n >${this.renderRole()}</ox-page-state\n >`\n }\n\n private renderRole() {\n return html`\n ${\n this.inherited\n ? html`<div note attention>\n ${i18next.t('text.role belongs to x domain and is edited there', {\n x: this.subject?.domain?.name || this.subject?.domain?.subdomain\n })}\n </div>`\n : nothing\n }\n ${this.renderSubject()} ${this.renderHolders()} ${this.renderGranted()}\n `\n }\n\n /**\n * 어느 역할을 고치고 있나, 그리고 그것이 무엇을 위한 것인가.\n *\n * 머리말은 이름만 들고 있었고, **직무를 말하는 한 줄인 설명은 목록에만 있었다.** 링크로\n * 들어온 사람은 `Administrator` 와 `PLANT-MANAGER` 를 낱말 말고는 구별할 방법이 없다.\n *\n * 설명이 없는 역할은 없다고 말한다. 다음에 할 일은 그것을 쓰는 것이고, 자리는 목록 격자다.\n */\n private renderSubject() {\n if (this.fetchState !== 'loaded') {\n return nothing\n }\n\n return html`\n <div subject>\n <div name>${this.subject?.name || i18next.t('label.role')}</div>\n ${\n this.subject?.description\n ? html`<div says>${this.subject.description}</div>`\n : html`<div says missing>${i18next.t('text.no description for this role')}</div>`\n }\n </div>\n `\n }\n\n /**\n * **이 역할이 할 수 있는 일 — 이것이 본문이다.**\n *\n * ── 뒤집은 이유 (UX 레인 §2, 사용자 평가) ──────────────────────────────────\n * 격자는 「이 모듈에서 무엇이 켜졌나」에 답한다. 관리자가 묻는 것은 **「이 역할이 무엇을\n * 하나」**이고, 격자로 그것을 읽으려면 모듈 46개를 한 번씩 열어야 한다. 역할을 부여하는\n * 사람은 부여하기 **전에** 그 답이 필요하다.\n *\n * 그래서 켜진 것을 문장으로 늘어놓는 것이 본문이 되고, 격자는 **더할 때 쓰는 도구**로\n * 자리를 옮긴다(`renderAdd`). 문장이 줄마다 있으므로 배우는 일이 따로 없다.\n *\n * ── 켜진 것만 낸다 ─────────────────────────────────────────────────────────\n * 전체를 여기 또 그리면 같은 46 모듈이 화면에 두 번 선다. 전체는 「더하기」 판이 갖는다.\n *\n * ── 글이 없을 때 ───────────────────────────────────────────────────────────\n * 「더하기」 판에서는 문장이 없으면 **없다고 말해도 된다** — 체크박스 옆에 「읽기」라는\n * 라벨이 이미 있어서 무엇을 켜는지 알 수 있다.\n *\n * 여기서는 다르다. **이 줄이 곧 그 권한의 신분이다.** 없다고만 쓰면 무엇을 떼는지 모르는\n * 줄이 되므로, 그때는 `모듈 · 축` 으로 이름을 짓는다. 그것은 코드 낱말이 아니다 —\n * `privilege.category.*` 57개와 「읽기/쓰기」가 사람 말이다.\n */\n private renderGranted() {\n if (this.fetchState !== 'loaded') {\n return nothing\n }\n\n const granted = this.modules\n .flatMap(module => module.cells.map(cell => ({ category: module.category, cell })))\n .filter(entry => this.assigned.has(entry.cell.id))\n\n return html`\n <div granted>\n <header>\n ${i18next.t('field.granted privileges')}\n <span count>${granted.length}</span>\n <nav>\n ${this.viewButton('privileges', i18next.t('label.privileges view'))} ${this.viewButton('tasks', i18next.t('label.tasks view'))}\n ${this.viewButton('template', i18next.t('label.template view'))}\n </nav>\n </header>\n\n ${\n this.view === 'tasks'\n ? this.renderTasks()\n : this.view === 'template'\n ? this.renderTemplate()\n : html`${\n granted.length\n ? html`<ul lines>\n ${granted.map(({ category, cell }) => this.renderGrantedLine(category, cell))}\n </ul>`\n : html`<div empty>${i18next.t('text.nothing is on for this role yet')}</div>`\n }\n ${this.renderAdd()}`\n }\n </div>\n `\n }\n\n private viewButton(view: View, label: string) {\n return html`<button ?selected=${this.view === view} @click=${() => this.showView(view)}>${label}</button>`\n }\n\n private async showView(view: View) {\n this.view = view\n if (view === 'template' && this.templates === null) {\n await this.fetchTemplates()\n }\n }\n\n /**\n * **The role by task** (ADR-0139 decision 11) — which of each task's privileges this role holds, as the server\n * computes it from the saved privileges.\n *\n * It says what the role holds, nothing more: 「전부」 is not \"this person can do the work\" (another role, the\n * data's own scope or an approval line decide that too), and 「일부」 is not a fault — a privilege several tasks\n * share makes each of them 「일부」.\n *\n * A task can be added to the role or taken off it (the user, 2026-10-20). The role stores privileges, never tasks,\n * so either is a set of privileges, shown before it is saved. Taking a task off keeps what another task the role\n * holds whole still uses (`role-task-edit.ts`). Both save at once and read the coverage again, so they are not\n * offered over unsaved changes - the coverage is the saved role's.\n */\n private renderTasks() {\n const tasks = this.coverage.tasks\n const held = tasks.filter(task => task.status !== 'none').sort((a, b) => statusOrder(a.status) - statusOrder(b.status))\n const unheld = tasks.filter(task => task.status === 'none')\n\n return html`\n <div review>\n ${this.dirty ? html`<div stale>${i18next.t('text.counted from the saved privileges')}</div>` : nothing}\n ${\n held.length\n ? html`<ul tasks>\n ${held.map(task => this.renderTask(task))}\n </ul>`\n : html`<div empty>${i18next.t('text.this role holds no task')}</div>`\n }\n ${\n unheld.length\n ? html`<h4>\n ${i18next.t('label.tasks this role does not hold')}\n <span count>${unheld.length}</span>\n <button quiet @click=${() => (this.showUnheldTasks = !this.showUnheldTasks)}>\n ${this.showUnheldTasks ? i18next.t('button.fold') : i18next.t('button.unfold')}\n </button>\n </h4>\n ${\n this.showUnheldTasks\n ? html`<ul tasks quiet>\n ${unheld.map(task => html`<li>${taskName(task.taskId)} ${this.taskEditButton(task)}</li>`)}\n </ul>`\n : nothing\n }`\n : nothing\n }\n ${\n this.coverage.unclassified.length\n ? html`<h4>\n ${i18next.t('label.privileges no task names')}\n <span count>${countOf(this.coverage.unclassified)}</span>\n </h4>\n ${this.renderSentences(this.coverage.unclassified)}`\n : nothing\n }\n </div>\n `\n }\n\n /** 더하기 for a task the role lacks some or all of, 빼기 for one it holds some or all of. */\n private taskEditButton(task: TaskCoverage) {\n if (this.inherited || task.status === 'broken') return nothing\n const disabled = this.dirty || this.saving\n const why = this.dirty ? i18next.t('text.save before editing by task') : ''\n return html`\n ${\n task.status !== 'all'\n ? html`<button quiet ?disabled=${disabled} title=${why} @click=${() => this.editTask(task, 'add')}>${i18next.t('button.add task')}</button>`\n : nothing\n }\n ${\n task.status !== 'none'\n ? html`<button quiet ?disabled=${disabled} title=${why} @click=${() => this.editTask(task, 'remove')}>${i18next.t('button.remove task')}</button>`\n : nothing\n }\n `\n }\n\n /** Adds a task's missing privileges to the role, or takes its own off - after showing which - and saves. */\n private async editTask(task: TaskCoverage, mode: 'add' | 'remove') {\n if (this.dirty || this.saving || this.inherited) return\n\n const name = taskName(task.taskId)\n const { remove, kept } = mode === 'remove' ? pairsToRemove(task, this.coverage.tasks) : { remove: [], kept: [] }\n const pairs = mode === 'add' ? pairsToAdd(task) : remove\n const words = (list: string[]) => list.map(pair => this.sentenceOf(pair.split(':')[0], pair.split(':')[1])).join(', ')\n const keptLine = kept.length ? ` ${i18next.t('text.kept for other tasks', { list: words(kept) })}` : ''\n\n if (!pairs.length) {\n await OxPrompt.open({\n type: 'info',\n title: i18next.t('title.remove task'),\n text: i18next.t('text.nothing to remove for task', { task: name }) + keptLine,\n confirmButton: { text: i18next.t('button.confirm') }\n })\n return\n }\n\n const confirmed = await OxPrompt.open({\n type: 'info',\n title: i18next.t(mode === 'add' ? 'title.add task' : 'title.remove task'),\n text: i18next.t(mode === 'add' ? 'text.add task privileges' : 'text.remove task privileges', { task: name, count: pairs.length, list: words(pairs) }) + keptLine,\n confirmButton: { text: i18next.t(mode === 'add' ? 'button.add task' : 'button.remove task') },\n cancelButton: { text: i18next.t('button.cancel') }\n })\n if (!confirmed) return\n\n const idOf = new Map(this.modules.flatMap(module => module.cells.map(cell => [pairOf(module.category, cell.axis), cell.id] as [string, string])))\n const next = new Set(this.assigned)\n for (const pair of pairs) {\n const id = idOf.get(pair)\n if (id) mode === 'add' ? next.add(id) : next.delete(id)\n }\n\n this.assigned = next\n this.dirty = true\n await this.onSave()\n }\n\n private renderTask(task: TaskCoverage) {\n const held = countOf(task.held)\n const total = held + countOf(task.missing)\n const says = i18next.t(`task.${task.taskId}.description`, { defaultValue: '' })\n\n return html`\n <li>\n <div>\n <span task-name>${taskName(task.taskId)}</span>\n ${\n task.status === 'broken'\n ? html`<span state broken>${i18next.t('text.task names an undeclared privilege')}</span>`\n : task.status === 'all'\n ? html`<span state all>${i18next.t('text.task held whole')}</span>`\n : html`<span state>${i18next.t('text.task held in part', { held, total })}</span>`\n }\n ${this.taskEditButton(task)}\n </div>\n ${says ? html`<div task-says>${says}</div>` : nothing}\n ${\n task.status === 'some'\n ? html`<div task-missing>${i18next.t('text.not held x', { x: task.missing.flatMap(grant => grant.axes.map(axis => this.sentenceOf(grant.category, axis))).join(' · ') })}</div>`\n : nothing\n }\n </li>\n `\n }\n\n /**\n * **The role against its base template** (ADR-0139 decision 11; ADR-0075 decision 1, amended 2026-10-26).\n *\n * A role keeps one base template (`Role.templateId`), whatever the role is called — a role named in the site's own\n * words (경영지원, 인사급여) is given its base here, once, and the role list counts what it lacks from then on.\n * Picking saves the base at once and touches no privilege. The role is never renamed to make a pair: an approval line\n * holds the role. What the role holds beyond the template is shown as wider, not wrong — the site may have widened it\n * on purpose — and nothing takes it away. What it lacks can be added; adding is the same top-up the template button\n * on the role list runs (`createRoleFromTemplate` onto this role's name).\n */\n private renderTemplate() {\n if (this.templates === null) {\n return html`<div review><div empty>${i18next.t('text.loading')}</div></div>`\n }\n\n if (!this.templates.length) {\n return html`<div review><div empty>${i18next.t('text.no template to compare with')}</div></div>`\n }\n\n const baseId: string = this.subject?.templateId || ''\n const template = this.templates.find(one => one.id === baseId)\n const gap = this.gap\n\n return html`\n <div review>\n <select ?disabled=${this.inherited || this.saving} @change=${(event: Event) => this.setBaseTemplate((event.target as HTMLSelectElement).value)}>\n <option value=\"\" ?selected=${!baseId}>${i18next.t('text.no base template')}</option>\n ${this.templates.map(one => html`<option value=${one.id} ?selected=${one.id === baseId}>${one.name}</option>`)}\n ${baseId && !template ? html`<option value=${baseId} selected>${baseId}</option>` : nothing}\n </select>\n ${baseId && !template ? html`<div note attention>${i18next.t('text.base template not in this installation')}</div>` : nothing}\n ${this.dirty ? html`<div stale>${i18next.t('text.counted from the saved privileges')}</div>` : nothing}\n ${\n template?.definitionErrors.length\n ? html`<div note attention>${i18next.t('text.template cannot be applied because', { list: template.definitionErrors.join(' · ') })}</div>`\n : nothing\n }\n ${\n gap\n ? html`\n <h4>\n ${i18next.t('label.missing against the template')}\n <span count>${gap.missingCount}</span>\n ${\n gap.missingCount && !this.inherited\n ? html`<button quiet ?disabled=${this.dirty || this.saving || Boolean(template?.definitionErrors.length)} @click=${() => this.addMissing()}>\n ${i18next.t('button.add')}\n </button>`\n : nothing\n }\n </h4>\n ${gap.missingCount ? this.renderSentences(gap.missing) : nothing}\n <h4>\n ${i18next.t('label.wider than the template')}\n <span count>${gap.extraCount}</span>\n </h4>\n ${gap.extraCount ? this.renderSentences(gap.extra) : nothing}\n `\n : nothing\n }\n </div>\n `\n }\n\n private renderSentences(grants: Grant[]) {\n return html`<ul sentences>\n ${grants.flatMap(grant => grant.axes.map(axis => html`<li>${this.sentenceOf(grant.category, axis)}</li>`))}\n </ul>`\n }\n\n /** A privilege in words — the sentence the privileges view draws, or its module and axis when there is none. */\n private sentenceOf(category: string, axis: string): string {\n const cell = this.modules.find(module => module.category === category)?.cells.find(one => one.axis === axis)\n return (cell && axisText(category, cell)) || `${moduleLabel(category)} · ${axisLabel(axis)}`\n }\n\n private async fetchTemplates() {\n const response = await client.query({\n query: gql`\n query {\n roleTemplates {\n id\n name\n definitionErrors\n }\n }\n `,\n context: gqlContext()\n })\n\n /* Reading templates asks the same door creating a role does; without it there is nothing to compare with. */\n const templates: TemplateChoice[] = response.errors?.length ? [] : response.data?.roleTemplates || []\n this.templates = templates\n await this.readGap()\n }\n\n /** Saves the role's base template - `''` bases it on none. The privileges stay as they are. */\n private async setBaseTemplate(templateId: string) {\n if (this.inherited || this.saving) return\n\n const response = await client.mutate({\n mutation: gql`\n mutation ($id: String!, $patch: RolePatch!) {\n updateRole(id: $id, patch: $patch) {\n id\n templateId\n }\n }\n `,\n variables: { id: this.subject.id, patch: { templateId: templateId || null } },\n context: gqlContext()\n })\n\n if (response.errors?.length) {\n document.dispatchEvent(new CustomEvent('notify', { detail: { level: 'error', message: response.errors[0]?.message } }))\n this.requestUpdate()\n return\n }\n\n this.subject = { ...this.subject, templateId: response.data?.updateRole?.templateId || null }\n await this.readGap()\n }\n\n private async readGap() {\n this.gap = null\n const baseId = this.subject?.templateId\n if (!baseId) return\n\n const response = await client.query({\n query: gql`\n query ($filters: [Filter!]) {\n roles(filters: $filters) {\n items {\n id\n templateGap {\n templateId\n templateName\n missing { ${GRANT_FIELDS} }\n missingCount\n extra { ${GRANT_FIELDS} }\n extraCount\n }\n }\n }\n }\n `,\n variables: { filters: [{ name: 'id', operator: 'eq', value: this.subject.id }] },\n context: gqlContext()\n })\n\n if (response.errors?.length) {\n document.dispatchEvent(new CustomEvent('notify', { detail: { level: 'error', message: response.errors[0]?.message } }))\n return\n }\n\n if (this.subject?.templateId === baseId) this.gap = response.data?.roles?.items?.[0]?.templateGap || null\n }\n\n /** Adds what the role lacks against the picked template — after showing what it adds. */\n private async addMissing() {\n const gap = this.gap\n if (!gap?.missingCount || this.dirty || this.inherited) return\n\n const list = gap.missing.flatMap(grant => grant.axes.map(axis => this.sentenceOf(grant.category, axis))).join(', ')\n const confirmed = await OxPrompt.open({\n type: 'info',\n title: i18next.t('title.add template privileges'),\n text: i18next.t('text.add template privileges', { template: gap.templateName, count: gap.missingCount, list }),\n confirmButton: { text: i18next.t('button.add') },\n cancelButton: { text: i18next.t('button.cancel') }\n })\n if (!confirmed) return\n\n const response = await client.mutate({\n mutation: gql`\n mutation ($templateId: String!, $name: String) {\n createRoleFromTemplate(templateId: $templateId, name: $name) {\n id\n }\n }\n `,\n variables: { templateId: gap.templateId, name: this.subject.name },\n context: gqlContext()\n })\n\n if (response.errors?.length) {\n document.dispatchEvent(new CustomEvent('notify', { detail: { level: 'error', message: response.errors[0]?.message } }))\n return\n }\n\n document.dispatchEvent(new CustomEvent('notify', { detail: { message: i18next.t('text.data_updated_successfully') } }))\n await this.fetch()\n await this.readGap()\n }\n\n /**\n * 한 줄 — 문장과 [떼기].\n *\n * 유령(이 배포의 어떤 코드도 요구하지 않는 권한)은 취소선으로 보이고 **떼는 것만** 된다.\n * 이미 붙어 있으니 뗄 수 있어야 하고, 붙여도 아무 일이 없으니 다시 켤 이유가 없다.\n */\n private renderGrantedLine(category: string, cell: Cell) {\n const written = axisText(category, cell)\n\n return html`\n <li ?ghost=${cell.deprecated}>\n <div line>\n ${written || html`<span named>${moduleLabel(category)} · ${axisLabel(cell.axis)}</span>`}\n ${cell.deprecated ? html`<span tag>${i18next.t('text.privilege no longer declared')}</span>` : nothing}\n </div>\n\n ${\n this.inherited\n ? nothing\n : html`<button drop title=${i18next.t('button.drop')} @click=${() => this.onToggle(cell.id, false)}>\n <md-icon>close</md-icon>\n </button>`\n }\n </li>\n `\n }\n\n /**\n * 「+ 할 수 있는 일 더하기」 — 격자가 여기 들어간다.\n *\n * 열어 두지 않는 이유는 하나다 — **처음 읽는 것이 「이 역할이 무엇을 하나」여야 한다.**\n * 46 모듈이 늘 펼쳐져 있으면 그 답이 화면 아래로 밀린다.\n *\n * 상속 역할에는 내주지 않는다. 여기서 저장할 수 없으므로 켜 보게 하는 것이 헛수고다 —\n * 대신 위쪽 안내가 어느 도메인에서 고치는지 말한다.\n */\n private renderAdd() {\n if (this.inherited || this.fetchState !== 'loaded') {\n return nothing\n }\n\n if (!this.modules.length) {\n return html`<div note attention>${i18next.t('text.no privilege declared in this deployment')}</div>`\n }\n\n return html`\n <button add @click=${() => this.openAdd()}>\n <md-icon>add</md-icon>${i18next.t('button.add what it can do')}\n </button>\n `\n }\n\n /**\n * 고르는 판을 **넓은 팝업**으로 연다.\n *\n * 사용자 말씀이다 — 「'할 수 있는 일 더하기' 작동을 팝업으로 넓게 할 수 있었으면 어떤가?\n * 지금은 좁아보이고」. 페이지 안에 펼치면 본문(할 수 있는 일)이 아래로 밀리고, 남은 폭에서\n * 모듈 46개와 축 목록을 함께 봐야 한다. 고르는 동안은 **고르는 것만** 보는 것이 맞다.\n *\n * ```\n * [읽었음] ox-floating-overlay.ts:126-134 [size=large] width 100% · height 100%\n * ```\n *\n * `escapable` 을 건드리지 않는다 — 켠 것은 팝업이 아니라 **이 페이지의 `assigned`** 에 바로\n * 들어가므로 닫든 ESC 를 누르든 잃는 것이 없다. 물어볼 것이 없는 판에 물으면 그 물음이 값을\n * 잃는다. 저장은 여전히 툴바가 한다.\n *\n * ⚠ 부품을 **동적으로 들인다.** 그 부품은 스타일 한 벌을 이 클래스에서 가져가므로, 위에서\n * 정적으로 import 하면 순환이 된다. 여는 순간에 들이면 그 고리가 끊긴다.\n */\n private async openAdd() {\n await import('../../components/role-privilege-picker.js')\n\n const popup = openPopup(\n html`<role-privilege-picker .page=${this} @close=${() => popup.close()}></role-privilege-picker>`,\n {\n backdrop: true,\n size: 'large',\n title: i18next.t('button.add what it can do')\n }\n )\n }\n\n /**\n * 고르는 판의 몸체 — **팝업이 이것을 그린다.**\n *\n * 판을 부품 안으로 옮기지 않는다. 모듈 접기·축 줄·유령 규칙이 전부 이 페이지의 상태\n * (`modules`·`assigned`·`selected`·`keyword`)를 보기 때문이고, 옮기면 **상태가 두 벌**이 된다.\n * 두 벌이 되는 순간 어느 쪽이 저장 범위인지 흐려진다 — 옛 편집기가 `querySelectorAll` 로\n * 범위를 만들던 결함이 정확히 그 모양이었다.\n */\n renderPickerBody() {\n return html`\n <div body class=\"page-fill\">\n <div panel>\n <header>\n ${i18next.t('field.module')}\n <span count>${this.modules.length}</span>\n </header>\n <ox-input-search\n .placeholder=${i18next.t('text.filter by module name')}\n .value=${this.keyword}\n @input=${(event: Event) => this.updateKeyword(event)}\n @change=${(event: Event) => this.updateKeyword(event)}\n ></ox-input-search>\n <div scroll>${this.renderModules()}</div>\n </div>\n\n <div panel>\n <header>\n ${this.selected ? moduleLabel(this.selected) : i18next.t('field.privileges')}\n <span count>${i18next.t('text.x of y on', { x: this.assigned.size, y: this.cellCount() })}</span>\n </header>\n <div scroll>${this.renderAxes()}</div>\n </div>\n </div>\n <div picker-footer>\n <span>\n ${\n this.saving\n ? '권한을 저장하고 있습니다.'\n : this.dirty\n ? '변경 사항이 있습니다. 저장해야 적용됩니다.'\n : '권한을 바꾼 뒤 저장할 수 있습니다.'\n }\n </span>\n <button ?disabled=${!this.savable || !this.dirty} @click=${() => this.onSave()}>\n ${this.saving ? '저장 중…' : i18next.t('button.save')}\n </button>\n </div>\n `\n }\n\n /** 팝업이 열려 있는 동안 자기를 등록한다 — 토글이 그것을 다시 그리게 한다. */\n picker?: { requestUpdate: () => void }\n\n /**\n * 이 역할을 가진 사람.\n *\n * 권한을 켜기 **전에** 누가 영향을 받는지 보이는 자리다. 아무도 안 가진 역할을 고치는 것과\n * 40명이 가진 역할을 고치는 것은 다른 일인데, 옛 화면은 그 둘이 똑같이 보였다.\n */\n private renderHolders() {\n if (this.fetchState !== 'loaded') {\n return nothing\n }\n\n return this.holders.length\n ? html`<div note>\n ${i18next.t('text.x people hold this role', { x: this.holders.length })} —\n ${this.holders.map(h => h.name || h.email).join(', ')}\n </div>`\n : html`<div note>${i18next.t('text.no one holds this role yet')}</div>`\n }\n\n private renderModules() {\n if (this.fetchState === 'loading') {\n return html`<div empty>${i18next.t('text.loading')}</div>`\n }\n\n if (!this.modules.length) {\n return html`<div empty>${i18next.t('text.no privilege declared in this deployment')}</div>`\n }\n\n if (!this.visibleModules.length) {\n return html`<div empty>${i18next.t('text.nothing matches x', { x: this.keyword })}</div>`\n }\n\n return html`\n <ul modules>\n ${this.visibleModules.map(module => {\n const on = module.cells.filter(cell => this.assigned.has(cell.id)).length\n\n return html`\n <li ?selected=${module.category === this.selected} @click=${() => this.selectModule(module.category)}>\n <span>${moduleLabel(module.category)}</span>\n ${\n on\n ? html`<span on>${i18next.t('text.x on', { x: on })}</span>`\n : html`<span none>${i18next.t('text.none on')}</span>`\n }\n </li>\n `\n })}\n </ul>\n `\n }\n\n private renderAxes() {\n if (this.fetchState === 'loading') {\n return html`<div empty>${i18next.t('text.loading')}</div>`\n }\n\n const module = this.modules.find(m => m.category === this.selected)\n if (!module) {\n /* 아무것도 안 고른 상태 — 무엇을 해야 하는지 말한다. */\n return html`<div empty>${i18next.t('text.pick a module on the left')}</div>`\n }\n\n return html`\n <ul axes>\n ${module.cells.map(cell => {\n const on = this.assigned.has(cell.id)\n\n return html`\n <li\n ?ghost=${cell.deprecated}\n ?readonly=${this.inherited || (cell.deprecated && !on)}\n @click=${(event: MouseEvent) => this.toggleCellFromRow(event, cell, on)}\n >\n <input\n type=\"checkbox\"\n id=\"cell-${cell.id}\"\n .checked=${on}\n ?disabled=${this.inherited || (cell.deprecated && !on)}\n @change=${(e: Event) => this.onToggle(cell.id, (e.target as HTMLInputElement).checked)}\n />\n <label for=\"cell-${cell.id}\">${axisLabel(cell.axis)}</label>\n\n ${\n cell.deprecated\n ? html`<div says>${i18next.t('text.privilege no longer declared')}</div>`\n : axisSays(module.category, cell) ||\n html`<div says missing>${i18next.t('text.no description for this privilege')}</div>`\n }\n </li>\n `\n })}\n </ul>\n `\n }\n\n private cellCount(): number {\n return this.modules.reduce((sum, module) => sum + module.cells.length, 0)\n }\n\n async pageUpdated(changes: any, lifecycle: any) {\n if (this.active) {\n this.lifecycle = lifecycle\n await this.fetch()\n }\n }\n\n /**\n * 한 번의 질의로 **전체 권한과 이 역할이 가진 것**을 함께 읽는다(`rolePrivileges(roleId:)`).\n *\n * 역할 이름과 가진 사람은 `roles(filters: id)` 로 같은 문서에서 받는다 — 옛 편집기는 역할을\n * **이름**으로 다시 찾았고, 부모 도메인에 같은 이름이 있으면 남의 역할을 그렸다.\n */\n private async fetch() {\n const roleId = this.lifecycle?.resourceId\n if (!roleId) {\n this.fetchState = 'failed'\n this.loadError = new Error(i18next.t('text.no role to show'))\n return\n }\n\n this.fetchState = 'loading'\n this.loadError = null\n this.mySubdomain = (await getDomain())?.subdomain || ''\n\n const response = await client.query({\n query: gql`\n query ($roleId: String!, $filters: [Filter!]) {\n rolePrivileges(roleId: $roleId) {\n id\n name\n category\n description\n assigned\n deprecated\n }\n roles(filters: $filters) {\n items {\n id\n name\n description\n templateId\n domain {\n id\n name\n subdomain\n }\n users {\n id\n name\n email\n }\n taskCoverage {\n tasks {\n taskId\n module\n status\n held { ${GRANT_FIELDS} }\n missing { ${GRANT_FIELDS} }\n undeclared { ${GRANT_FIELDS} }\n }\n unclassified { ${GRANT_FIELDS} }\n }\n }\n }\n }\n `,\n variables: { roleId, filters: [{ name: 'id', operator: 'eq', value: roleId }] },\n context: gqlContext()\n })\n\n if (response.errors?.length) {\n /* 실패를 「권한이 하나도 없다」로 그리지 않는다 — 저장하면 실제로 그렇게 된다. */\n this.fetchState = 'failed'\n this.loadError = response.errors\n return\n }\n\n const role = response.data?.roles?.items?.[0]\n\n /*\n * A role id nobody has resolves with no error and no role, so the screen drew an empty\n * privilege list - a role that is gone looked exactly like a role that has been granted\n * nothing, and saving from there would have written that emptiness back.\n */\n if (!role?.id) {\n this.fetchState = 'failed'\n this.loadError = new Error(i18next.t('text.this role no longer exists'))\n return\n }\n\n this.subject = role\n this.holders = this.subject.users || []\n this.coverage = role.taskCoverage || { tasks: [], unclassified: [] }\n this.build(response.data?.rolePrivileges || [])\n this.fetchState = 'loaded'\n\n /*\n * The shell reads `context` once, when the page becomes active - before this fetch\n * resolves. Without this call the header keeps the fallback, so the screen showed\n * \"권한\" and never said which role was being edited.\n */\n this.updateContext()\n }\n\n /** 평평한 목록을 모듈로 접는다. 축 순서는 아는 것 먼저, 나머지는 이름 순. */\n private build(privileges: any[]) {\n const byCategory = new Map<string, Module>()\n const assigned = new Set<string>()\n\n for (const privilege of privileges) {\n const { id, name, category, description, deprecated } = privilege\n if (!id || !name || !category) {\n continue\n }\n\n if (privilege.assigned) {\n assigned.add(id)\n }\n\n const module = byCategory.get(category) || { category, cells: [] as Cell[] }\n module.cells.push({ id, axis: name, deprecated: Boolean(deprecated), description })\n byCategory.set(category, module)\n }\n\n for (const module of byCategory.values()) {\n module.cells.sort((a, b) => axisOrder(a.axis) - axisOrder(b.axis) || a.axis.localeCompare(b.axis))\n }\n\n this.modules = [...byCategory.values()].sort((a, b) =>\n moduleLabel(a.category).localeCompare(moduleLabel(b.category))\n )\n this.assigned = assigned\n this.dirty = false\n\n /* 고른 것이 없으면 첫 모듈을 연다 — 빈 오른쪽 판을 처음 화면으로 두지 않는다. */\n if (!this.modules.find(m => m.category === this.selected)) {\n this.selected = this.modules[0]?.category || ''\n }\n }\n\n private onToggle(id: string, checked: boolean) {\n if (this.assigned.has(id) === checked) {\n return\n }\n\n const next = new Set(this.assigned)\n checked ? next.add(id) : next.delete(id)\n this.assigned = next\n this.dirty = true\n this.updateContext()\n\n /* 팝업은 자기 상태를 안 들고 이 페이지를 본다 — 열려 있으면 다시 그리라고 알린다. */\n this.refreshPicker()\n }\n\n /** 라벨·설명을 포함한 행 전체를 누를 수 있게 한다. checkbox와 키보드 Space는 native change가 맡는다. */\n private toggleCellFromRow(event: MouseEvent, cell: Cell, on: boolean) {\n if (this.inherited || (cell.deprecated && !on) || event.target instanceof HTMLInputElement) {\n return\n }\n\n /* label의 기본 동작이 checkbox change를 한 번 더 보내지 않게 막는다. */\n event.preventDefault()\n this.onToggle(cell.id, !on)\n }\n\n get addressDirty(): boolean { return this.dirty }\n\n get deactivation(): boolean | Parameters<typeof OxPrompt.open>[0] {\n if (!this.dirty) return true\n return {\n title: '저장하지 않은 변경 사항',\n text: '수정된 권한이 있습니다. 저장하지 않고 이 페이지를 나가시겠습니까?',\n confirmButton: { text: '저장하지 않고 나가기' },\n cancelButton: { text: i18next.t('button.cancel') }\n }\n }\n\n /**\n * 저장.\n *\n * **범위는 `modules` 전체다.** 왼쪽에서 다른 모듈을 고르면 앞 모듈의 체크박스는 DOM 에\n * 없는데, 그것을 훑어 보내던 것이 옛 편집기가 권한을 조용히 떼던 자리다.\n *\n * `privileges` 만 보낸다 — 이름과 설명은 목록 격자가 소유한다.\n */\n private async onSave() {\n if (!this.savable) {\n return\n }\n\n this.saving = true\n this.updateContext()\n\n try {\n const response = await client.mutate({\n mutation: gql`\n mutation ($id: String!, $patch: RolePatch!) {\n updateRole(id: $id, patch: $patch) {\n id\n }\n }\n `,\n variables: {\n id: this.subject.id,\n patch: { privileges: [...this.assigned].map(id => ({ id })) }\n },\n context: gqlContext()\n })\n\n if (response.errors?.length) {\n document.dispatchEvent(\n new CustomEvent('notify', { detail: { level: 'error', message: response.errors[0]?.message } })\n )\n return\n }\n\n document.dispatchEvent(\n new CustomEvent('notify', { detail: { message: i18next.t('text.data_updated_successfully') } })\n )\n\n /* 유령을 뗐으면 목록에서 사라져야 하고, 켠 것이 요약에 반영돼야 한다. */\n await this.fetch()\n this.dirty = false\n } finally {\n this.saving = false\n this.updateContext()\n }\n }\n}\n\n/**\n * 모듈 이름을 사람 말로.\n *\n * `privilege.category.<category>` 키가 있으면 그것을 쓰고, 없으면 **코드 낱말을 그대로** 낸다 —\n * 지어내지 않는다. 지금 46개 중 하나(`system`)만 채워져 있고, UX 레인이 나머지를 쓰고 있다.\n */\nfunction moduleLabel(category: string): string {\n const key = `privilege.category.${category}`\n return i18next.t(key, { defaultValue: category })\n}\n\n/**\n * 「켜면 무엇을 할 수 있게 되나」 — **이 화면의 이유다.**\n *\n * 사용자 평가가 「화면으로부터 배울 수 있는 것이 없다」였고, 그 원인은 화면이 감춘 것이 아니라\n * 시스템에 그 문장이 없던 것이었다. 부팅 동기화가 `{ category, name }` 둘만 저장해서\n * (`auth-base/server/index.ts:34-36`) 713개 선언이 전부 빈 설명이었다.\n *\n * 그 글을 UX 레인이 `privilege.allows.<category>.<axis>` 로 썼다(57 모듈 · 104 문장, ko·en).\n * 번역은 디스크에서 서비스되므로 **배포를 기다리지 않고 붙는다.**\n *\n * ── 보는 순서 ───────────────────────────────────────────────────────────────\n * ```\n * 1 서버 칸 채워지면 그것이 이긴다 — 배포마다 다를 수 있는 사실은 서버가 안다\n * 2 우리가 쓴 글 privilege.allows.<category>.<axis>\n * 3 없다고 말한다 둘 다 없으면. 지어내지 않는다\n * ```\n *\n * ⚠ **`defaultValue: ''` 가 없으면 3번으로 못 떨어진다** — i18next 는 키가 없을 때 **키 이름을\n * 그대로** 돌려주므로 화면에 `privilege.allows.board.query` 가 찍힌다. 빈 문자열을 기본값으로\n * 줘서 「없음」이 거짓으로 판정되게 한다.\n */\nfunction axisText(category: string, cell: Cell): string {\n if (cell.description) {\n return cell.description\n }\n\n return i18next.t(`privilege.allows.${category}.${cell.axis}`, { defaultValue: '' })\n}\n\nfunction axisSays(category: string, cell: Cell) {\n const written = axisText(category, cell)\n return written ? html`<div says>${written}</div>` : null\n}\n\nfunction axisOrder(axis: string): number {\n const index = KNOWN_AXES.indexOf(axis)\n return index < 0 ? KNOWN_AXES.length : index\n}\n\n/** A task's name in the words of the module that declares it (`task.<id>.name`); the id when there are none. */\nfunction taskName(id: string): string {\n return i18next.t(`task.${id}.name`, { defaultValue: id })\n}\n\nfunction statusOrder(status: TaskCoverage['status']): number {\n return ['broken', 'all', 'some', 'none'].indexOf(status)\n}\n\nfunction countOf(grants: Grant[]): number {\n return grants.reduce((count, grant) => count + grant.axes.length, 0)\n}\n"]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { readFileSync } from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
/**
|
|
4
|
-
* The role privilege page reads a role by task and against
|
|
5
|
-
*
|
|
4
|
+
* The role privilege page reads a role by task and against its base template (ADR-0139 decision 11; ADR-0075
|
|
5
|
+
* decision 1, amended 2026-10-26). What is computed - all, some, none, wider, missing - is tested in auth-base (`tests/role-task.test.ts`,
|
|
6
6
|
* `tests/role-template-gap-db.test.ts`); this holds what the screen does with it. The page needs a DOM, so the test
|
|
7
7
|
* reads its source with comments stripped.
|
|
8
8
|
*/
|
|
@@ -12,6 +12,8 @@ const between = (from, to) => code.slice(code.indexOf(from), code.indexOf(to));
|
|
|
12
12
|
const tasks = between('private renderTasks(', 'private renderTask(');
|
|
13
13
|
const template = between('private renderTemplate(', 'private renderSentences(');
|
|
14
14
|
const pick = between('private async fetchTemplates(', 'private async addMissing(');
|
|
15
|
+
const setBase = between('private async setBaseTemplate(', 'private async readGap(');
|
|
16
|
+
const readGap = between('private async readGap(', 'private async addMissing(');
|
|
15
17
|
const add = between('private async addMissing(', '\n private cellCount(');
|
|
16
18
|
const editButton = between('private taskEditButton(', 'private async editTask(');
|
|
17
19
|
const edit = between('private async editTask(', 'private renderTask(');
|
|
@@ -37,9 +39,21 @@ describe('the role by task and against a template', () => {
|
|
|
37
39
|
expect(tasks).toMatch(/this\.showUnheldTasks\s*\?/);
|
|
38
40
|
expect(tasks).toMatch(/this\.coverage\.unclassified/);
|
|
39
41
|
});
|
|
40
|
-
it(
|
|
41
|
-
expect(
|
|
42
|
-
expect(
|
|
42
|
+
it('★ lays the role against its stored base, never a template found by the role\'s name', () => {
|
|
43
|
+
expect(code).toMatch(/templateId\s+domain\s*\{/);
|
|
44
|
+
expect(code).not.toMatch(/one\.name === this\.subject/);
|
|
45
|
+
expect(readGap).toMatch(/const baseId = this\.subject\?\.templateId\s*if \(!baseId\) return/);
|
|
46
|
+
expect(readGap).toMatch(/templateGap \{/);
|
|
47
|
+
});
|
|
48
|
+
it('★ saves the picked base at once through updateRole, touching no privilege, and not on an inherited role', () => {
|
|
49
|
+
expect(template).toMatch(/@change=\$\{\(event: Event\) => this\.setBaseTemplate\(/);
|
|
50
|
+
expect(template).toMatch(/<select \?disabled=\$\{this\.inherited \|\| this\.saving\}/);
|
|
51
|
+
expect(setBase).toMatch(/if \(this\.inherited \|\| this\.saving\) return/);
|
|
52
|
+
expect(setBase).toMatch(/patch: \{ templateId: templateId \|\| null \}/);
|
|
53
|
+
expect(setBase).not.toMatch(/privileges/);
|
|
54
|
+
});
|
|
55
|
+
it('★ says so when the base is a template this installation does not declare', () => {
|
|
56
|
+
expect(template).toMatch(/baseId && !template \? html`<div note attention>\$\{i18next\.t\('text\.base template not in this installation'\)\}/);
|
|
43
57
|
});
|
|
44
58
|
it('★ shows what the role holds beyond the template as wider, with nothing to take it away', () => {
|
|
45
59
|
expect(template).toMatch(/label\.wider than the template/);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role-task-review.test.js","sourceRoot":"","sources":["../../../client/pages/role/role-task-review.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAA;AACjC,OAAO,IAAI,MAAM,MAAM,CAAA;AAEvB;;;;;GAKG;AAEH,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,wBAAwB,CAAC,EAAE,OAAO,CAAC,CAAA;AACpF,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAA;AAEjF,MAAM,OAAO,GAAG,CAAC,IAAY,EAAE,EAAU,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAA;AAC9F,MAAM,KAAK,GAAG,OAAO,CAAC,sBAAsB,EAAE,qBAAqB,CAAC,CAAA;AACpE,MAAM,QAAQ,GAAG,OAAO,CAAC,yBAAyB,EAAE,0BAA0B,CAAC,CAAA;AAC/E,MAAM,IAAI,GAAG,OAAO,CAAC,+BAA+B,EAAE,2BAA2B,CAAC,CAAA;AAClF,MAAM,GAAG,GAAG,OAAO,CAAC,2BAA2B,EAAE,wBAAwB,CAAC,CAAA;AAC1E,MAAM,UAAU,GAAG,OAAO,CAAC,yBAAyB,EAAE,yBAAyB,CAAC,CAAA;AAChF,MAAM,IAAI,GAAG,OAAO,CAAC,yBAAyB,EAAE,qBAAqB,CAAC,CAAA;AAEtE,QAAQ,CAAC,yCAAyC,EAAE,GAAG,EAAE;IACvD,EAAE,CAAC,0FAA0F,EAAE,GAAG,EAAE;QAClG,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,yCAAyC,CAAC,CAAA;QAC/D,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,6BAA6B,CAAC,CAAA;IACzD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qHAAqH,EAAE,GAAG,EAAE;QAC7H,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,8CAA8C,CAAC,CAAA;QACpE,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAA;QAC1C,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAA;IACrD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,4GAA4G,EAAE,GAAG,EAAE;QACpH,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,kEAAkE,CAAC,CAAA;QACxF,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC,CAAA;QACxF,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,0BAA0B,CAAC,CAAA;QAChD,MAAM,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,sEAAsE,CAAC,CAAA;QAClG,MAAM,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,gDAAgD,CAAC,CAAA;IAC9E,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,kFAAkF,EAAE,GAAG,EAAE;QAC1F,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,yBAAyB,CAAC,CAAA;QAChD,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,4BAA4B,CAAC,CAAA;QACnD,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,8BAA8B,CAAC,CAAA;IACvD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"role-task-review.test.js","sourceRoot":"","sources":["../../../client/pages/role/role-task-review.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync } from 'fs'\nimport path from 'path'\n\n/**\n * The role privilege page reads a role by task and against its base template (ADR-0139 decision 11; ADR-0075\n * decision 1, amended 2026-10-26). What is computed - all, some, none, wider, missing - is tested in auth-base (`tests/role-task.test.ts`,\n * `tests/role-template-gap-db.test.ts`); this holds what the screen does with it. The page needs a DOM, so the test\n * reads its source with comments stripped.\n */\n\nconst source = readFileSync(path.join(__dirname, 'role-privilege-page.ts'), 'utf-8')\nconst code = source.replace(/\\/\\*[\\s\\S]*?\\*\\//g, '').replace(/^\\s*\\/\\/.*$/gm, '')\n\nconst between = (from: string, to: string) => code.slice(code.indexOf(from), code.indexOf(to))\nconst tasks = between('private renderTasks(', 'private renderTask(')\nconst template = between('private renderTemplate(', 'private renderSentences(')\nconst pick = between('private async fetchTemplates(', 'private async addMissing(')\nconst setBase = between('private async setBaseTemplate(', 'private async readGap(')\nconst readGap = between('private async readGap(', 'private async addMissing(')\nconst add = between('private async addMissing(', '\\n private cellCount(')\nconst editButton = between('private taskEditButton(', 'private async editTask(')\nconst edit = between('private async editTask(', 'private renderTask(')\n\ndescribe('the role by task and against a template', () => {\n it('★ asks the server for the coverage with the role - the screen keeps no copy of the tasks', () => {\n expect(code).toMatch(/taskCoverage\\s*\\{\\s*tasks\\s*\\{\\s*taskId/)\n expect(code).not.toMatch(/registerRoleTask|ROLE_TASKS/)\n })\n\n it('★ edits by task through the rule - what is added is what the role lacks, what is taken off spares other whole tasks', () => {\n expect(edit).toMatch(/pairsToRemove\\(task, this\\.coverage\\.tasks\\)/)\n expect(edit).toMatch(/pairsToAdd\\(task\\)/)\n expect(tasks).not.toMatch(/createRoleFromTemplate/)\n })\n\n it('★ shows the privileges before saving, and offers no task edit over unsaved changes or on an inherited role', () => {\n expect(edit).toMatch(/if \\(this\\.dirty \\|\\| this\\.saving \\|\\| this\\.inherited\\) return/)\n expect(edit.indexOf('OxPrompt.open(')).toBeLessThan(edit.indexOf('await this.onSave()'))\n expect(edit).toMatch(/if \\(!confirmed\\) return/)\n expect(editButton).toMatch(/if \\(this\\.inherited \\|\\| task\\.status === 'broken'\\) return nothing/)\n expect(editButton).toMatch(/const disabled = this\\.dirty \\|\\| this\\.saving/)\n })\n\n it('★ keeps the tasks the role holds nothing of folded, and what no task names apart', () => {\n expect(tasks).toMatch(/task\\.status === 'none'/)\n expect(tasks).toMatch(/this\\.showUnheldTasks\\s*\\?/)\n expect(tasks).toMatch(/this\\.coverage\\.unclassified/)\n })\n\n it('★ lays the role against its stored base, never a template found by the role\\'s name', () => {\n expect(code).toMatch(/templateId\\s+domain\\s*\\{/)\n expect(code).not.toMatch(/one\\.name === this\\.subject/)\n expect(readGap).toMatch(/const baseId = this\\.subject\\?\\.templateId\\s*if \\(!baseId\\) return/)\n expect(readGap).toMatch(/templateGap \\{/)\n })\n\n it('★ saves the picked base at once through updateRole, touching no privilege, and not on an inherited role', () => {\n expect(template).toMatch(/@change=\\$\\{\\(event: Event\\) => this\\.setBaseTemplate\\(/)\n expect(template).toMatch(/<select \\?disabled=\\$\\{this\\.inherited \\|\\| this\\.saving\\}/)\n expect(setBase).toMatch(/if \\(this\\.inherited \\|\\| this\\.saving\\) return/)\n expect(setBase).toMatch(/patch: \\{ templateId: templateId \\|\\| null \\}/)\n expect(setBase).not.toMatch(/privileges/)\n })\n\n it('★ says so when the base is a template this installation does not declare', () => {\n expect(template).toMatch(/baseId && !template \\? html`<div note attention>\\$\\{i18next\\.t\\('text\\.base template not in this installation'\\)\\}/)\n })\n\n it('★ shows what the role holds beyond the template as wider, with nothing to take it away', () => {\n expect(template).toMatch(/label\\.wider than the template/)\n expect(template).toMatch(/gap\\.extra\\b/)\n expect(template.slice(template.indexOf('label.wider than the template'))).not.toMatch(/<button/)\n })\n\n it('★ adds what is missing onto this role by its name, after showing it, and not over unsaved changes', () => {\n expect(add).toMatch(/OxPrompt\\.open\\(/)\n expect(add).toMatch(/if \\(!gap\\?\\.missingCount \\|\\| this\\.dirty \\|\\| this\\.inherited\\) return/)\n expect(add).toMatch(/createRoleFromTemplate\\(templateId: \\$templateId, name: \\$name\\)/)\n expect(add).toMatch(/variables: \\{ templateId: gap\\.templateId, name: this\\.subject\\.name \\}/)\n expect(template).toMatch(/\\?disabled=\\$\\{this\\.dirty \\|\\| this\\.saving \\|\\| Boolean\\(template\\?\\.definitionErrors\\.length\\)\\}/)\n })\n\n it('names a task in the words of the module that declares it, the id when there are none', () => {\n expect(code).toMatch(/i18next\\.t\\(`task\\.\\$\\{id\\}\\.name`, \\{ defaultValue: id \\}\\)/)\n })\n})\n"]}
|