@things-factory/auth-ui 10.1.6 → 10.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-client/components/role-privilege-picker-state.d.ts +11 -0
- package/dist-client/components/role-privilege-picker-state.js +15 -0
- package/dist-client/components/role-privilege-picker-state.js.map +1 -0
- package/dist-client/components/role-privilege-picker-state.test.js +20 -0
- package/dist-client/components/role-privilege-picker-state.test.js.map +1 -0
- package/dist-client/components/role-privilege-picker.d.ts +40 -0
- package/dist-client/components/role-privilege-picker.js +77 -0
- package/dist-client/components/role-privilege-picker.js.map +1 -0
- package/dist-client/components/role-template-picker.d.ts +83 -0
- package/dist-client/components/role-template-picker.js +487 -0
- package/dist-client/components/role-template-picker.js.map +1 -0
- package/dist-client/pages/role/role-list-page.d.ts +104 -13
- package/dist-client/pages/role/role-list-page.js +396 -72
- package/dist-client/pages/role/role-list-page.js.map +1 -1
- package/dist-client/pages/role/role-privilege-page.d.ts +113 -37
- package/dist-client/pages/role/role-privilege-page.js +426 -139
- package/dist-client/pages/role/role-privilege-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/translations/en.json +27 -1
- package/translations/ja.json +14 -1
- package/translations/ko.json +26 -0
- package/translations/ms.json +15 -2
- package/translations/zh.json +14 -1
- package/dist-client/components/create-role.d.ts +0 -1
- package/dist-client/components/create-role.js +0 -117
- package/dist-client/components/create-role.js.map +0 -1
- package/dist-client/components/credential-manager.d.ts +0 -11
- package/dist-client/components/credential-manager.js +0 -64
- package/dist-client/components/credential-manager.js.map +0 -1
- package/dist-client/components/role-detail.d.ts +0 -144
- package/dist-client/components/role-detail.js +0 -698
- package/dist-client/components/role-detail.js.map +0 -1
- package/dist-client/components/role-edit-form.js +0 -95
- package/dist-client/components/role-edit-form.js.map +0 -1
- package/dist-client/components/role-privilege-editor.d.ts +0 -1
- package/dist-client/components/role-privilege-editor.js +0 -368
- package/dist-client/components/role-privilege-editor.js.map +0 -1
- package/dist-client/pages/role/role-management.d.ts +0 -4
- package/dist-client/pages/role/role-management.js +0 -137
- package/dist-client/pages/role/role-management.js.map +0 -1
- package/dist-client/pages/user/user-management.d.ts +0 -35
- package/dist-client/pages/user/user-management.js +0 -223
- package/dist-client/pages/user/user-management.js.map +0 -1
- /package/dist-client/components/{role-edit-form.d.ts → role-privilege-picker-state.test.d.ts} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"role-detail.js","sourceRoot":"","sources":["../../client/components/role-detail.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@material/web/button/elevated-button.js'\nimport '@material/web/button/outlined-button.js'\nimport '@material/web/textfield/filled-text-field.js'\nimport '@material/web/checkbox/checkbox.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport type { CSSResultGroup } from 'lit'\n\nimport { client, gqlContext } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { OxPrompt } from '@operato/popup/ox-prompt.js'\nimport { ButtonContainerStyles, ScrollbarStyles } from '@operato/styles'\nimport { getDomain, hasPrivilege } from '@things-factory/auth-base/dist-client'\n\n/** 아는 축은 사람 말로 쓰고, 그 순서로 세운다. 모르는 축은 자기 이름으로 뒤에 붙는다. */\nconst KNOWN_AXES = ['query', 'mutation', 'execute']\nconst AXIS_LABEL: Record<string, string> = {\n query: 'label.read',\n mutation: 'label.write',\n execute: 'label.execute'\n}\n\ntype Cell = { id: string; assigned: boolean; deprecated: boolean; description?: string }\ntype Row = { category: string; cells: Record<string, Cell> }\n\n/**\n * 한 역할이 무엇을 열 수 있나 — **모듈 × 읽기·쓰기** 격자로 고친다.\n *\n * ── 옛 화면이 무엇을 잘못했나 (`components/role-privilege-editor.ts`) ────────\n * 세 개가 함께 있었고 셋 다 조용했다.\n *\n * **① A 의 이름이 B 에 저장된다.** 이름 입력이 `roleChanged` 를 매 글자마다 페이지로 올리고,\n * 페이지가 그것을 `updateRoleObj` 한 칸에 담아 **모든** 권한 편집기에 내려 줬다. 저장은\n * `{ ...updateRoleObj, privileges }` 였다. A 를 열어 이름을 고치고 B 를 열어 저장하면 B 의\n * 이름이 A 가 된다. 여기서는 이름·설명이 **이 컴포넌트의 상태**다 — 페이지가 들지 않는다.\n *\n * **② 이름으로 남을 찾는다.** 권한을 읽을 때 `role(name:)` 를 썼는데, 그 질의의 범위는\n * 「내 도메인 + 부모」다. 부모에 같은 이름의 역할이 있으면 **다른 역할의 권한을 그렸고**,\n * 저장은 손에 든 id 로 갔다. 여기서는 `rolePrivileges(roleId:)` 하나로 읽는다.\n *\n * **③ 저장 범위를 DOM 에서 만든다.** `querySelectorAll('input:checked')` 로 보냈다. 접히거나\n * 안 그려진 것은 「끈 것」이 되어 **조용히 떨어졌다.** 여기서는 서버가 준 전체 목록이 범위이고,\n * 검색으로 걸러도 안 보이는 칸은 그대로 유지된다.\n *\n * ── 왜 격자인가 ─────────────────────────────────────────────────────────────\n * 옛 화면은 84줄을 한 줄씩 늘어놓고 `description` 을 라벨로 썼다. 「board 를 편집할 수 있나」를\n * 알려면 그 줄을 눈으로 찾아야 했다. 모듈이 행이고 읽기·쓰기가 열이면 **한 모듈에서 무엇이\n * 열려 있나가 한 줄로 보인다.**\n *\n * 열은 **데이터가 정한다.** `execute` 는 아직 서버에 없으므로 열이 나오지 않고, 선언이 서면\n * 화면을 고치지 않고 열이 생긴다 — 없는 것을 있는 것처럼 그리지 않는다.\n *\n * ── 빈 칸과 유령 ────────────────────────────────────────────────────────────\n * 칸이 아예 없는 것(`—`)은 「끈 것」이 아니라 **그 모듈에 그런 권한이 선언되지 않았다**는 뜻이다.\n * 유령(이 배포의 어떤 코드도 요구하지 않는 권한)은 취소선으로 보이고, **켜는 것만 막는다** —\n * 이미 켜져 있는 것은 떼야 하니까.\n */\n@customElement('role-detail')\nexport class RoleDetail extends localize(i18next)(LitElement) {\n static styles: CSSResultGroup = [\n ButtonContainerStyles,\n /* The grid scrolls, so it needs the app's 5px bar. Measured 15px here before this line. */\n ScrollbarStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-medium, 8px);\n padding: var(--spacing-large, 12px);\n min-width: min(560px, 92vw);\n max-height: 78vh;\n background-color: var(--md-sys-color-surface);\n color: var(--md-sys-color-on-surface);\n }\n\n md-filled-text-field {\n width: 100%;\n }\n\n [section] {\n display: flex;\n align-items: baseline;\n gap: 8px;\n margin-top: var(--spacing-medium, 8px);\n color: var(--md-sys-color-on-surface-variant);\n font-size: 0.8rem;\n }\n\n [section] [count] {\n color: var(--md-sys-color-outline);\n }\n\n /* 격자만 흐르게 둔다 — 이름과 단추는 언제나 제자리에 있어야 한다. */\n /*\n * flex: 1 alone gave this box 69px on a 458px-tall popup whose cap is 644px: a flex\n * column that shrinks to its content leaves nothing for the child that asked to grow,\n * so 32 module rows of 31px were read through a 2.2-row window. The floor is what makes\n * the table legible; flex: 1 still lets it take the rest when the popup is taller.\n */\n [scroll] {\n flex: 1;\n min-height: min(320px, 40vh);\n overflow: auto;\n }\n\n table {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.85rem;\n }\n\n thead th {\n position: sticky;\n top: 0;\n z-index: 1;\n padding: 6px 8px;\n background-color: var(--md-sys-color-surface);\n color: var(--md-sys-color-on-surface-variant);\n font-weight: 500;\n text-align: center;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n\n thead th:first-child {\n text-align: left;\n }\n\n tbody td {\n padding: 4px 8px;\n border-bottom: 1px solid var(--md-sys-color-surface-variant);\n text-align: center;\n }\n\n tbody td:first-child {\n text-align: left;\n white-space: nowrap;\n }\n\n /* 칸이 없는 것은 「끈 것」이 아니다 — 조용한 색으로 그렇게 말한다. */\n [absent] {\n color: var(--md-sys-color-outline);\n }\n\n [ghost] {\n text-decoration: line-through;\n color: var(--md-sys-color-outline);\n }\n\n [note] {\n padding: 8px 10px;\n border-radius: var(--md-sys-shape-corner-small, 6px);\n font-size: 0.82rem;\n line-height: 1.5;\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 ul[holders] {\n display: flex;\n flex-wrap: wrap;\n gap: 4px 8px;\n list-style: none;\n margin: 0;\n padding: 0;\n font-size: 0.82rem;\n color: var(--md-sys-color-on-surface-variant);\n }\n\n [invalid] {\n color: var(--md-sys-color-error);\n font-size: 0.8rem;\n min-height: 1.1rem;\n }\n\n /* 되돌릴 수 없는 조치 — 선으로 갈라 두고 색은 글자에만 쓴다. */\n [danger-zone] {\n margin-top: var(--spacing-small, 4px);\n border-top: 1px solid var(--md-sys-color-outline-variant);\n padding-top: var(--spacing-small, 4px);\n }\n\n [danger-zone] md-outlined-button {\n --md-outlined-button-label-text-color: var(--md-sys-color-error);\n --md-outlined-button-outline-color: var(--md-sys-color-outline-variant);\n --md-outlined-button-hover-label-text-color: var(--md-sys-color-error);\n --md-outlined-button-hover-state-layer-color: var(--md-sys-color-error);\n --md-outlined-button-pressed-label-text-color: var(--md-sys-color-error);\n }\n `\n ]\n\n /** 목록이 골라 준 역할. `id` 가 있어야 한다. 없으면 새로 만드는 자리다. */\n @property({ type: Object }) role: any\n\n @state() private name: string = ''\n @state() private description: string = ''\n\n /** 서버가 준 전체 권한. **이것이 저장 범위다** — 화면이 무엇을 그렸는지와 무관하다. */\n @state() private rows: Row[] = []\n @state() private axes: string[] = []\n @state() private assigned: Set<string> = new Set()\n @state() private holders: any[] = []\n\n /*\n * 지금 어느 도메인에 있나 — **subdomain 으로 견준다.**\n *\n * 프로필이 실어 주는 `domain` 에는 `id` 가 없다(`auth-private-process-router.ts` — name·\n * subdomain·type·theme 만 담는다). 그래서 역할 쪽에서도 `subdomain` 을 함께 읽어 그것으로\n * 견준다. 두 도메인이 같은 subdomain 을 가질 수 없으므로 id 와 같은 답을 준다.\n */\n @state() private mySubdomain: string = ''\n\n /** 지우기만 선언이 다르다 — 소유권 우회가 없다. 그 선언을 그대로 되묻는다. */\n @state() private mayDelete: boolean = false\n\n @state() private keyword: string = ''\n @state() private loading: boolean = true\n @state() private failed: boolean = false\n @state() private working: boolean = false\n @state() private message: string = ''\n\n private get creating(): boolean {\n return !this.role?.id\n }\n\n /**\n * **부모 도메인의 역할은 여기서 못 고친다.**\n *\n * 목록은 상속으로 부모 역할까지 보여 준다 — 실제로 부여할 수 있는 범위가 그것이기 때문이다.\n * 그런데 `updateRole`·`deleteRole` 은 `domain.id` 하나로 좁혀 찾으므로 부모 것은 못 찾는다.\n * 그래서 여기서는 **읽기 전용으로 두고 어디로 가야 하는지 말한다** — 눌리는 단추를 두고 서버가\n * 거절하면 그것은 안내가 아니라 오류다.\n */\n private get inherited(): boolean {\n const subdomain = this.role?.domain?.subdomain\n return Boolean(subdomain && this.mySubdomain && subdomain !== this.mySubdomain)\n }\n\n /** 저장할 수 있나 — 단추와 처리기가 같은 답을 본다. */\n private get savable(): boolean {\n return !this.loading && !this.failed && !this.working && !this.inherited && Boolean(this.name.trim())\n }\n\n /**\n * 지울 수 있나.\n *\n * 서버가 **가진 사람이 있으면 거절한다**(`deleteRole` — 「cannot delete role because it is\n * currently in use」). 그러니 사람 목록이 비어 있을 때만 내준다. 그리고 이 하나만 선언이 다르다 —\n * `@privilege(category:\"user\", privilege:\"mutation\")` 로 **소유권 우회가 없다.** 만들기·고치기는\n * 소유자에게 열려 있는데 지우기만 권한을 요구한다.\n */\n private get deletable(): boolean {\n return (\n !this.creating && !this.inherited && !this.loading && !this.working && this.mayDelete && this.holders.length === 0\n )\n }\n\n /** 보여 줄 행. **저장 범위가 아니다.** */\n private get visible(): Row[] {\n const keyword = this.keyword.trim().toLowerCase()\n return keyword ? this.rows.filter(row => row.category.toLowerCase().includes(keyword)) : this.rows\n }\n\n render() {\n return html`\n <md-filled-text-field\n type=\"text\"\n label=${i18next.t('field.name')}\n .value=${this.name}\n ?readonly=${this.inherited}\n @input=${(e: Event) => (this.name = (e.target as HTMLInputElement).value)}\n ></md-filled-text-field>\n\n <md-filled-text-field\n type=\"text\"\n label=${i18next.t('field.description')}\n .value=${this.description}\n ?readonly=${this.inherited}\n @input=${(e: Event) => (this.description = (e.target as HTMLInputElement).value)}\n ></md-filled-text-field>\n\n <div invalid>${this.message}</div>\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.role?.domain?.name || this.role?.domain?.subdomain\n })}\n </div>`\n : nothing}\n\n <div section>\n ${i18next.t('field.privileges')}\n <span count>${i18next.t('text.x of y on', { x: this.assigned.size, y: this.cellCount() })}</span>\n </div>\n\n ${this.renderPrivileges()} ${this.renderHolders()}\n\n <div class=\"button-container\">\n <md-elevated-button ?disabled=${!this.savable} @click=${() => this.onSave()}>\n ${i18next.t('button.save')}\n </md-elevated-button>\n <md-elevated-button @click=${() => this.dispatchEvent(new CustomEvent('close'))}>\n ${i18next.t('button.cancel')}\n </md-elevated-button>\n </div>\n\n ${this.renderDangerZone()}\n `\n }\n\n /**\n * 역할 지우기.\n *\n * **왜 안 되는지를 말한다.** 서버는 가진 사람이 있으면 거절하는데(`deleteRole`), 옛 화면은\n * 언제나 단추를 보여 주고 눌린 뒤에 401 을 받았다. 여기서는 사람이 있으면 단추 대신 그 사실을\n * 적는다 — 관리자가 다음에 할 일(사람에게서 역할을 떼는 것)이 그 문장에서 나온다.\n */\n private renderDangerZone() {\n if (this.creating || this.inherited || this.loading) {\n return nothing\n }\n\n return html`\n <div danger-zone>\n ${this.holders.length\n ? html`<div note>${i18next.t('text.remove the role from x people first', { x: this.holders.length })}</div>`\n : html`<div class=\"button-container\">\n <md-outlined-button ?disabled=${!this.deletable} @click=${() => this.onDelete()}>\n ${i18next.t('button.delete role')}\n </md-outlined-button>\n </div>`}\n </div>\n `\n }\n\n private renderPrivileges() {\n if (this.loading) {\n return html`<div note>${i18next.t('text.loading')}</div>`\n }\n\n if (this.failed) {\n return html`<div note attention>${i18next.t('text.failed to fetch')}</div>`\n }\n\n if (!this.rows.length) {\n /*\n * 권한 마스터는 부팅 때 `@privilege` 선언으로 채워진다. 비어 있다는 것은 이 배포가 아직\n * 아무것도 선언하지 않았다는 뜻이므로, 역할에 붙일 것이 없다.\n */\n return html`<div note attention>${i18next.t('text.no privilege declared in this deployment')}</div>`\n }\n\n return html`\n <md-filled-text-field\n type=\"text\"\n label=${i18next.t('field.module')}\n placeholder=${i18next.t('text.filter by module name')}\n .value=${this.keyword}\n @input=${(e: Event) => (this.keyword = (e.target as HTMLInputElement).value)}\n >\n <md-icon slot=\"leading-icon\">search</md-icon>\n </md-filled-text-field>\n\n <div scroll>\n <table>\n <thead>\n <tr>\n <th>${i18next.t('field.module')}</th>\n ${this.axes.map(axis => html`<th>${AXIS_LABEL[axis] ? i18next.t(AXIS_LABEL[axis]) : axis}</th>`)}\n </tr>\n </thead>\n <tbody>\n ${this.visible.map(\n row => html`\n <tr>\n <td>${row.category}</td>\n ${this.axes.map(axis => this.renderCell(row, axis))}\n </tr>\n `\n )}\n </tbody>\n </table>\n </div>\n\n ${this.visible.length === 0\n ? html`<div note>${i18next.t('text.nothing matches x', { x: this.keyword })}</div>`\n : nothing}\n `\n }\n\n /**\n * 한 칸.\n *\n * ⚠ **설명이 없다.** `description` 은 권한 행의 칸인데 **아무도 채우지 않는다** — 부팅 동기화가\n * `{ category, name }` 둘만 저장하고(`auth-base/server/index.ts:34-36`), `createPrivilege` 는\n * 그 칸을 받지만 그것을 부르는 화면이 없다. 그래서 713개 선언이 전부 빈 설명이다.\n *\n * 없을 때 `${'$'}{category} ${'$'}{axis}` 를 보여 주던 것을 걷어냈다 — 그건 **코드 낱말을 한 번 더**\n * 보여 주는 것이고, 관리자가 「배울 것이 없다」고 한 자리가 정확히 그것이다. 지금은 없다고\n * 말한다. 지어내지 않는다.\n *\n * 글이 어디서 오는지는 UX 레인이 정하고 있다(`privilege.category.*` 키가 en·ko 에 이미 하나\n * 있고 나머지 45개가 비어 있다). 그 키가 오면 이 자리는 tooltip 이 아니라 **본문**으로 옮긴다.\n */\n private renderCell(row: Row, axis: string) {\n const cell = row.cells[axis]\n\n if (!cell) {\n /* 없는 것과 끈 것을 눈으로 갈라 놓는다. */\n return html`<td absent title=${i18next.t('text.not declared for this module')}>—</td>`\n }\n\n const on = this.assigned.has(cell.id)\n\n return html`\n <td>\n <md-checkbox\n .checked=${on}\n ?disabled=${this.inherited || (cell.deprecated && !on)}\n aria-label=${`${row.category} ${axis}`}\n title=${cell.deprecated\n ? i18next.t('text.privilege no longer declared')\n : cell.description || i18next.t('text.no description for this privilege')}\n @change=${(e: Event) => this.onToggle(cell.id, (e.target as HTMLInputElement).checked)}\n ></md-checkbox>\n ${cell.deprecated ? html`<span ghost>·</span>` : nothing}\n </td>\n `\n }\n\n /**\n * 이 역할을 가진 사람.\n *\n * 「권한을 어디서 찾나」의 반대 방향이다 — 권한을 고르기 전에 **이걸 바꾸면 누가 영향을\n * 받는지** 보이는 것이 사람 목록이다. 새로 만드는 자리에는 아직 없다.\n */\n private renderHolders() {\n if (this.creating || this.loading) {\n return nothing\n }\n\n return html`\n <div section>\n ${i18next.t('label.user')} <span count>${this.holders.length}</span>\n </div>\n ${this.holders.length\n ? html`<ul holders>\n ${this.holders.map(holder => html`<li>${holder.name || holder.email}</li>`)}\n </ul>`\n : html`<div note>${i18next.t('text.no one holds this role yet')}</div>`}\n `\n }\n\n private cellCount(): number {\n return this.rows.reduce((sum, row) => sum + Object.keys(row.cells).length, 0)\n }\n\n connectedCallback() {\n super.connectedCallback()\n this.checkGates()\n }\n\n /** 서버 선언을 그대로 되묻고, 지금 도메인을 프로필에서 읽는다. */\n private async checkGates() {\n this.mayDelete = Boolean(await hasPrivilege({ category: 'user', privilege: 'mutation' }))\n this.mySubdomain = (await getDomain())?.subdomain || ''\n }\n\n updated(changes: Map<string, unknown>) {\n if (changes.has('role')) {\n this.name = this.role?.name || ''\n this.description = this.role?.description || ''\n this.keyword = ''\n this.message = ''\n this.fetch()\n }\n }\n\n /**\n * 한 번의 질의로 **전체 목록과 이 역할이 가진 것**을 함께 읽는다(`rolePrivileges(roleId:)`).\n *\n * 새로 만드는 자리는 역할이 없으니 `privileges` 로 목록만 읽는다 — 그때 켜진 것은 없다.\n */\n private async fetch() {\n this.loading = true\n this.failed = false\n this.holders = []\n\n const response = this.creating\n ? await client.query({\n query: gql`\n query {\n privileges {\n items {\n id\n name\n category\n description\n deprecated\n }\n }\n }\n `,\n context: gqlContext()\n })\n : 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 users {\n id\n name\n email\n }\n }\n }\n }\n `,\n variables: {\n roleId: this.role.id,\n filters: [{ name: 'id', operator: 'eq', value: this.role.id }]\n },\n context: gqlContext()\n })\n\n if (response.errors?.length) {\n /* 실패를 「권한이 하나도 없다」로 그리지 않는다 — 저장하면 실제로 그렇게 된다. */\n this.failed = true\n this.loading = false\n return\n }\n\n const privileges = this.creating\n ? response.data?.privileges?.items || []\n : response.data?.rolePrivileges || []\n\n this.build(privileges)\n this.holders = response.data?.roles?.items?.[0]?.users || []\n this.loading = false\n }\n\n /** 평평한 목록을 격자로 세운다. 열은 데이터에 있는 축만. */\n private build(privileges: any[]) {\n const byCategory = new Map<string, Row>()\n const axes = new Set<string>()\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 axes.add(name)\n if (privilege.assigned) {\n assigned.add(id)\n }\n\n const row = byCategory.get(category) || { category, cells: {} }\n row.cells[name] = { id, assigned: Boolean(privilege.assigned), deprecated: Boolean(deprecated), description }\n byCategory.set(category, row)\n }\n\n const known = KNOWN_AXES.filter(axis => axes.has(axis))\n const rest = [...axes].filter(axis => !KNOWN_AXES.includes(axis)).sort()\n\n this.axes = [...known, ...rest]\n this.rows = [...byCategory.values()].sort((a, b) => a.category.localeCompare(b.category))\n this.assigned = assigned\n }\n\n private onToggle(id: string, checked: boolean) {\n const next = new Set(this.assigned)\n checked ? next.add(id) : next.delete(id)\n this.assigned = next\n }\n\n /**\n * 저장.\n *\n * **범위는 `rows` 다.** 검색으로 걸러 놓고 저장해도 안 보이던 칸은 켜진 채로 간다 — 옛 화면이\n * DOM 을 훑어 조용히 떼던 자리다.\n */\n private async onSave() {\n if (!this.savable) {\n return\n }\n\n this.working = true\n this.message = ''\n\n try {\n const privileges = [...this.assigned].map(id => ({ id }))\n const response = this.creating\n ? await client.mutate({\n mutation: gql`\n mutation ($role: NewRole!) {\n createRole(role: $role) {\n id\n }\n }\n `,\n variables: {\n role: { name: this.name.trim(), description: this.description.trim(), privileges }\n },\n context: gqlContext()\n })\n : 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.role.id,\n patch: { name: this.name.trim(), description: this.description.trim(), privileges }\n },\n context: gqlContext()\n })\n\n if (response.errors?.length) {\n this.message = response.errors[0]?.message || ''\n return\n }\n\n this.dispatchEvent(new CustomEvent('role-updated', { bubbles: true, composed: true }))\n } finally {\n this.working = false\n }\n }\n\n /**\n * 역할을 지운다.\n *\n * **누가 잃는지 먼저 말한다.** 옛 화면은 「역할을 삭제하시겠습니까」만 물었다 — 그 역할을 가진\n * 사람이 몇인지는 같은 화면에 있었는데도 확인 문구에 없었다.\n */\n private async onDelete() {\n if (!this.deletable) {\n return\n }\n\n const confirmed = await OxPrompt.open({\n type: 'warning',\n title: i18next.t('button.delete role'),\n text: i18next.t('text.are_you_sure_to_delete_x', { x: i18next.t('label.role') }),\n confirmButton: { text: i18next.t('button.delete') },\n cancelButton: { text: i18next.t('button.cancel') }\n })\n\n if (!confirmed) {\n return\n }\n\n this.working = true\n\n try {\n const response = await client.mutate({\n mutation: gql`\n mutation ($id: String!) {\n deleteRole(id: $id)\n }\n `,\n variables: { id: this.role.id },\n context: gqlContext()\n })\n\n if (response.errors?.length) {\n this.message = response.errors[0]?.message || ''\n return\n }\n\n this.dispatchEvent(new CustomEvent('role-updated', { bubbles: true, composed: true }))\n } finally {\n this.working = false\n }\n }\n}\n"]}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { __decorate, __metadata } from "tslib";
|
|
2
|
-
import { css, html, LitElement } from 'lit';
|
|
3
|
-
import { customElement, property, query } from 'lit/decorators.js';
|
|
4
|
-
import { i18next, localize } from '@operato/i18n';
|
|
5
|
-
let RoleEditForm = class RoleEditForm extends localize(i18next)(LitElement) {
|
|
6
|
-
static { this.styles = css `
|
|
7
|
-
[field-2column] {
|
|
8
|
-
border-radius: var(--border-radius);
|
|
9
|
-
display: grid;
|
|
10
|
-
grid-template-columns: 1fr 1fr;
|
|
11
|
-
gap: 5px 15px;
|
|
12
|
-
clear: both;
|
|
13
|
-
max-width: var(--input-container-max-width);
|
|
14
|
-
}
|
|
15
|
-
[field] {
|
|
16
|
-
display: flex;
|
|
17
|
-
flex-direction: column;
|
|
18
|
-
padding-bottom: var(--spacing-medium);
|
|
19
|
-
}
|
|
20
|
-
input {
|
|
21
|
-
background-color: var(--input-field-background, var(--md-sys-color-surface-container-highest));
|
|
22
|
-
color: var(--input-field-color, var(--md-sys-color-on-surface));
|
|
23
|
-
border: var(--border-dim-color);
|
|
24
|
-
border-radius: var(--border-radius);
|
|
25
|
-
margin: var(--input-margin);
|
|
26
|
-
padding: var(--input-padding);
|
|
27
|
-
min-width: 250px;
|
|
28
|
-
font: var(--input-font);
|
|
29
|
-
}
|
|
30
|
-
label {
|
|
31
|
-
display: flex;
|
|
32
|
-
flex-direction: column;
|
|
33
|
-
font: var(--label-font);
|
|
34
|
-
color: var(--md-sys-color-on-primary);
|
|
35
|
-
text-transform: var(--label-text-transform);
|
|
36
|
-
}
|
|
37
|
-
@media screen and (max-width: 480px) {
|
|
38
|
-
[field] {
|
|
39
|
-
grid-column: span 2;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
`; }
|
|
43
|
-
render() {
|
|
44
|
-
const role = this.role || { name: '', description: '' };
|
|
45
|
-
return html `
|
|
46
|
-
<form field-2column @input="${this.dispatchRoleChanged.bind(this)}">
|
|
47
|
-
<div field>
|
|
48
|
-
<label
|
|
49
|
-
>role name
|
|
50
|
-
<input name="name" value="${role.name}" @click="${e => e.stopPropagation()}" />
|
|
51
|
-
</label>
|
|
52
|
-
</div>
|
|
53
|
-
|
|
54
|
-
<div field>
|
|
55
|
-
<label
|
|
56
|
-
>role description
|
|
57
|
-
<input name="description" .value="${role.description}" @click="${e => e.stopPropagation()}" />
|
|
58
|
-
</label>
|
|
59
|
-
</div>
|
|
60
|
-
</form>
|
|
61
|
-
`;
|
|
62
|
-
}
|
|
63
|
-
getRole() {
|
|
64
|
-
const name = this.nameInput.value;
|
|
65
|
-
const description = this.descriptionInput.value;
|
|
66
|
-
if (!name) {
|
|
67
|
-
return this.showToast(i18next.t('error.value is empty', { value: i18next.t('field.name') }));
|
|
68
|
-
}
|
|
69
|
-
return { name, description };
|
|
70
|
-
}
|
|
71
|
-
dispatchRoleChanged() {
|
|
72
|
-
this.dispatchEvent(new CustomEvent('roleChanged', {
|
|
73
|
-
detail: this.getRole()
|
|
74
|
-
}));
|
|
75
|
-
}
|
|
76
|
-
showToast(message) {
|
|
77
|
-
document.dispatchEvent(new CustomEvent('notify', { detail: { message, option: { timer: 1000 } } }));
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
__decorate([
|
|
81
|
-
property({ type: Object }),
|
|
82
|
-
__metadata("design:type", Object)
|
|
83
|
-
], RoleEditForm.prototype, "role", void 0);
|
|
84
|
-
__decorate([
|
|
85
|
-
query('input[name=name]'),
|
|
86
|
-
__metadata("design:type", HTMLInputElement)
|
|
87
|
-
], RoleEditForm.prototype, "nameInput", void 0);
|
|
88
|
-
__decorate([
|
|
89
|
-
query('input[name=description]'),
|
|
90
|
-
__metadata("design:type", HTMLInputElement)
|
|
91
|
-
], RoleEditForm.prototype, "descriptionInput", void 0);
|
|
92
|
-
RoleEditForm = __decorate([
|
|
93
|
-
customElement('role-edit-form')
|
|
94
|
-
], RoleEditForm);
|
|
95
|
-
//# sourceMappingURL=role-edit-form.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"role-edit-form.js","sourceRoot":"","sources":["../../client/components/role-edit-form.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAElE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAGjD,IAAM,YAAY,GAAlB,MAAM,YAAa,SAAQ,QAAQ,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC;aAC/C,WAAM,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoClB,AApCY,CAoCZ;IAGD,MAAM;QACJ,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,CAAA;QAEvD,OAAO,IAAI,CAAA;oCACqB,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC;;;;wCAI/B,IAAI,CAAC,IAAI,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,eAAe,EAAE;;;;;;;gDAOtC,IAAI,CAAC,WAAW,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,eAAe,EAAE;;;;KAIhG,CAAA;IACH,CAAC;IAKD,OAAO;QACL,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAA;QACjC,MAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAA;QAE/C,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,sBAAsB,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAA;QAC9F,CAAC;QAED,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,CAAA;IAC9B,CAAC;IAED,mBAAmB;QACjB,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,aAAa,EAAE;YAC7B,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE;SACvB,CAAC,CACH,CAAA;IACH,CAAC;IAED,SAAS,CAAC,OAAO;QACf,QAAQ,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,CAAA;IACrG,CAAC;;AAhD2B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;0CAAU;AAwBV;IAA1B,KAAK,CAAC,kBAAkB,CAAC;8BAAa,gBAAgB;+CAAA;AACrB;IAAjC,KAAK,CAAC,yBAAyB,CAAC;8BAAoB,gBAAgB;sDAAA;AA/DjE,YAAY;IADjB,aAAa,CAAC,gBAAgB,CAAC;GAC1B,YAAY,CAuFjB","sourcesContent":["import { css, html, LitElement } from 'lit'\nimport { customElement, property, query } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\n\n@customElement('role-edit-form')\nclass RoleEditForm extends localize(i18next)(LitElement) {\n static styles = css`\n [field-2column] {\n border-radius: var(--border-radius);\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 5px 15px;\n clear: both;\n max-width: var(--input-container-max-width);\n }\n [field] {\n display: flex;\n flex-direction: column;\n padding-bottom: var(--spacing-medium);\n }\n input {\n background-color: var(--input-field-background, var(--md-sys-color-surface-container-highest));\n color: var(--input-field-color, var(--md-sys-color-on-surface));\n border: var(--border-dim-color);\n border-radius: var(--border-radius);\n margin: var(--input-margin);\n padding: var(--input-padding);\n min-width: 250px;\n font: var(--input-font);\n }\n label {\n display: flex;\n flex-direction: column;\n font: var(--label-font);\n color: var(--md-sys-color-on-primary);\n text-transform: var(--label-text-transform);\n }\n @media screen and (max-width: 480px) {\n [field] {\n grid-column: span 2;\n }\n }\n `\n @property({ type: Object }) role: any\n\n render() {\n const role = this.role || { name: '', description: '' }\n\n return html`\n <form field-2column @input=\"${this.dispatchRoleChanged.bind(this)}\">\n <div field>\n <label\n >role name\n <input name=\"name\" value=\"${role.name}\" @click=\"${e => e.stopPropagation()}\" />\n </label>\n </div>\n\n <div field>\n <label\n >role description\n <input name=\"description\" .value=\"${role.description}\" @click=\"${e => e.stopPropagation()}\" />\n </label>\n </div>\n </form>\n `\n }\n\n @query('input[name=name]') nameInput!: HTMLInputElement\n @query('input[name=description]') descriptionInput!: HTMLInputElement\n\n getRole() {\n const name = this.nameInput.value\n const description = this.descriptionInput.value\n\n if (!name) {\n return this.showToast(i18next.t('error.value is empty', { value: i18next.t('field.name') }))\n }\n\n return { name, description }\n }\n\n dispatchRoleChanged() {\n this.dispatchEvent(\n new CustomEvent('roleChanged', {\n detail: this.getRole()\n })\n )\n }\n\n showToast(message) {\n document.dispatchEvent(new CustomEvent('notify', { detail: { message, option: { timer: 1000 } } }))\n }\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,368 +0,0 @@
|
|
|
1
|
-
import { __decorate, __metadata } from "tslib";
|
|
2
|
-
import gql from 'graphql-tag';
|
|
3
|
-
import { css, html, LitElement } from 'lit';
|
|
4
|
-
import { customElement, property, state } from 'lit/decorators.js';
|
|
5
|
-
import { client, gqlContext } from '@operato/graphql';
|
|
6
|
-
import { i18next, localize } from '@operato/i18n';
|
|
7
|
-
import { OxPrompt } from '@operato/popup/ox-prompt.js';
|
|
8
|
-
import { ButtonContainerStyles } from '@operato/styles';
|
|
9
|
-
let RolePrivilegeEditor = class RolePrivilegeEditor extends localize(i18next)(LitElement) {
|
|
10
|
-
constructor() {
|
|
11
|
-
super(...arguments);
|
|
12
|
-
this.allPrivileges = [];
|
|
13
|
-
this.privileges = [];
|
|
14
|
-
this.users = [];
|
|
15
|
-
}
|
|
16
|
-
static { this.styles = [
|
|
17
|
-
ButtonContainerStyles,
|
|
18
|
-
css `
|
|
19
|
-
:host {
|
|
20
|
-
display: flex;
|
|
21
|
-
flex-direction: column;
|
|
22
|
-
|
|
23
|
-
border: 1px solid var(--md-sys-color-primary);
|
|
24
|
-
font: normal 15px var(--theme-font);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
div {
|
|
28
|
-
margin: var(--spacing-medium);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
div[users] {
|
|
32
|
-
margin: 0;
|
|
33
|
-
background-color: var(--md-sys-color-surface-variant);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
div[titler] {
|
|
37
|
-
color: var(--md-sys-color-secondary);
|
|
38
|
-
font: bold 1.2em var(--theme-font);
|
|
39
|
-
text-transform: capitalize;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
ul {
|
|
43
|
-
flex: 1;
|
|
44
|
-
color: var(--md-sys-color-secondary);
|
|
45
|
-
background-color: var(--md-sys-color-surface-variant);
|
|
46
|
-
overflow: auto;
|
|
47
|
-
display: grid;
|
|
48
|
-
grid-template-columns: 1fr 1fr;
|
|
49
|
-
margin-block-start: 0;
|
|
50
|
-
margin-block-end: 0;
|
|
51
|
-
padding: var(--spacing-medium);
|
|
52
|
-
list-style: none;
|
|
53
|
-
border: 1px dashed rgba(0, 0, 0, 0.1);
|
|
54
|
-
border-width: 1px 0;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
.category-group {
|
|
58
|
-
display: contents;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
.category-title {
|
|
62
|
-
font-weight: bold;
|
|
63
|
-
font-size: 0.9em;
|
|
64
|
-
color: var(--md-sys-color-primary);
|
|
65
|
-
text-transform: uppercase;
|
|
66
|
-
opacity: 0.7;
|
|
67
|
-
align-self: stretch;
|
|
68
|
-
position: sticky;
|
|
69
|
-
top: 0;
|
|
70
|
-
display: flex;
|
|
71
|
-
align-items: center;
|
|
72
|
-
justify-content: flex-end;
|
|
73
|
-
text-align: right;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
.category-privileges {
|
|
77
|
-
display: contents;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
@media screen and (min-width: 640px) {
|
|
81
|
-
ul {
|
|
82
|
-
grid-template-columns: minmax(200px, auto) 1fr 1fr;
|
|
83
|
-
gap: 0;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
.category-title {
|
|
87
|
-
grid-column: 1;
|
|
88
|
-
padding-right: var(--spacing-medium);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
li {
|
|
92
|
-
grid-column: auto;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
input[type='checkbox'] {
|
|
97
|
-
display: inline;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
li {
|
|
101
|
-
padding: var(--spacing-small);
|
|
102
|
-
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/* 코드가 더 이상 요구하지 않는 권한 — 부여해도 아무 일이 없으므로 눈에 구별되어야 한다.
|
|
106
|
-
감추지는 않는다. 이미 역할에 붙어 있을 수 있고, 그것을 보고 떼는 것이 관리자의 일이다. */
|
|
107
|
-
li[deprecated] label {
|
|
108
|
-
opacity: 0.55;
|
|
109
|
-
text-decoration: line-through;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
li[deprecated] small {
|
|
113
|
-
display: block;
|
|
114
|
-
margin-left: 1.6em;
|
|
115
|
-
color: var(--md-sys-color-error);
|
|
116
|
-
font-size: 0.8em;
|
|
117
|
-
text-decoration: none;
|
|
118
|
-
opacity: 0.9;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
#checkAll,
|
|
122
|
-
[for='checkAll'] {
|
|
123
|
-
margin-bottom: var(--spacing-medium);
|
|
124
|
-
padding-bottom: var(--spacing-small);
|
|
125
|
-
font-weight: bold;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
md-elevated-button {
|
|
129
|
-
margin: 5px;
|
|
130
|
-
background-color: var(--md-sys-color-surface-variant);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
md-outlined-button {
|
|
134
|
-
background-color: var(--md-sys-color-surface-variant);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
@media screen and (max-width: 639px) {
|
|
138
|
-
ul {
|
|
139
|
-
grid-template-columns: 1fr;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
.category-group {
|
|
143
|
-
display: block;
|
|
144
|
-
margin-top: var(--spacing-medium);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
.category-group:first-child {
|
|
148
|
-
margin-top: 0;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
.category-title {
|
|
152
|
-
position: static;
|
|
153
|
-
margin-bottom: var(--spacing-small);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
.category-privileges {
|
|
157
|
-
display: grid;
|
|
158
|
-
grid-template-columns: 1fr;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
`
|
|
162
|
-
]; }
|
|
163
|
-
render() {
|
|
164
|
-
const allPrivileges = this.allPrivileges || [];
|
|
165
|
-
const privileges = this.privileges || [];
|
|
166
|
-
const users = this.users || [];
|
|
167
|
-
// Group privileges by category
|
|
168
|
-
const groupedPrivileges = allPrivileges.reduce((acc, privilege) => {
|
|
169
|
-
const category = privilege.category || 'Other';
|
|
170
|
-
if (!acc[category]) {
|
|
171
|
-
acc[category] = [];
|
|
172
|
-
}
|
|
173
|
-
acc[category].push(privilege);
|
|
174
|
-
return acc;
|
|
175
|
-
}, {});
|
|
176
|
-
// Sort categories alphabetically
|
|
177
|
-
const sortedCategories = Object.keys(groupedPrivileges).sort();
|
|
178
|
-
// Sort privileges within each category by name (descending)
|
|
179
|
-
sortedCategories.forEach(category => {
|
|
180
|
-
groupedPrivileges[category].sort((a, b) => b.name.localeCompare(a.name));
|
|
181
|
-
});
|
|
182
|
-
return html `
|
|
183
|
-
<div titler>${String(i18next.t('label.user'))}</div>
|
|
184
|
-
|
|
185
|
-
<div users>${users.map(user => html `<div>${user.name} (${user.email})</div>`)}</div>
|
|
186
|
-
|
|
187
|
-
<div titler>${String(i18next.t('field.privileges'))}</div>
|
|
188
|
-
|
|
189
|
-
<div>
|
|
190
|
-
<input id="checkAll" type="checkbox" @change=${e => this.oncheckAll(e)} />
|
|
191
|
-
<label for="checkAll">Check all</label>
|
|
192
|
-
</div>
|
|
193
|
-
|
|
194
|
-
<ul privileges>
|
|
195
|
-
${sortedCategories.map(category => html `
|
|
196
|
-
<div class="category-group">
|
|
197
|
-
<div class="category-title">${category}</div>
|
|
198
|
-
<div class="category-privileges">
|
|
199
|
-
${groupedPrivileges[category].map(
|
|
200
|
-
/* privilege : {id, name, category, description} */
|
|
201
|
-
privilege => html `
|
|
202
|
-
<li ?deprecated=${privilege.deprecated}>
|
|
203
|
-
<input
|
|
204
|
-
type="checkbox"
|
|
205
|
-
id="${privilege.id}"
|
|
206
|
-
.checked=${privileges.map(p => p.id).indexOf(privilege.id) > -1}
|
|
207
|
-
.data-privilege=${privilege}
|
|
208
|
-
/>
|
|
209
|
-
<label for="${privilege.id}">${privilege.description}</label>
|
|
210
|
-
${privilege.deprecated
|
|
211
|
-
? html `<small>${String(i18next.t('text.privilege no longer declared'))}</small>`
|
|
212
|
-
: ''}
|
|
213
|
-
</li>
|
|
214
|
-
`)}
|
|
215
|
-
</div>
|
|
216
|
-
</div>
|
|
217
|
-
`)}
|
|
218
|
-
</ul>
|
|
219
|
-
|
|
220
|
-
<div class="button-container">
|
|
221
|
-
<md-elevated-button @click=${e => this.onSave()}>${String(i18next.t('button.save'))}</md-elevated-button>
|
|
222
|
-
<md-elevated-button @click=${e => this.onDeleteRole()} danger
|
|
223
|
-
>${String(i18next.t('button.delete role'))}</md-elevated-button
|
|
224
|
-
>
|
|
225
|
-
</div>
|
|
226
|
-
`;
|
|
227
|
-
}
|
|
228
|
-
async firstUpdated() {
|
|
229
|
-
this.allPrivileges = await this.fetchPrivileges();
|
|
230
|
-
}
|
|
231
|
-
async updated(changes) {
|
|
232
|
-
if (changes.has('role')) {
|
|
233
|
-
await this.fetchPrivilegesOnRole(this.role.name);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
checkAll(checked) {
|
|
237
|
-
Array.from(this.renderRoot.querySelectorAll('ul[privileges] input[type=checkbox]')).forEach(checkbox => (checkbox.checked = checked));
|
|
238
|
-
}
|
|
239
|
-
oncheckAll(e) {
|
|
240
|
-
this.checkAll(e.target.checked);
|
|
241
|
-
}
|
|
242
|
-
async onSave() {
|
|
243
|
-
const updateRoleObj = this.updateRoleObj;
|
|
244
|
-
const privileges = Array.from(this.renderRoot.querySelectorAll('ul[privileges] input[type=checkbox]:checked')).map(e => ({ id: e['data-privilege'].id }) // ObjectRef 타입에 맞게 id만 전송
|
|
245
|
-
);
|
|
246
|
-
const patch = { ...updateRoleObj, privileges: privileges };
|
|
247
|
-
if (this.role) {
|
|
248
|
-
const response = await client.mutate({
|
|
249
|
-
mutation: gql `
|
|
250
|
-
mutation ($id: String!, $patch: RolePatch!) {
|
|
251
|
-
updateRole(id: $id, patch: $patch) {
|
|
252
|
-
id
|
|
253
|
-
name
|
|
254
|
-
description
|
|
255
|
-
privileges {
|
|
256
|
-
id
|
|
257
|
-
description
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
`,
|
|
262
|
-
variables: { id: this.role.id, patch },
|
|
263
|
-
context: gqlContext()
|
|
264
|
-
});
|
|
265
|
-
if (!response.errors) {
|
|
266
|
-
await this.dispatchEvent(new CustomEvent('fetch-roles'));
|
|
267
|
-
this.showToast(i18next.t('text.data_updated_successfully'));
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
async onDeleteRole() {
|
|
272
|
-
if (await OxPrompt.open({
|
|
273
|
-
title: i18next.t('text.are_you_sure'),
|
|
274
|
-
text: i18next.t('text.are_you_sure_to_delete_x', { x: i18next.t('label.role') }),
|
|
275
|
-
confirmButton: { text: i18next.t('button.delete') },
|
|
276
|
-
cancelButton: { text: i18next.t('button.cancel') }
|
|
277
|
-
})) {
|
|
278
|
-
const response = await client.mutate({
|
|
279
|
-
mutation: gql `
|
|
280
|
-
mutation ($id: String!) {
|
|
281
|
-
deleteRole(id: $id)
|
|
282
|
-
}
|
|
283
|
-
`,
|
|
284
|
-
variables: { id: this.role.id },
|
|
285
|
-
context: gqlContext()
|
|
286
|
-
});
|
|
287
|
-
if (!response.errors) {
|
|
288
|
-
await this.dispatchEvent(new CustomEvent('fetch-roles'));
|
|
289
|
-
await OxPrompt.open({
|
|
290
|
-
type: 'success',
|
|
291
|
-
title: i18next.t('text.completed'),
|
|
292
|
-
text: i18next.t('text.x_deleted_successfully', { x: i18next.t('label.role') }),
|
|
293
|
-
confirmButton: { text: i18next.t('button.confirm') }
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
async fetchPrivileges() {
|
|
299
|
-
const response = await client.query({
|
|
300
|
-
query: gql `
|
|
301
|
-
query privileges {
|
|
302
|
-
privileges {
|
|
303
|
-
items {
|
|
304
|
-
id
|
|
305
|
-
name
|
|
306
|
-
category
|
|
307
|
-
description
|
|
308
|
-
deprecated
|
|
309
|
-
}
|
|
310
|
-
total
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
`
|
|
314
|
-
});
|
|
315
|
-
return response.data.privileges.items;
|
|
316
|
-
}
|
|
317
|
-
async fetchPrivilegesOnRole(name) {
|
|
318
|
-
const response = await client.query({
|
|
319
|
-
query: gql `
|
|
320
|
-
query ($name: String!) {
|
|
321
|
-
role(name: $name) {
|
|
322
|
-
id
|
|
323
|
-
name
|
|
324
|
-
privileges {
|
|
325
|
-
id
|
|
326
|
-
description
|
|
327
|
-
}
|
|
328
|
-
users {
|
|
329
|
-
name
|
|
330
|
-
email
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
`,
|
|
335
|
-
variables: { name },
|
|
336
|
-
context: gqlContext()
|
|
337
|
-
});
|
|
338
|
-
this.privileges = response.data.role?.privileges;
|
|
339
|
-
this.users = response.data.role?.users;
|
|
340
|
-
}
|
|
341
|
-
showToast(message) {
|
|
342
|
-
document.dispatchEvent(new CustomEvent('notify', { detail: { message, option: { timer: 1000 } } }));
|
|
343
|
-
}
|
|
344
|
-
};
|
|
345
|
-
__decorate([
|
|
346
|
-
property({ type: Object }),
|
|
347
|
-
__metadata("design:type", Object)
|
|
348
|
-
], RolePrivilegeEditor.prototype, "role", void 0);
|
|
349
|
-
__decorate([
|
|
350
|
-
property({ type: Array }),
|
|
351
|
-
__metadata("design:type", Array)
|
|
352
|
-
], RolePrivilegeEditor.prototype, "allPrivileges", void 0);
|
|
353
|
-
__decorate([
|
|
354
|
-
property({ type: Object }),
|
|
355
|
-
__metadata("design:type", Object)
|
|
356
|
-
], RolePrivilegeEditor.prototype, "updateRoleObj", void 0);
|
|
357
|
-
__decorate([
|
|
358
|
-
state(),
|
|
359
|
-
__metadata("design:type", Array)
|
|
360
|
-
], RolePrivilegeEditor.prototype, "privileges", void 0);
|
|
361
|
-
__decorate([
|
|
362
|
-
state(),
|
|
363
|
-
__metadata("design:type", Array)
|
|
364
|
-
], RolePrivilegeEditor.prototype, "users", void 0);
|
|
365
|
-
RolePrivilegeEditor = __decorate([
|
|
366
|
-
customElement('role-privilege-editor')
|
|
367
|
-
], RolePrivilegeEditor);
|
|
368
|
-
//# sourceMappingURL=role-privilege-editor.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"role-privilege-editor.js","sourceRoot":"","sources":["../../client/components/role-privilege-editor.ts"],"names":[],"mappings":"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","sourcesContent":["import gql from 'graphql-tag'\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { client, gqlContext } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { OxPrompt } from '@operato/popup/ox-prompt.js'\nimport { ButtonContainerStyles } from '@operato/styles'\n\n@customElement('role-privilege-editor')\nclass RolePrivilegeEditor extends localize(i18next)(LitElement) {\n static styles = [\n ButtonContainerStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n\n border: 1px solid var(--md-sys-color-primary);\n font: normal 15px var(--theme-font);\n }\n\n div {\n margin: var(--spacing-medium);\n }\n\n div[users] {\n margin: 0;\n background-color: var(--md-sys-color-surface-variant);\n }\n\n div[titler] {\n color: var(--md-sys-color-secondary);\n font: bold 1.2em var(--theme-font);\n text-transform: capitalize;\n }\n\n ul {\n flex: 1;\n color: var(--md-sys-color-secondary);\n background-color: var(--md-sys-color-surface-variant);\n overflow: auto;\n display: grid;\n grid-template-columns: 1fr 1fr;\n margin-block-start: 0;\n margin-block-end: 0;\n padding: var(--spacing-medium);\n list-style: none;\n border: 1px dashed rgba(0, 0, 0, 0.1);\n border-width: 1px 0;\n }\n\n .category-group {\n display: contents;\n }\n\n .category-title {\n font-weight: bold;\n font-size: 0.9em;\n color: var(--md-sys-color-primary);\n text-transform: uppercase;\n opacity: 0.7;\n align-self: stretch;\n position: sticky;\n top: 0;\n display: flex;\n align-items: center;\n justify-content: flex-end;\n text-align: right;\n }\n\n .category-privileges {\n display: contents;\n }\n\n @media screen and (min-width: 640px) {\n ul {\n grid-template-columns: minmax(200px, auto) 1fr 1fr;\n gap: 0;\n }\n\n .category-title {\n grid-column: 1;\n padding-right: var(--spacing-medium);\n }\n\n li {\n grid-column: auto;\n }\n }\n\n input[type='checkbox'] {\n display: inline;\n }\n\n li {\n padding: var(--spacing-small);\n border-bottom: 1px solid rgba(0, 0, 0, 0.1);\n }\n\n /* 코드가 더 이상 요구하지 않는 권한 — 부여해도 아무 일이 없으므로 눈에 구별되어야 한다.\n 감추지는 않는다. 이미 역할에 붙어 있을 수 있고, 그것을 보고 떼는 것이 관리자의 일이다. */\n li[deprecated] label {\n opacity: 0.55;\n text-decoration: line-through;\n }\n\n li[deprecated] small {\n display: block;\n margin-left: 1.6em;\n color: var(--md-sys-color-error);\n font-size: 0.8em;\n text-decoration: none;\n opacity: 0.9;\n }\n\n #checkAll,\n [for='checkAll'] {\n margin-bottom: var(--spacing-medium);\n padding-bottom: var(--spacing-small);\n font-weight: bold;\n }\n\n md-elevated-button {\n margin: 5px;\n background-color: var(--md-sys-color-surface-variant);\n }\n\n md-outlined-button {\n background-color: var(--md-sys-color-surface-variant);\n }\n\n @media screen and (max-width: 639px) {\n ul {\n grid-template-columns: 1fr;\n }\n\n .category-group {\n display: block;\n margin-top: var(--spacing-medium);\n }\n\n .category-group:first-child {\n margin-top: 0;\n }\n\n .category-title {\n position: static;\n margin-bottom: var(--spacing-small);\n }\n\n .category-privileges {\n display: grid;\n grid-template-columns: 1fr;\n }\n }\n `\n ]\n\n @property({ type: Object }) role: any\n @property({ type: Array }) allPrivileges: any[] = []\n @property({ type: Object }) updateRoleObj: any\n\n @state() privileges: { id: string; description: string }[] = []\n @state() users: { /* id: string; username: string; */ name: string; email: string }[] = []\n\n render() {\n const allPrivileges = this.allPrivileges || []\n const privileges = this.privileges || []\n const users = this.users || []\n\n // Group privileges by category\n const groupedPrivileges = allPrivileges.reduce(\n (acc, privilege) => {\n const category = privilege.category || 'Other'\n if (!acc[category]) {\n acc[category] = []\n }\n acc[category].push(privilege)\n return acc\n },\n {} as Record<string, any[]>\n )\n\n // Sort categories alphabetically\n const sortedCategories = Object.keys(groupedPrivileges).sort()\n\n // Sort privileges within each category by name (descending)\n sortedCategories.forEach(category => {\n groupedPrivileges[category].sort((a, b) => b.name.localeCompare(a.name))\n })\n\n return html`\n <div titler>${String(i18next.t('label.user'))}</div>\n\n <div users>${users.map(user => html`<div>${user.name} (${user.email})</div>`)}</div>\n\n <div titler>${String(i18next.t('field.privileges'))}</div>\n\n <div>\n <input id=\"checkAll\" type=\"checkbox\" @change=${e => this.oncheckAll(e)} />\n <label for=\"checkAll\">Check all</label>\n </div>\n\n <ul privileges>\n ${sortedCategories.map(\n category => html`\n <div class=\"category-group\">\n <div class=\"category-title\">${category}</div>\n <div class=\"category-privileges\">\n ${groupedPrivileges[category].map(\n /* privilege : {id, name, category, description} */\n privilege => html`\n <li ?deprecated=${privilege.deprecated}>\n <input\n type=\"checkbox\"\n id=\"${privilege.id}\"\n .checked=${privileges.map(p => p.id).indexOf(privilege.id) > -1}\n .data-privilege=${privilege}\n />\n <label for=\"${privilege.id}\">${privilege.description}</label>\n ${privilege.deprecated\n ? html`<small>${String(i18next.t('text.privilege no longer declared'))}</small>`\n : ''}\n </li>\n `\n )}\n </div>\n </div>\n `\n )}\n </ul>\n\n <div class=\"button-container\">\n <md-elevated-button @click=${e => this.onSave()}>${String(i18next.t('button.save'))}</md-elevated-button>\n <md-elevated-button @click=${e => this.onDeleteRole()} danger\n >${String(i18next.t('button.delete role'))}</md-elevated-button\n >\n </div>\n `\n }\n\n async firstUpdated() {\n this.allPrivileges = await this.fetchPrivileges()\n }\n\n async updated(changes) {\n if (changes.has('role')) {\n await this.fetchPrivilegesOnRole(this.role.name)\n }\n }\n\n checkAll(checked) {\n Array.from(this.renderRoot.querySelectorAll('ul[privileges] input[type=checkbox]')).forEach(\n checkbox => ((checkbox as HTMLInputElement).checked = checked)\n )\n }\n\n oncheckAll(e) {\n this.checkAll(e.target.checked)\n }\n\n async onSave() {\n const updateRoleObj = this.updateRoleObj\n const privileges = Array.from(this.renderRoot.querySelectorAll('ul[privileges] input[type=checkbox]:checked')).map(\n e => ({ id: e['data-privilege'].id }) // ObjectRef 타입에 맞게 id만 전송\n )\n\n const patch = { ...updateRoleObj, privileges: privileges }\n\n if (this.role) {\n const response = await client.mutate({\n mutation: gql`\n mutation ($id: String!, $patch: RolePatch!) {\n updateRole(id: $id, patch: $patch) {\n id\n name\n description\n privileges {\n id\n description\n }\n }\n }\n `,\n variables: { id: this.role.id, patch },\n context: gqlContext()\n })\n\n if (!response.errors) {\n await this.dispatchEvent(new CustomEvent('fetch-roles'))\n this.showToast(i18next.t('text.data_updated_successfully'))\n }\n }\n }\n\n async onDeleteRole() {\n if (\n await OxPrompt.open({\n title: i18next.t('text.are_you_sure'),\n text: i18next.t('text.are_you_sure_to_delete_x', { x: i18next.t('label.role') }),\n confirmButton: { text: i18next.t('button.delete') },\n cancelButton: { text: i18next.t('button.cancel') }\n })\n ) {\n const response = await client.mutate({\n mutation: gql`\n mutation ($id: String!) {\n deleteRole(id: $id)\n }\n `,\n variables: { id: this.role.id },\n context: gqlContext()\n })\n\n if (!response.errors) {\n await this.dispatchEvent(new CustomEvent('fetch-roles'))\n\n await OxPrompt.open({\n type: 'success',\n title: i18next.t('text.completed'),\n text: i18next.t('text.x_deleted_successfully', { x: i18next.t('label.role') }),\n confirmButton: { text: i18next.t('button.confirm') }\n })\n }\n }\n }\n\n async fetchPrivileges() {\n const response = await client.query({\n query: gql`\n query privileges {\n privileges {\n items {\n id\n name\n category\n description\n deprecated\n }\n total\n }\n }\n `\n })\n\n return response.data.privileges.items\n }\n\n async fetchPrivilegesOnRole(name) {\n const response = await client.query({\n query: gql`\n query ($name: String!) {\n role(name: $name) {\n id\n name\n privileges {\n id\n description\n }\n users {\n name\n email\n }\n }\n }\n `,\n variables: { name },\n context: gqlContext()\n })\n\n this.privileges = response.data.role?.privileges\n this.users = response.data.role?.users\n }\n\n showToast(message) {\n document.dispatchEvent(new CustomEvent('notify', { detail: { message, option: { timer: 1000 } } }))\n }\n}\n"]}
|