@things-factory/auth-ui 10.1.226 → 10.1.227
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-template-picker.d.ts +5 -0
- package/dist-client/components/role-template-picker.js +151 -42
- package/dist-client/components/role-template-picker.js.map +1 -1
- package/dist-client/components/role-template-picker.test.d.ts +1 -0
- package/dist-client/components/role-template-picker.test.js +41 -0
- package/dist-client/components/role-template-picker.test.js.map +1 -0
- package/dist-client/pages/role/role-add-delete.test.d.ts +1 -0
- package/dist-client/pages/role/role-add-delete.test.js +61 -0
- package/dist-client/pages/role/role-add-delete.test.js.map +1 -0
- package/dist-client/pages/role/role-inheritance-shown.test.d.ts +1 -0
- package/dist-client/pages/role/role-inheritance-shown.test.js +31 -0
- package/dist-client/pages/role/role-inheritance-shown.test.js.map +1 -0
- package/dist-client/pages/role/role-list-page.d.ts +15 -11
- package/dist-client/pages/role/role-list-page.js +112 -53
- package/dist-client/pages/role/role-list-page.js.map +1 -1
- package/dist-client/pages/role/role-privilege-inherited.test.d.ts +1 -0
- package/dist-client/pages/role/role-privilege-inherited.test.js +23 -0
- package/dist-client/pages/role/role-privilege-inherited.test.js.map +1 -0
- package/dist-client/pages/role/role-privilege-page.d.ts +8 -5
- package/dist-client/pages/role/role-privilege-page.js +23 -13
- package/dist-client/pages/role/role-privilege-page.js.map +1 -1
- package/dist-client/pages/role/role-privilege-views.test.d.ts +1 -0
- package/dist-client/pages/role/role-privilege-views.test.js +19 -0
- package/dist-client/pages/role/role-privilege-views.test.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/helps/auth/application.ko.md +1 -1
- package/helps/auth/auth-provider.ko.md +1 -1
- package/helps/auth/domain-owners.ko.md +1 -1
- package/helps/operato-hub/partners.ko.md +1 -1
- package/helps/system/attribute.ko.md +1 -1
- package/package.json +6 -6
- package/translations/en.json +17 -10
- package/translations/es.json +17 -10
- package/translations/ja.json +17 -10
- package/translations/ko.json +18 -11
- package/translations/ms.json +17 -10
- package/translations/zh.json +17 -10
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-add-delete.test.js","sourceRoot":"","sources":["../../../client/pages/role/role-add-delete.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync } from 'fs'\nimport path from 'path'\n\n/**\n * How a role is added and deleted on the role list (the user, 2026-10-28: 「도대체 어떻게 추가하고, 삭제한다는\n * 말인가」). One way to add - the 「역할 추가」 window, no blank row in the grid - and deleting acts on picked rows,\n * says first which picked roles people still hold, and deletes none of them. The page needs a DOM, so the test\n * reads its source with comments stripped.\n */\n\nconst read = (file: string) => readFileSync(path.join(__dirname, file), 'utf-8')\nconst strip = (source: string) => source.replace(/\\/\\*[\\s\\S]*?\\*\\//g, '').replace(/^\\s*\\/\\/.*$/gm, '').replace(/<!--[\\s\\S]*?-->/g, '')\n\nconst code = strip(read('role-list-page.ts'))\nconst deleting = code.slice(code.indexOf('private async onDeleteGrid('), code.indexOf('private async openCreate('))\nconst words = JSON.parse(readFileSync(path.join(__dirname, '../../../translations/ko.json'), 'utf-8'))\n\ndescribe('adding a role', () => {\n it('★ the button says what it adds', () => {\n expect(code).toMatch(/<button add @click=\\$\\{\\(\\) => this\\.openCreate\\(\\)\\}>\\s*<md-icon>add<\\/md-icon>\\$\\{i18next\\.t\\('button\\.add role'\\)\\}/)\n expect(words['button.add role']).toBe('역할 추가')\n })\n\n it('★ the grid draws no blank row to type a role into', () => {\n expect(code).toMatch(/rows: \\{\\s*appendable: false,/)\n })\n\n it('the window is titled the same', () => {\n expect(code).toMatch(/title: i18next\\.t\\('button\\.add role'\\)/)\n })\n})\n\ndescribe('deleting a role', () => {\n it('★ rows can be picked', () => {\n expect(code).toMatch(/\\{ type: 'gutter', gutterName: 'row-selector', multiple: true, fixed: true \\}/)\n })\n\n it('★ the list asks how many people hold each role and shows it', () => {\n expect(code).toMatch(/privilegeCount\\s+userCount\\s+templateGap/)\n expect(code).toMatch(/name: 'userCount',[\\s\\S]{0,120}renderer: userCountCell/)\n })\n\n it('★ names the picked roles people hold and stops before asking the server', () => {\n const held = deleting.indexOf(\"record.userCount > 0\")\n expect(held).toBeGreaterThan(0)\n expect(deleting.indexOf('deleteRoles(ids')).toBeGreaterThan(held)\n expect(deleting.slice(held, deleting.indexOf('deleteRoles(ids'))).toMatch(/text\\.roles held by users are not deleted[\\s\\S]*?return\\s*\\}/)\n })\n\n it('★ names picked roles inherited from the parent and stops - even mixed with this domain\\'s own', () => {\n const inherited = deleting.indexOf('this.isInherited(record)')\n expect(inherited).toBeGreaterThan(0)\n expect(deleting.slice(inherited, deleting.indexOf('deleteRoles(ids'))).toMatch(/if \\(inherited\\.length\\) \\{[\\s\\S]*?text\\.inherited roles are not deleted here[\\s\\S]*?return\\s*\\}/)\n expect(deleting).toMatch(/const mine = selected\\n/)\n })\n\n it('says to pick rows when none are picked, instead of doing nothing', () => {\n expect(deleting).toMatch(/if \\(!selected\\?\\.length\\) \\{\\s*await OxPrompt\\.open\\([\\s\\S]*?text\\.pick the roles to delete/)\n })\n\n it('names what it is about to delete before deleting it', () => {\n expect(deleting).toMatch(/text\\.delete these roles', \\{ names: mine\\.map/)\n })\n\n it('every sentence has its words in every language', () => {\n const keys = ['button.add role', 'field.role users', 'text.n people', 'text.pick the roles to delete', 'text.roles held by users are not deleted', 'text.role and its users', 'text.delete these roles', 'text.inherited roles are not deleted here']\n for (const language of ['ko', 'en', 'ja', 'zh', 'es', 'ms']) {\n const all = JSON.parse(readFileSync(path.join(__dirname, `../../../translations/${language}.json`), 'utf-8'))\n for (const key of keys) expect([language, key, typeof all[key]]).toEqual([language, key, 'string'])\n }\n })\n})\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
/**
|
|
4
|
+
* A domain that inherits from nowhere shows nothing about inheritance - no 「우리 역할 · 상속 · 전체」
|
|
5
|
+
* buttons, no line about where roles come from, no source column (the user, 2026-10-28). The page
|
|
6
|
+
* needs a DOM, so the test reads its source with comments stripped.
|
|
7
|
+
*/
|
|
8
|
+
const source = readFileSync(path.join(__dirname, 'role-list-page.ts'), 'utf-8');
|
|
9
|
+
const code = source.replace(/\/\*[\s\S]*?\*\//g, '').replace(/^\s*\/\/.*$/gm, '');
|
|
10
|
+
const scopes = code.slice(code.indexOf('private renderScopes('), code.indexOf('private renderLineage('));
|
|
11
|
+
const initialized = code.slice(code.indexOf('async pageInitialized('), code.indexOf('async pageUpdated('));
|
|
12
|
+
describe('the role list in a domain with no parent', () => {
|
|
13
|
+
it('★ inherits only when the profile names a parent - not knowing is not having none', () => {
|
|
14
|
+
expect(code).toMatch(/private hasParent\(\): boolean \{\s*return this\.parentDomain !== null\s*\}/);
|
|
15
|
+
});
|
|
16
|
+
it('★ draws no scope buttons, and so no lineage line', () => {
|
|
17
|
+
expect(scopes).toMatch(/^private renderScopes\(\) \{\s*if \(!this\.hasParent\(\)\) \{\s*return nothing\s*\}/);
|
|
18
|
+
expect(code.match(/this\.renderLineage\(\)/g)).toHaveLength(1);
|
|
19
|
+
expect(scopes).toMatch(/this\.renderLineage\(\)/);
|
|
20
|
+
});
|
|
21
|
+
it('★ leaves the source column and its list detail out', () => {
|
|
22
|
+
expect(initialized).toMatch(/const inheriting = this\.hasParent\(\)/);
|
|
23
|
+
expect(initialized).toMatch(/details: inheriting \? \['privilegeCount', 'userCount', 'source'\] : \['privilegeCount', 'userCount'\]/);
|
|
24
|
+
expect(initialized).toMatch(/\.\.\.\(inheriting\s*\?\s*\[\s*\{\s*type: 'string',\s*name: 'source',/);
|
|
25
|
+
expect(initialized.match(/name: 'source'/g)).toHaveLength(1);
|
|
26
|
+
});
|
|
27
|
+
it('counts the scopes only where there are scopes to count', () => {
|
|
28
|
+
expect(initialized).toMatch(/if \(this\.hasParent\(\)\) \{\s*this\.countScopes\(\)\s*\}/);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
//# sourceMappingURL=role-inheritance-shown.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-inheritance-shown.test.js","sourceRoot":"","sources":["../../../client/pages/role/role-inheritance-shown.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAA;AACjC,OAAO,IAAI,MAAM,MAAM,CAAA;AAEvB;;;;GAIG;AAEH,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,mBAAmB,CAAC,EAAE,OAAO,CAAC,CAAA;AAC/E,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAA;AAEjF,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,uBAAuB,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC,CAAA;AACxG,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,wBAAwB,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAAA;AAE1G,QAAQ,CAAC,0CAA0C,EAAE,GAAG,EAAE;IACxD,EAAE,CAAC,kFAAkF,EAAE,GAAG,EAAE;QAC1F,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,6EAA6E,CAAC,CAAA;IACrG,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,qFAAqF,CAAC,CAAA;QAC7G,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;QAC9D,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,yBAAyB,CAAC,CAAA;IACnD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,wCAAwC,CAAC,CAAA;QACrE,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,wGAAwG,CAAC,CAAA;QACrI,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,uEAAuE,CAAC,CAAA;QACpG,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;IAC9D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,4DAA4D,CAAC,CAAA;IAC3F,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["import { readFileSync } from 'fs'\nimport path from 'path'\n\n/**\n * A domain that inherits from nowhere shows nothing about inheritance - no 「우리 역할 · 상속 · 전체」\n * buttons, no line about where roles come from, no source column (the user, 2026-10-28). The page\n * needs a DOM, so the test reads its source with comments stripped.\n */\n\nconst source = readFileSync(path.join(__dirname, 'role-list-page.ts'), 'utf-8')\nconst code = source.replace(/\\/\\*[\\s\\S]*?\\*\\//g, '').replace(/^\\s*\\/\\/.*$/gm, '')\n\nconst scopes = code.slice(code.indexOf('private renderScopes('), code.indexOf('private renderLineage('))\nconst initialized = code.slice(code.indexOf('async pageInitialized('), code.indexOf('async pageUpdated('))\n\ndescribe('the role list in a domain with no parent', () => {\n it('★ inherits only when the profile names a parent - not knowing is not having none', () => {\n expect(code).toMatch(/private hasParent\\(\\): boolean \\{\\s*return this\\.parentDomain !== null\\s*\\}/)\n })\n\n it('★ draws no scope buttons, and so no lineage line', () => {\n expect(scopes).toMatch(/^private renderScopes\\(\\) \\{\\s*if \\(!this\\.hasParent\\(\\)\\) \\{\\s*return nothing\\s*\\}/)\n expect(code.match(/this\\.renderLineage\\(\\)/g)).toHaveLength(1)\n expect(scopes).toMatch(/this\\.renderLineage\\(\\)/)\n })\n\n it('★ leaves the source column and its list detail out', () => {\n expect(initialized).toMatch(/const inheriting = this\\.hasParent\\(\\)/)\n expect(initialized).toMatch(/details: inheriting \\? \\['privilegeCount', 'userCount', 'source'\\] : \\['privilegeCount', 'userCount'\\]/)\n expect(initialized).toMatch(/\\.\\.\\.\\(inheriting\\s*\\?\\s*\\[\\s*\\{\\s*type: 'string',\\s*name: 'source',/)\n expect(initialized.match(/name: 'source'/g)).toHaveLength(1)\n })\n\n it('counts the scopes only where there are scopes to count', () => {\n expect(initialized).toMatch(/if \\(this\\.hasParent\\(\\)\\) \\{\\s*this\\.countScopes\\(\\)\\s*\\}/)\n })\n})\n"]}
|
|
@@ -105,19 +105,22 @@ export declare class RoleListPage extends RoleListPageBase {
|
|
|
105
105
|
*/
|
|
106
106
|
private renderScopes;
|
|
107
107
|
/**
|
|
108
|
-
* 어디에서 물려받나 —
|
|
108
|
+
* 어디에서 물려받나 — **어느 도메인인지**를 적는다. 그 역할을 고치러 가야 할 곳이다.
|
|
109
109
|
*
|
|
110
|
-
*
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
* 묶음 단추와 함께 그려지므로 부모가 있을 때만 온다(`hasParent`).
|
|
111
|
+
*/
|
|
112
|
+
private renderLineage;
|
|
113
|
+
/**
|
|
114
|
+
* 물려받는 도메인인가 — 상속을 말하는 것(묶음 단추 · 물려받는 곳 · 출처 칸)이 모두 이것을 본다.
|
|
114
115
|
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
116
|
+
* 최상위 도메인에는 상속이 없다. 「우리 역할 9 · 상속 0 · 전체 9」와 줄마다 「우리」는 같은 말을
|
|
117
|
+
* 되풀이할 뿐이라 그리지 않는다(사용자 2026-10-28: 「이 도메인이 상속을 받는 도메인이 아니라면,
|
|
118
|
+
* 굳이 상속과 관련된 정보를 보일 필요가 없을 것이다」).
|
|
117
119
|
*
|
|
118
|
-
* 아직
|
|
120
|
+
* 아직 모르면(`undefined`, 옛 서버가 `parent` 칸을 안 보낼 때) 그대로 그린다 — 모르는 것을
|
|
121
|
+
* 「없다」로 접지 않는다.
|
|
119
122
|
*/
|
|
120
|
-
private
|
|
123
|
+
private hasParent;
|
|
121
124
|
private onScope;
|
|
122
125
|
/**
|
|
123
126
|
* 세 묶음의 건수.
|
|
@@ -188,8 +191,9 @@ export declare class RoleListPage extends RoleListPageBase {
|
|
|
188
191
|
/**
|
|
189
192
|
* 고른 역할을 지운다.
|
|
190
193
|
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
194
|
+
* 가진 사람이 있는 역할은 서버가 거절한다(`role-in-use`) — 하나라도 있으면 아무것도 안 지운다. 화면은
|
|
195
|
+
* 보내기 전에 같은 것을 사람 수 칸으로 알고 있으므로 먼저 그 이름을 말하고 멈춘다. 서버의 거절은 그 사이
|
|
196
|
+
* 누군가 부여했을 때를 위한 것이다.
|
|
193
197
|
*/
|
|
194
198
|
private onDeleteGrid;
|
|
195
199
|
/**
|
|
@@ -167,11 +167,15 @@ let RoleListPage = class RoleListPage extends RoleListPageBase {
|
|
|
167
167
|
* 같은 줄의 문법이 깨진다. 범위 단추와 같은 모서리·같은 테두리를 쓰되, **여는 일**이므로
|
|
168
168
|
* 채워서 한 단 앞으로 낸다 — 나머지는 거르는 일이고 이것만 만드는 일이다.
|
|
169
169
|
*/
|
|
170
|
+
/* 「+」 하나로는 무엇을 더하는지 몰랐다(2026-10-28 사용자: 「'+' 라고 하지 말고 '역할 추가' 라고 하자」). */
|
|
170
171
|
button[add] {
|
|
171
172
|
display: inline-flex;
|
|
172
173
|
align-items: center;
|
|
173
174
|
justify-content: center;
|
|
174
|
-
|
|
175
|
+
gap: 4px;
|
|
176
|
+
padding: 5px 12px 5px 8px;
|
|
177
|
+
font: inherit;
|
|
178
|
+
font-size: var(--fontsize-small, 13px);
|
|
175
179
|
color: var(--md-sys-color-on-secondary-container);
|
|
176
180
|
background-color: var(--md-sys-color-secondary-container);
|
|
177
181
|
border: 1px solid transparent;
|
|
@@ -254,8 +258,8 @@ let RoleListPage = class RoleListPage extends RoleListPageBase {
|
|
|
254
258
|
여기 ox-record-creator 가 있었다 — 목록에 빈 줄을 만들고 이름을 치게 하는
|
|
255
259
|
관례(24 파일)다. 걷어낸 이유는 openCreate 의 머리말에 있다.
|
|
256
260
|
-->
|
|
257
|
-
<button add @click=${() => this.openCreate()}
|
|
258
|
-
<md-icon>add</md-icon
|
|
261
|
+
<button add @click=${() => this.openCreate()}>
|
|
262
|
+
<md-icon>add</md-icon>${i18next.t('button.add role')}
|
|
259
263
|
</button>
|
|
260
264
|
</div>
|
|
261
265
|
</div>
|
|
@@ -275,6 +279,9 @@ let RoleListPage = class RoleListPage extends RoleListPageBase {
|
|
|
275
279
|
* 아직 못 센 묶음은 수를 **안 그린다** — 0 으로 메우면 「없다」와 같아 보인다.
|
|
276
280
|
*/
|
|
277
281
|
renderScopes() {
|
|
282
|
+
if (!this.hasParent()) {
|
|
283
|
+
return nothing;
|
|
284
|
+
}
|
|
278
285
|
const scopes = [
|
|
279
286
|
[InheritedValueType.None, 'label.ours'],
|
|
280
287
|
[InheritedValueType.Only, 'label.inherited'],
|
|
@@ -293,30 +300,26 @@ let RoleListPage = class RoleListPage extends RoleListPageBase {
|
|
|
293
300
|
`;
|
|
294
301
|
}
|
|
295
302
|
/**
|
|
296
|
-
* 어디에서 물려받나 —
|
|
303
|
+
* 어디에서 물려받나 — **어느 도메인인지**를 적는다. 그 역할을 고치러 가야 할 곳이다.
|
|
297
304
|
*
|
|
298
|
-
*
|
|
299
|
-
* 도메인에서는 **어느 도메인인지**가 필요하고(그 역할을 고치러 가야 할 곳이다), 최상위
|
|
300
|
-
* 도메인에서는 **물려받는 곳이 없다는 사실**이 필요하다. 후자를 안 적으면 「상속 0」이
|
|
301
|
-
* 「아직 안 만들었나」로 읽힌다.
|
|
302
|
-
*
|
|
303
|
-
* 단추를 숨기지 않는다. 숨기면 최상위 도메인의 관리자는 상속이라는 것이 있다는 사실
|
|
304
|
-
* 자체를 모르고, 자식 도메인을 만든 뒤에 갑자기 나타나는 단추를 만난다.
|
|
305
|
-
*
|
|
306
|
-
* 아직 프로필을 못 읽었으면 **아무것도 안 적는다** — 모르는 것을 「없다」로 적지 않는다.
|
|
305
|
+
* 묶음 단추와 함께 그려지므로 부모가 있을 때만 온다(`hasParent`).
|
|
307
306
|
*/
|
|
308
307
|
renderLineage() {
|
|
309
|
-
if (this.parentDomain === undefined) {
|
|
310
|
-
return nothing;
|
|
311
|
-
}
|
|
312
308
|
const from = this.parentDomain?.name || this.parentDomain?.subdomain;
|
|
313
|
-
return html
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
309
|
+
return html `<span lineage>${i18next.t('text.inherited from x', { x: from })}</span>`;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* 물려받는 도메인인가 — 상속을 말하는 것(묶음 단추 · 물려받는 곳 · 출처 칸)이 모두 이것을 본다.
|
|
313
|
+
*
|
|
314
|
+
* 최상위 도메인에는 상속이 없다. 「우리 역할 9 · 상속 0 · 전체 9」와 줄마다 「우리」는 같은 말을
|
|
315
|
+
* 되풀이할 뿐이라 그리지 않는다(사용자 2026-10-28: 「이 도메인이 상속을 받는 도메인이 아니라면,
|
|
316
|
+
* 굳이 상속과 관련된 정보를 보일 필요가 없을 것이다」).
|
|
317
|
+
*
|
|
318
|
+
* 아직 모르면(`undefined`, 옛 서버가 `parent` 칸을 안 보낼 때) 그대로 그린다 — 모르는 것을
|
|
319
|
+
* 「없다」로 접지 않는다.
|
|
320
|
+
*/
|
|
321
|
+
hasParent() {
|
|
322
|
+
return this.parentDomain !== null;
|
|
320
323
|
}
|
|
321
324
|
onScope(scope) {
|
|
322
325
|
if (this.scope === scope) {
|
|
@@ -421,7 +424,10 @@ let RoleListPage = class RoleListPage extends RoleListPageBase {
|
|
|
421
424
|
* 안 적는다. 「모른다」를 「없다」로 접지 않는다.
|
|
422
425
|
*/
|
|
423
426
|
this.parentDomain = mine && 'parent' in mine ? mine.parent : undefined;
|
|
424
|
-
this.
|
|
427
|
+
if (this.hasParent()) {
|
|
428
|
+
this.countScopes();
|
|
429
|
+
}
|
|
430
|
+
const inheriting = this.hasParent();
|
|
425
431
|
this.config = {
|
|
426
432
|
/*
|
|
427
433
|
* **두 모드가 다른 말을 하지 않게 한다.**
|
|
@@ -435,10 +441,12 @@ let RoleListPage = class RoleListPage extends RoleListPageBase {
|
|
|
435
441
|
*/
|
|
436
442
|
list: {
|
|
437
443
|
fields: ['name', 'description'],
|
|
438
|
-
details: ['privilegeCount', 'source']
|
|
444
|
+
details: inheriting ? ['privilegeCount', 'userCount', 'source'] : ['privilegeCount', 'userCount']
|
|
439
445
|
},
|
|
440
446
|
columns: [
|
|
441
447
|
{ type: 'gutter', gutterName: 'sequence', fixed: true },
|
|
448
|
+
/* 지우기가 고른 행에 닿는다 — 고르는 칸이 없으면 「삭제」는 무엇을 지우는지 말할 수 없다. */
|
|
449
|
+
{ type: 'gutter', gutterName: 'row-selector', multiple: true, fixed: true },
|
|
442
450
|
{
|
|
443
451
|
/*
|
|
444
452
|
* **권한 편집으로 가는 문.**
|
|
@@ -533,6 +541,18 @@ let RoleListPage = class RoleListPage extends RoleListPageBase {
|
|
|
533
541
|
record: { align: 'center', renderer: privilegeCountCell },
|
|
534
542
|
width: 110
|
|
535
543
|
},
|
|
544
|
+
{
|
|
545
|
+
/*
|
|
546
|
+
* **이 역할을 가진 사람이 몇 명인가.** 가진 사람이 있는 역할은 지우지 않으므로(`deleteRoles`),
|
|
547
|
+
* 지우기 전에 이 수가 무엇이 지워지지 않을지 말한다(2026-10-28 사용자: 「각 역할이 부여된 숫자를
|
|
548
|
+
* 보여주는 것이 좋겠다」).
|
|
549
|
+
*/
|
|
550
|
+
type: 'string',
|
|
551
|
+
name: 'userCount',
|
|
552
|
+
header: i18next.t('field.role users'),
|
|
553
|
+
record: { align: 'center', renderer: userCountCell },
|
|
554
|
+
width: 100
|
|
555
|
+
},
|
|
536
556
|
{
|
|
537
557
|
/*
|
|
538
558
|
* **템플릿이 그 뒤로 더 얻은 것** (ADR-0075 결정 1).
|
|
@@ -551,25 +571,29 @@ let RoleListPage = class RoleListPage extends RoleListPageBase {
|
|
|
551
571
|
record: { align: 'center', renderer: (_v, _c, record) => this.templateGapCell(record) },
|
|
552
572
|
width: 150
|
|
553
573
|
},
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
574
|
+
...(inheriting
|
|
575
|
+
? [
|
|
576
|
+
{
|
|
577
|
+
/*
|
|
578
|
+
* **출처 — 여기가 「보이는 것과 고칠 수 있는 것」을 가른다.**
|
|
579
|
+
*
|
|
580
|
+
* 전에는 도메인 이름을 날값으로 냈다(`SYSTEM` · `MIRATEK`). 그건 구분이 아니다 —
|
|
581
|
+
* 읽는 사람이 자기 도메인 이름을 외우고 있어야 한다. 지금은 「우리」와
|
|
582
|
+
* 「상속 · SYSTEM」 으로 말한다.
|
|
583
|
+
*
|
|
584
|
+
* ⚠ `classifier` 로 행 전체를 물들이지 않는다. 선례들이 색을 `'#8B0000'` 처럼 박아
|
|
585
|
+
* 두었고(dataset 5곳), 그 규약(`emphasized: [배경, 글자]`)이 `var(...)` 를 받는지도
|
|
586
|
+
* 확인되지 않았다. 그리고 `emphasized` 는 **강조**하는 장치인데 여기서 필요한 것은
|
|
587
|
+
* 그 반대다 — 11행 중 9행을 강조하면 강조가 아니다. 그래서 이 칸이 진다.
|
|
588
|
+
*/
|
|
589
|
+
type: 'string',
|
|
590
|
+
name: 'source',
|
|
591
|
+
header: i18next.t('field.source'),
|
|
592
|
+
record: { align: 'center', renderer: (_v, _c, record) => this.sourceCell(record) },
|
|
593
|
+
width: 150
|
|
594
|
+
}
|
|
595
|
+
]
|
|
596
|
+
: []),
|
|
573
597
|
{
|
|
574
598
|
type: 'datetime',
|
|
575
599
|
name: 'updatedAt',
|
|
@@ -580,6 +604,8 @@ let RoleListPage = class RoleListPage extends RoleListPageBase {
|
|
|
580
604
|
}
|
|
581
605
|
],
|
|
582
606
|
rows: {
|
|
607
|
+
/* 역할은 「역할 추가」 창으로만 선다 — 격자 맨 아래 빈 줄은 이름만 받는 두 번째 문이었다. */
|
|
608
|
+
appendable: false,
|
|
583
609
|
selectable: { multiple: true }
|
|
584
610
|
},
|
|
585
611
|
sorters: [{ name: 'name' }]
|
|
@@ -636,6 +662,7 @@ let RoleListPage = class RoleListPage extends RoleListPageBase {
|
|
|
636
662
|
subdomain
|
|
637
663
|
}
|
|
638
664
|
privilegeCount
|
|
665
|
+
userCount
|
|
639
666
|
templateGap {
|
|
640
667
|
templateId
|
|
641
668
|
templateName
|
|
@@ -716,24 +743,47 @@ let RoleListPage = class RoleListPage extends RoleListPageBase {
|
|
|
716
743
|
/**
|
|
717
744
|
* 고른 역할을 지운다.
|
|
718
745
|
*
|
|
719
|
-
*
|
|
720
|
-
*
|
|
746
|
+
* 가진 사람이 있는 역할은 서버가 거절한다(`role-in-use`) — 하나라도 있으면 아무것도 안 지운다. 화면은
|
|
747
|
+
* 보내기 전에 같은 것을 사람 수 칸으로 알고 있으므로 먼저 그 이름을 말하고 멈춘다. 서버의 거절은 그 사이
|
|
748
|
+
* 누군가 부여했을 때를 위한 것이다.
|
|
721
749
|
*/
|
|
722
750
|
async onDeleteGrid() {
|
|
723
751
|
const selected = this.grist.selected;
|
|
724
752
|
if (!selected?.length) {
|
|
753
|
+
await OxPrompt.open({
|
|
754
|
+
type: 'info',
|
|
755
|
+
title: i18next.t('button.delete'),
|
|
756
|
+
text: i18next.t('text.pick the roles to delete'),
|
|
757
|
+
confirmButton: { text: i18next.t('button.confirm') }
|
|
758
|
+
});
|
|
725
759
|
return;
|
|
726
760
|
}
|
|
727
761
|
/*
|
|
728
762
|
* **상속된 행은 여기서 못 지운다** — `deleteRoles` 가 `domain.id` 로 좁혀 찾으므로 그 행은
|
|
729
|
-
* 조용히 남는다.
|
|
763
|
+
* 조용히 남는다. 우리 역할과 섞여 골랐을 때도 빼고 지우지 않는다 — 그러면 확인 창이 고른 것과
|
|
764
|
+
* 다른 것을 지우겠다고 말한다. 이름을 대고 멈춘다(가진 사람이 있는 역할과 같은 규칙).
|
|
730
765
|
*/
|
|
731
|
-
const
|
|
732
|
-
if (
|
|
766
|
+
const inherited = selected.filter((record) => this.isInherited(record));
|
|
767
|
+
if (inherited.length) {
|
|
733
768
|
await OxPrompt.open({
|
|
734
769
|
type: 'warning',
|
|
735
770
|
title: i18next.t('button.delete'),
|
|
736
|
-
text: i18next.t('text.inherited roles are
|
|
771
|
+
text: i18next.t('text.inherited roles are not deleted here', {
|
|
772
|
+
names: inherited.map((record) => record.name).join(', ')
|
|
773
|
+
}),
|
|
774
|
+
confirmButton: { text: i18next.t('button.confirm') }
|
|
775
|
+
});
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
const mine = selected;
|
|
779
|
+
const held = mine.filter((record) => record.userCount > 0);
|
|
780
|
+
if (held.length) {
|
|
781
|
+
await OxPrompt.open({
|
|
782
|
+
type: 'warning',
|
|
783
|
+
title: i18next.t('button.delete'),
|
|
784
|
+
text: i18next.t('text.roles held by users are not deleted', {
|
|
785
|
+
names: held.map((record) => i18next.t('text.role and its users', { name: record.name, n: record.userCount })).join(', ')
|
|
786
|
+
}),
|
|
737
787
|
confirmButton: { text: i18next.t('button.confirm') }
|
|
738
788
|
});
|
|
739
789
|
return;
|
|
@@ -741,8 +791,8 @@ let RoleListPage = class RoleListPage extends RoleListPageBase {
|
|
|
741
791
|
if (!(await OxPrompt.open({
|
|
742
792
|
type: 'warning',
|
|
743
793
|
title: i18next.t('button.delete'),
|
|
744
|
-
text: i18next.t('text.roles
|
|
745
|
-
confirmButton: { text: i18next.t('button.
|
|
794
|
+
text: i18next.t('text.delete these roles', { names: mine.map((record) => record.name).join(', ') }),
|
|
795
|
+
confirmButton: { text: i18next.t('button.delete') },
|
|
746
796
|
cancelButton: { text: i18next.t('button.cancel') }
|
|
747
797
|
}))) {
|
|
748
798
|
return;
|
|
@@ -784,7 +834,7 @@ let RoleListPage = class RoleListPage extends RoleListPageBase {
|
|
|
784
834
|
const popup = openPopup(html `<role-template-picker .page=${this} @close=${() => popup.close()}></role-template-picker>`, {
|
|
785
835
|
backdrop: true,
|
|
786
836
|
size: 'large',
|
|
787
|
-
title: i18next.t('button.
|
|
837
|
+
title: i18next.t('button.add role')
|
|
788
838
|
});
|
|
789
839
|
}
|
|
790
840
|
/** 템플릿에서 — 이름은 이 공장이 정한 것을 그대로 보낸다. */
|
|
@@ -968,4 +1018,13 @@ function privilegeCountCell(_value, _column, record) {
|
|
|
968
1018
|
? html `<auth-badge tone="attention">${i18next.t('text.no privilege granted')}</auth-badge>`
|
|
969
1019
|
: html `<auth-badge>${count}</auth-badge>`;
|
|
970
1020
|
}
|
|
1021
|
+
/** 가진 사람 수 한 칸 — 0 은 조용히, 가진 사람이 있으면(지울 수 없으면) 보통 무게로. */
|
|
1022
|
+
function userCountCell(_value, _column, record) {
|
|
1023
|
+
const count = record?.userCount;
|
|
1024
|
+
/* 「모른다」와 「0」은 다르다 — 아직 안 온 값은 그리지 않는다. */
|
|
1025
|
+
if (count === undefined || count === null) {
|
|
1026
|
+
return html ``;
|
|
1027
|
+
}
|
|
1028
|
+
return html `<auth-badge tone=${count ? nothing : 'quiet'}>${i18next.t('text.n people', { n: count })}</auth-badge>`;
|
|
1029
|
+
}
|
|
971
1030
|
//# sourceMappingURL=role-list-page.js.map
|