@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
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** 검색·선택 상태는 Lit 화면 밖에서도 같은 규칙으로 검증한다. */
|
|
2
|
+
export type PrivilegeModuleRef = {
|
|
3
|
+
category: string;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* 모듈은 코드(category)와 사람이 보는 이름 모두로 찾는다. 관리자가 「사용자」를 입력했는데
|
|
7
|
+
* 내부 코드 `user`만 비교하면 결과가 없다고 나오는 것은 검색으로서 실패다.
|
|
8
|
+
*/
|
|
9
|
+
export declare function filterPrivilegeModules<T extends PrivilegeModuleRef>(modules: T[], keyword: string, labelOf: (category: string) => string): T[];
|
|
10
|
+
/** 검색 결과가 바뀌어 선택한 모듈이 사라지면 첫 결과를 연다. */
|
|
11
|
+
export declare function selectedVisibleModule<T extends PrivilegeModuleRef>(modules: T[], selected: string): string;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 모듈은 코드(category)와 사람이 보는 이름 모두로 찾는다. 관리자가 「사용자」를 입력했는데
|
|
3
|
+
* 내부 코드 `user`만 비교하면 결과가 없다고 나오는 것은 검색으로서 실패다.
|
|
4
|
+
*/
|
|
5
|
+
export function filterPrivilegeModules(modules, keyword, labelOf) {
|
|
6
|
+
const needle = keyword.trim().toLocaleLowerCase();
|
|
7
|
+
if (!needle)
|
|
8
|
+
return modules;
|
|
9
|
+
return modules.filter(module => [module.category, labelOf(module.category)].some(value => value.toLocaleLowerCase().includes(needle)));
|
|
10
|
+
}
|
|
11
|
+
/** 검색 결과가 바뀌어 선택한 모듈이 사라지면 첫 결과를 연다. */
|
|
12
|
+
export function selectedVisibleModule(modules, selected) {
|
|
13
|
+
return modules.some(module => module.category === selected) ? selected : modules[0]?.category || '';
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=role-privilege-picker-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-privilege-picker-state.js","sourceRoot":"","sources":["../../client/components/role-privilege-picker-state.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CACpC,OAAY,EACZ,OAAe,EACf,OAAqC;IAErC,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,iBAAiB,EAAE,CAAA;IACjD,IAAI,CAAC,MAAM;QAAE,OAAO,OAAO,CAAA;IAE3B,OAAO,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAC7B,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,iBAAiB,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CACtG,CAAA;AACH,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,qBAAqB,CAA+B,OAAY,EAAE,QAAgB;IAChG,OAAO,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,IAAI,EAAE,CAAA;AACrG,CAAC","sourcesContent":["/** 검색·선택 상태는 Lit 화면 밖에서도 같은 규칙으로 검증한다. */\nexport type PrivilegeModuleRef = { category: string }\n\n/**\n * 모듈은 코드(category)와 사람이 보는 이름 모두로 찾는다. 관리자가 「사용자」를 입력했는데\n * 내부 코드 `user`만 비교하면 결과가 없다고 나오는 것은 검색으로서 실패다.\n */\nexport function filterPrivilegeModules<T extends PrivilegeModuleRef>(\n modules: T[],\n keyword: string,\n labelOf: (category: string) => string\n): T[] {\n const needle = keyword.trim().toLocaleLowerCase()\n if (!needle) return modules\n\n return modules.filter(module =>\n [module.category, labelOf(module.category)].some(value => value.toLocaleLowerCase().includes(needle))\n )\n}\n\n/** 검색 결과가 바뀌어 선택한 모듈이 사라지면 첫 결과를 연다. */\nexport function selectedVisibleModule<T extends PrivilegeModuleRef>(modules: T[], selected: string): string {\n return modules.some(module => module.category === selected) ? selected : modules[0]?.category || ''\n}\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { filterPrivilegeModules, selectedVisibleModule } from './role-privilege-picker-state';
|
|
2
|
+
const modules = [{ category: 'user' }, { category: 'board' }, { category: 'worklist' }];
|
|
3
|
+
const labels = { user: '사용자', board: '보드', worklist: '작업지시' };
|
|
4
|
+
const labelOf = (category) => labels[category] || category;
|
|
5
|
+
describe('권한 모듈 검색', () => {
|
|
6
|
+
it('화면에 보이는 한글 모듈명으로 찾는다', () => {
|
|
7
|
+
expect(filterPrivilegeModules(modules, '사용자', labelOf)).toEqual([{ category: 'user' }]);
|
|
8
|
+
});
|
|
9
|
+
it('코드 모듈명으로도 찾는다', () => {
|
|
10
|
+
expect(filterPrivilegeModules(modules, 'work', labelOf)).toEqual([{ category: 'worklist' }]);
|
|
11
|
+
});
|
|
12
|
+
it('검색 결과에서 사라진 선택은 첫 결과로 옮긴다', () => {
|
|
13
|
+
const visible = filterPrivilegeModules(modules, '작업', labelOf);
|
|
14
|
+
expect(selectedVisibleModule(visible, 'user')).toBe('worklist');
|
|
15
|
+
});
|
|
16
|
+
it('아직 보이는 선택은 유지한다', () => {
|
|
17
|
+
expect(selectedVisibleModule(modules, 'board')).toBe('board');
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
//# sourceMappingURL=role-privilege-picker-state.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-privilege-picker-state.test.js","sourceRoot":"","sources":["../../client/components/role-privilege-picker-state.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAA;AAE7F,MAAM,OAAO,GAAG,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAA;AACvF,MAAM,MAAM,GAA2B,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAA;AACrF,MAAM,OAAO,GAAG,CAAC,QAAgB,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAA;AAElE,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;IACxB,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,MAAM,CAAC,sBAAsB,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;IACzF,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,eAAe,EAAE,GAAG,EAAE;QACvB,MAAM,CAAC,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC,CAAA;IAC9F,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,OAAO,GAAG,sBAAsB,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA;QAC9D,MAAM,CAAC,qBAAqB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IACjE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,iBAAiB,EAAE,GAAG,EAAE;QACzB,MAAM,CAAC,qBAAqB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAC/D,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["import { filterPrivilegeModules, selectedVisibleModule } from './role-privilege-picker-state'\n\nconst modules = [{ category: 'user' }, { category: 'board' }, { category: 'worklist' }]\nconst labels: Record<string, string> = { user: '사용자', board: '보드', worklist: '작업지시' }\nconst labelOf = (category: string) => labels[category] || category\n\ndescribe('권한 모듈 검색', () => {\n it('화면에 보이는 한글 모듈명으로 찾는다', () => {\n expect(filterPrivilegeModules(modules, '사용자', labelOf)).toEqual([{ category: 'user' }])\n })\n\n it('코드 모듈명으로도 찾는다', () => {\n expect(filterPrivilegeModules(modules, 'work', labelOf)).toEqual([{ category: 'worklist' }])\n })\n\n it('검색 결과에서 사라진 선택은 첫 결과로 옮긴다', () => {\n const visible = filterPrivilegeModules(modules, '작업', labelOf)\n expect(selectedVisibleModule(visible, 'user')).toBe('worklist')\n })\n\n it('아직 보이는 선택은 유지한다', () => {\n expect(selectedVisibleModule(modules, 'board')).toBe('board')\n })\n})\n"]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import '@material/web/icon/icon.js';
|
|
2
|
+
import '@material/web/checkbox/checkbox.js';
|
|
3
|
+
import '@operato/input/ox-input-search.js';
|
|
4
|
+
import { LitElement } from 'lit';
|
|
5
|
+
import type { CSSResultGroup } from 'lit';
|
|
6
|
+
/**
|
|
7
|
+
* 「할 수 있는 일 더하기」의 **그릇**.
|
|
8
|
+
*
|
|
9
|
+
* ── 왜 팝업인가 (사용자, 2026-09-11) ────────────────────────────────────────
|
|
10
|
+
* 「'할 수 있는 일 더하기' 작동을 팝업으로 넓게 할 수 있었으면 어떤가? 지금은 좁아보이고」
|
|
11
|
+
*
|
|
12
|
+
* 페이지 안에 펼치면 본문(할 수 있는 일)이 아래로 밀리고, 남은 폭에서 모듈 46개와 축 목록을
|
|
13
|
+
* 함께 봐야 한다. 고르는 동안은 **고르는 것만** 보는 것이 맞다.
|
|
14
|
+
*
|
|
15
|
+
* ── 이 부품은 아무것도 안 그린다 ────────────────────────────────────────────
|
|
16
|
+
* 모듈 접기·축 줄·유령 규칙이 전부 페이지의 상태(`modules`·`assigned`·`selected`·`keyword`)를
|
|
17
|
+
* 본다. 그것을 여기로 옮기면 **상태가 두 벌**이 되고, 그 순간 어느 쪽이 저장 범위인지
|
|
18
|
+
* 흐려진다 — 옛 편집기가 `querySelectorAll` 로 범위를 만들던 결함이 정확히 그 모양이었다.
|
|
19
|
+
*
|
|
20
|
+
* 그래서 그리기는 페이지가 하고(`renderPickerBody`), 여기는 담아서 띄우기만 한다. 대신
|
|
21
|
+
* 페이지가 토글할 때 이 부품에게 다시 그리라고 알린다(`page.picker`).
|
|
22
|
+
*
|
|
23
|
+
* ── 스타일도 한 벌이다 ──────────────────────────────────────────────────────
|
|
24
|
+
* 팝업은 페이지의 shadow root **밖**에 뜨므로 페이지 스타일이 여기까지 안 온다. 베껴 두면
|
|
25
|
+
* 한쪽만 고쳐지는 날이 오고, 오늘 이 화면에서 그 부류를 이미 만났다 — 칩 시절 CSS 가 남아
|
|
26
|
+
* 새 규칙을 이겼다. 그래서 페이지의 `styles` 를 그대로 가져간다.
|
|
27
|
+
*
|
|
28
|
+
* 페이지가 이 파일을 **동적으로** 들이기 때문에 이 import 가 순환이 되지 않는다.
|
|
29
|
+
*/
|
|
30
|
+
export declare class RolePrivilegePicker extends LitElement {
|
|
31
|
+
static styles: CSSResultGroup;
|
|
32
|
+
/** 담는 페이지. 이 부품은 그것의 `renderPickerBody()` 를 그린다. */
|
|
33
|
+
page?: any;
|
|
34
|
+
private searchInput?;
|
|
35
|
+
private readonly onSearchInput;
|
|
36
|
+
connectedCallback(): void;
|
|
37
|
+
disconnectedCallback(): void;
|
|
38
|
+
render(): any;
|
|
39
|
+
updated(): void;
|
|
40
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { __decorate, __metadata } from "tslib";
|
|
2
|
+
import '@material/web/icon/icon.js';
|
|
3
|
+
import '@material/web/checkbox/checkbox.js';
|
|
4
|
+
import '@operato/input/ox-input-search.js';
|
|
5
|
+
import { html, LitElement } from 'lit';
|
|
6
|
+
import { customElement, property } from 'lit/decorators.js';
|
|
7
|
+
import { RolePrivilegePage } from '../pages/role/role-privilege-page.js';
|
|
8
|
+
/**
|
|
9
|
+
* 「할 수 있는 일 더하기」의 **그릇**.
|
|
10
|
+
*
|
|
11
|
+
* ── 왜 팝업인가 (사용자, 2026-09-11) ────────────────────────────────────────
|
|
12
|
+
* 「'할 수 있는 일 더하기' 작동을 팝업으로 넓게 할 수 있었으면 어떤가? 지금은 좁아보이고」
|
|
13
|
+
*
|
|
14
|
+
* 페이지 안에 펼치면 본문(할 수 있는 일)이 아래로 밀리고, 남은 폭에서 모듈 46개와 축 목록을
|
|
15
|
+
* 함께 봐야 한다. 고르는 동안은 **고르는 것만** 보는 것이 맞다.
|
|
16
|
+
*
|
|
17
|
+
* ── 이 부품은 아무것도 안 그린다 ────────────────────────────────────────────
|
|
18
|
+
* 모듈 접기·축 줄·유령 규칙이 전부 페이지의 상태(`modules`·`assigned`·`selected`·`keyword`)를
|
|
19
|
+
* 본다. 그것을 여기로 옮기면 **상태가 두 벌**이 되고, 그 순간 어느 쪽이 저장 범위인지
|
|
20
|
+
* 흐려진다 — 옛 편집기가 `querySelectorAll` 로 범위를 만들던 결함이 정확히 그 모양이었다.
|
|
21
|
+
*
|
|
22
|
+
* 그래서 그리기는 페이지가 하고(`renderPickerBody`), 여기는 담아서 띄우기만 한다. 대신
|
|
23
|
+
* 페이지가 토글할 때 이 부품에게 다시 그리라고 알린다(`page.picker`).
|
|
24
|
+
*
|
|
25
|
+
* ── 스타일도 한 벌이다 ──────────────────────────────────────────────────────
|
|
26
|
+
* 팝업은 페이지의 shadow root **밖**에 뜨므로 페이지 스타일이 여기까지 안 온다. 베껴 두면
|
|
27
|
+
* 한쪽만 고쳐지는 날이 오고, 오늘 이 화면에서 그 부류를 이미 만났다 — 칩 시절 CSS 가 남아
|
|
28
|
+
* 새 규칙을 이겼다. 그래서 페이지의 `styles` 를 그대로 가져간다.
|
|
29
|
+
*
|
|
30
|
+
* 페이지가 이 파일을 **동적으로** 들이기 때문에 이 import 가 순환이 되지 않는다.
|
|
31
|
+
*/
|
|
32
|
+
let RolePrivilegePicker = class RolePrivilegePicker extends LitElement {
|
|
33
|
+
constructor() {
|
|
34
|
+
super(...arguments);
|
|
35
|
+
this.onSearchInput = (event) => {
|
|
36
|
+
/* ox-input-search 는 값을 확정할 때만 host `change`를 낸다. 모듈을 찾는 일은 입력 중에도
|
|
37
|
+
결과가 바뀌어야 하므로, 이 팝업의 shadow root 안 native input을 직접 듣는다. */
|
|
38
|
+
this.page?.updateKeyword?.(event);
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
static { this.styles = RolePrivilegePage.styles; }
|
|
42
|
+
connectedCallback() {
|
|
43
|
+
super.connectedCallback();
|
|
44
|
+
if (this.page) {
|
|
45
|
+
this.page.picker = this;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
disconnectedCallback() {
|
|
49
|
+
this.searchInput?.removeEventListener('input', this.onSearchInput);
|
|
50
|
+
this.searchInput = undefined;
|
|
51
|
+
super.disconnectedCallback();
|
|
52
|
+
if (this.page?.picker === this) {
|
|
53
|
+
this.page.picker = undefined;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
render() {
|
|
57
|
+
return this.page?.renderPickerBody?.() ?? html ``;
|
|
58
|
+
}
|
|
59
|
+
updated() {
|
|
60
|
+
const nextSearchInput = this.renderRoot.querySelector('ox-input-search')?.shadowRoot?.querySelector('input');
|
|
61
|
+
if (nextSearchInput === this.searchInput) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
this.searchInput?.removeEventListener('input', this.onSearchInput);
|
|
65
|
+
this.searchInput = nextSearchInput;
|
|
66
|
+
this.searchInput?.addEventListener('input', this.onSearchInput);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
__decorate([
|
|
70
|
+
property({ type: Object }),
|
|
71
|
+
__metadata("design:type", Object)
|
|
72
|
+
], RolePrivilegePicker.prototype, "page", void 0);
|
|
73
|
+
RolePrivilegePicker = __decorate([
|
|
74
|
+
customElement('role-privilege-picker')
|
|
75
|
+
], RolePrivilegePicker);
|
|
76
|
+
export { RolePrivilegePicker };
|
|
77
|
+
//# sourceMappingURL=role-privilege-picker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role-privilege-picker.js","sourceRoot":"","sources":["../../client/components/role-privilege-picker.ts"],"names":[],"mappings":";AAAA,OAAO,4BAA4B,CAAA;AACnC,OAAO,oCAAoC,CAAA;AAC3C,OAAO,mCAAmC,CAAA;AAE1C,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AAEtC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAE3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAA;AAExE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEI,IAAM,mBAAmB,GAAzB,MAAM,mBAAoB,SAAQ,UAAU;IAA5C;;QAQY,kBAAa,GAAG,CAAC,KAAY,EAAE,EAAE;YAChD;yEAC6D;YAC7D,IAAI,CAAC,IAAI,EAAE,aAAa,EAAE,CAAC,KAAK,CAAC,CAAA;QACnC,CAAC,CAAA;IAmCH,CAAC;aA9CQ,WAAM,GAAmB,iBAAiB,CAAC,MAAwB,AAA7D,CAA6D;IAa1E,iBAAiB;QACf,KAAK,CAAC,iBAAiB,EAAE,CAAA;QACzB,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAA;QACzB,CAAC;IACH,CAAC;IAED,oBAAoB;QAClB,IAAI,CAAC,WAAW,EAAE,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,aAAa,CAAC,CAAA;QAClE,IAAI,CAAC,WAAW,GAAG,SAAS,CAAA;QAC5B,KAAK,CAAC,oBAAoB,EAAE,CAAA;QAC5B,IAAI,IAAI,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,EAAE,CAAC;YAC/B,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,SAAS,CAAA;QAC9B,CAAC;IACH,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAC,IAAI,EAAE,gBAAgB,EAAE,EAAE,IAAI,IAAI,CAAA,EAAE,CAAA;IAClD,CAAC;IAED,OAAO;QACL,MAAM,eAAe,GAAI,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,iBAAiB,CAAS,EAAE,UAAU,EAAE,aAAa,CAC1G,OAAO,CACwB,CAAA;QAEjC,IAAI,eAAe,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC;YACzC,OAAM;QACR,CAAC;QAED,IAAI,CAAC,WAAW,EAAE,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,aAAa,CAAC,CAAA;QAClE,IAAI,CAAC,WAAW,GAAG,eAAe,CAAA;QAClC,IAAI,CAAC,WAAW,EAAE,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,aAAa,CAAC,CAAA;IACjE,CAAC;;AA1C2B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;iDAAW;AAJ3B,mBAAmB;IAD/B,aAAa,CAAC,uBAAuB,CAAC;GAC1B,mBAAmB,CA+C/B","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@material/web/checkbox/checkbox.js'\nimport '@operato/input/ox-input-search.js'\n\nimport { html, LitElement } from 'lit'\nimport type { CSSResultGroup } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { RolePrivilegePage } from '../pages/role/role-privilege-page.js'\n\n/**\n * 「할 수 있는 일 더하기」의 **그릇**.\n *\n * ── 왜 팝업인가 (사용자, 2026-09-11) ────────────────────────────────────────\n * 「'할 수 있는 일 더하기' 작동을 팝업으로 넓게 할 수 있었으면 어떤가? 지금은 좁아보이고」\n *\n * 페이지 안에 펼치면 본문(할 수 있는 일)이 아래로 밀리고, 남은 폭에서 모듈 46개와 축 목록을\n * 함께 봐야 한다. 고르는 동안은 **고르는 것만** 보는 것이 맞다.\n *\n * ── 이 부품은 아무것도 안 그린다 ────────────────────────────────────────────\n * 모듈 접기·축 줄·유령 규칙이 전부 페이지의 상태(`modules`·`assigned`·`selected`·`keyword`)를\n * 본다. 그것을 여기로 옮기면 **상태가 두 벌**이 되고, 그 순간 어느 쪽이 저장 범위인지\n * 흐려진다 — 옛 편집기가 `querySelectorAll` 로 범위를 만들던 결함이 정확히 그 모양이었다.\n *\n * 그래서 그리기는 페이지가 하고(`renderPickerBody`), 여기는 담아서 띄우기만 한다. 대신\n * 페이지가 토글할 때 이 부품에게 다시 그리라고 알린다(`page.picker`).\n *\n * ── 스타일도 한 벌이다 ──────────────────────────────────────────────────────\n * 팝업은 페이지의 shadow root **밖**에 뜨므로 페이지 스타일이 여기까지 안 온다. 베껴 두면\n * 한쪽만 고쳐지는 날이 오고, 오늘 이 화면에서 그 부류를 이미 만났다 — 칩 시절 CSS 가 남아\n * 새 규칙을 이겼다. 그래서 페이지의 `styles` 를 그대로 가져간다.\n *\n * 페이지가 이 파일을 **동적으로** 들이기 때문에 이 import 가 순환이 되지 않는다.\n */\n@customElement('role-privilege-picker')\nexport class RolePrivilegePicker extends LitElement {\n static styles: CSSResultGroup = RolePrivilegePage.styles as CSSResultGroup\n\n /** 담는 페이지. 이 부품은 그것의 `renderPickerBody()` 를 그린다. */\n @property({ type: Object }) page?: any\n\n private searchInput?: HTMLInputElement\n\n private readonly onSearchInput = (event: Event) => {\n /* ox-input-search 는 값을 확정할 때만 host `change`를 낸다. 모듈을 찾는 일은 입력 중에도\n 결과가 바뀌어야 하므로, 이 팝업의 shadow root 안 native input을 직접 듣는다. */\n this.page?.updateKeyword?.(event)\n }\n\n connectedCallback() {\n super.connectedCallback()\n if (this.page) {\n this.page.picker = this\n }\n }\n\n disconnectedCallback() {\n this.searchInput?.removeEventListener('input', this.onSearchInput)\n this.searchInput = undefined\n super.disconnectedCallback()\n if (this.page?.picker === this) {\n this.page.picker = undefined\n }\n }\n\n render() {\n return this.page?.renderPickerBody?.() ?? html``\n }\n\n updated() {\n const nextSearchInput = (this.renderRoot.querySelector('ox-input-search') as any)?.shadowRoot?.querySelector(\n 'input'\n ) as HTMLInputElement | undefined\n\n if (nextSearchInput === this.searchInput) {\n return\n }\n\n this.searchInput?.removeEventListener('input', this.onSearchInput)\n this.searchInput = nextSearchInput\n this.searchInput?.addEventListener('input', this.onSearchInput)\n }\n}\n"]}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import '@material/web/icon/icon.js';
|
|
2
|
+
import { LitElement } from 'lit';
|
|
3
|
+
import type { CSSResultGroup } from 'lit';
|
|
4
|
+
declare const RoleTemplatePicker_base: (new (...args: any[]) => LitElement) & typeof LitElement;
|
|
5
|
+
/**
|
|
6
|
+
* Starting a role.
|
|
7
|
+
*
|
|
8
|
+
* ── Why this stands where the blank row used to ──────────────────────────────
|
|
9
|
+
* A new plant has no roles, and the old answer to "make one" was an empty line waiting for
|
|
10
|
+
* a name. Naming it is the easy half; the hard half is which of 84 privileges belong to a
|
|
11
|
+
* quality inspector, and that is a judgement the product has made and the plant has not.
|
|
12
|
+
*
|
|
13
|
+
* miratek is the case: two roles (Administrator, TWIN-CONNECTOR), no MES seats, and eleven
|
|
14
|
+
* approval doors standing empty because there is nothing to route them to. Read from
|
|
15
|
+
* inside, that looks like a plant that has not decided. It is a plant with nothing to
|
|
16
|
+
* decide from.
|
|
17
|
+
*
|
|
18
|
+
* ── Both doors, in one place ─────────────────────────────────────────────────
|
|
19
|
+
* The templates come first and the blank name last, because the order is what teaches: a
|
|
20
|
+
* plant that sees "quality inspector, 7 privileges" learns what a seat looks like before
|
|
21
|
+
* it is asked to invent one. Hiding the blank door would be worse, though — the seats a
|
|
22
|
+
* factory actually has ("night shift lead", "outside inspector") are its own, and the
|
|
23
|
+
* product does not know them.
|
|
24
|
+
*
|
|
25
|
+
* ── Why the name is editable ─────────────────────────────────────────────────
|
|
26
|
+
* The template knows the seat exists; it does not know what the people there call it. And
|
|
27
|
+
* because a role is also what an approval line points at, the same seat is sometimes
|
|
28
|
+
* needed twice — a day and a night inspector hold the same privileges and take different
|
|
29
|
+
* approvals. A fixed name would allow each template exactly once per domain.
|
|
30
|
+
*/
|
|
31
|
+
export declare class RoleTemplatePicker extends RoleTemplatePicker_base {
|
|
32
|
+
static styles: CSSResultGroup;
|
|
33
|
+
page?: any;
|
|
34
|
+
private templates;
|
|
35
|
+
private selected?;
|
|
36
|
+
/** `undefined` means "the blank door" — name the seat yourself. */
|
|
37
|
+
private scratch;
|
|
38
|
+
private name;
|
|
39
|
+
private description;
|
|
40
|
+
connectedCallback(): Promise<void>;
|
|
41
|
+
private fetchTemplates;
|
|
42
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
43
|
+
/**
|
|
44
|
+
* What the seat opens, in one line — **counted, not interpreted.**
|
|
45
|
+
*
|
|
46
|
+
* This briefly said "changes 6 · only sees 8", which read well and was not something the
|
|
47
|
+
* data supports: the privilege axis is an unconstrained string and its name does not
|
|
48
|
+
* determine what the resolver behind it does. Counting is safe; grouping by meaning was
|
|
49
|
+
* not. See `renderOpens`.
|
|
50
|
+
*
|
|
51
|
+
* "0 privileges" is still two different facts and must not be merged: granting nothing
|
|
52
|
+
* on purpose is an approval seat, and a template whose grants are all missing here is a
|
|
53
|
+
* defect (`missingCount`).
|
|
54
|
+
*/
|
|
55
|
+
private summarize;
|
|
56
|
+
private renderDetail;
|
|
57
|
+
/**
|
|
58
|
+
* 이 자리가 무엇을 여나 — **선언된 것만.**
|
|
59
|
+
*
|
|
60
|
+
* ⚠ 여기 「바꿀 수 있는 것 / 볼 수만 있는 것」 두 묶음이 있었다. 사용자가 물었다(2026-09-11):
|
|
61
|
+
* 「바꿀 수 있고, 볼 수만 있는 것을 너가 어떻게 알아? 논리적이지 않은 것을 우겨넣지 말고.」
|
|
62
|
+
*
|
|
63
|
+
* 몰랐다. 지어낸 것이다. 근거가 없는 이유가 둘이다.
|
|
64
|
+
*
|
|
65
|
+
* ① `@privilege(privilege:)` 는 **제약 없는 문자열**이다(지시자 선언이 `privilege: String`
|
|
66
|
+
* 이고 enum 이 아니다). 지금 쓰인 값이 mutation 484 · query 314 로 둘뿐인 것은 **오늘
|
|
67
|
+
* 그렇게 쓴 것**이지 둘로 한정된 것이 아니다. 내 코드는 `axis !== 'query'` 로 갈랐으므로,
|
|
68
|
+
* 처음 보는 값은 **알아서가 아니라 query 가 아니라서** 「바꿀 수 있는 것」으로 떨어졌다.
|
|
69
|
+
* ② 이름이 뜻을 보장하지 않는다. 축 이름과 실제 GraphQL 연산이 어긋나는 자리가 6곳이고,
|
|
70
|
+
* 그중 둘은 이 기능의 내 파일이다(`role-template-query.ts` — 템플릿을 **읽는** 질의에
|
|
71
|
+
* `privilege: "mutation"` 을 달았다. 역할을 만드는 문이라서 그게 맞다). 축은 연산 종류가
|
|
72
|
+
* 아니라 **어느 문을 지나야 하나**의 이름이다.
|
|
73
|
+
*
|
|
74
|
+
* 그래서 화면은 선언된 것을 그대로 낸다 — 어느 모듈에, 어느 권한이. 읽히게 만드는 것
|
|
75
|
+
* (굵기·줄 간격·가운뎃점)은 아무것도 주장하지 않으므로 그대로 둔다.
|
|
76
|
+
*/
|
|
77
|
+
private renderOpens;
|
|
78
|
+
private pick;
|
|
79
|
+
private pickScratch;
|
|
80
|
+
private canCreate;
|
|
81
|
+
private create;
|
|
82
|
+
}
|
|
83
|
+
export {};
|