@things-factory/auth-ui 10.0.0-beta.6 → 10.0.0-beta.67

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.
@@ -0,0 +1,259 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import '@material/web/icon/icon.js';
3
+ import '@material/web/button/elevated-button.js';
4
+ import '@operato/data-grist/ox-grist.js';
5
+ import '@operato/context/ox-context-page-toolbar.js';
6
+ import '../../components/add-domain-owner-popup.js';
7
+ import gql from 'graphql-tag';
8
+ import { css, html } from 'lit';
9
+ import { customElement, property, query, state } from 'lit/decorators.js';
10
+ import { DataGrist } from '@operato/data-grist/ox-grist.js';
11
+ import { client, gqlContext } from '@operato/graphql';
12
+ import { i18next, localize } from '@operato/i18n';
13
+ import { openPopup } from '@operato/layout';
14
+ import { PageView, store } from '@operato/shell';
15
+ import { OxPrompt } from '@operato/popup/ox-prompt.js';
16
+ import { CommonHeaderStyles, CommonGristStyles, ScrollbarStyles } from '@operato/styles';
17
+ import { isMobileDevice } from '@operato/utils';
18
+ import { p13n } from '@operato/p13n';
19
+ /**
20
+ * Domain Owners Management Page
21
+ *
22
+ * 현재 도메인의 owner 목록을 표시하고, 추가/제거를 관리한다.
23
+ * 멀티 오너 지원 — 모든 owner는 동등한 권한을 가짐 (primary 없음).
24
+ * Domain.owner legacy cache 필드는 대표 owner 표시용으로 유지됨.
25
+ */
26
+ let DomainOwnerManagement = class DomainOwnerManagement extends p13n(localize(i18next)(PageView)) {
27
+ constructor() {
28
+ super(...arguments);
29
+ this.active = false;
30
+ this.mode = isMobileDevice() ? 'CARD' : 'GRID';
31
+ this.isDomainOwner = false;
32
+ }
33
+ static { this.styles = [
34
+ CommonGristStyles,
35
+ CommonHeaderStyles,
36
+ ScrollbarStyles,
37
+ css `
38
+ :host {
39
+ display: flex;
40
+ flex-direction: column;
41
+ overflow: hidden;
42
+ }
43
+ ox-grist {
44
+ overflow-y: auto;
45
+ flex: 1;
46
+ }
47
+ `
48
+ ]; }
49
+ get context() {
50
+ return {
51
+ title: i18next.t('text.domain owners'),
52
+ help: 'auth/domain-owners',
53
+ // 서버의 @privilege 가 최종 게이트. 버튼 표시/숨김은 isDomainOwner
54
+ // 클라이언트 플래그에 의존하지 않음 — auth profile 이 해당 플래그를
55
+ // 제대로 세팅 안 했을 때 버튼이 사라져 조작 불가 상태가 되는 것을 방지.
56
+ actions: [
57
+ {
58
+ title: i18next.t('button.add'),
59
+ action: this._onAddOwner.bind(this),
60
+ icon: 'person_add'
61
+ }
62
+ ],
63
+ toolbar: false
64
+ };
65
+ }
66
+ render() {
67
+ return html `
68
+ <ox-grist
69
+ .mode=${this.mode}
70
+ .config=${this.config}
71
+ .fetchHandler=${this._fetchHandler.bind(this)}
72
+ .personalConfigProvider=${this.getPagePreferenceProvider('ox-grist')}
73
+ >
74
+ <div slot="headroom" class="header">
75
+ <ox-context-page-toolbar class="actions" .context=${this.context}></ox-context-page-toolbar>
76
+ </div>
77
+ </ox-grist>
78
+ `;
79
+ }
80
+ async pageInitialized() {
81
+ this.config = {
82
+ rows: { appendable: false, selectable: { multiple: false } },
83
+ columns: [
84
+ { type: 'gutter', gutterName: 'sequence' },
85
+ {
86
+ type: 'gutter',
87
+ gutterName: 'button',
88
+ icon: 'person_remove',
89
+ title: i18next.t('button.remove owner'),
90
+ handlers: {
91
+ click: (_columns, _data, _column, record, _rowIndex) => {
92
+ // 클라이언트 게이트 없음 — 서버의 @privilege(domainOwnerGranted:true)
93
+ // 가 최종 authoritative. 오너 아니면 서버 에러를 프롬프트로 표시.
94
+ if (record?.user) {
95
+ this._onRemoveOwner(record);
96
+ }
97
+ }
98
+ }
99
+ },
100
+ {
101
+ type: 'string',
102
+ name: 'userName',
103
+ header: i18next.t('field.name'),
104
+ record: { editable: false },
105
+ width: 180
106
+ },
107
+ {
108
+ type: 'string',
109
+ name: 'userEmail',
110
+ header: i18next.t('field.email'),
111
+ record: { editable: false },
112
+ width: 220
113
+ },
114
+ {
115
+ type: 'string',
116
+ name: 'reason',
117
+ header: i18next.t('field.reason'),
118
+ record: { editable: false },
119
+ width: 220
120
+ },
121
+ {
122
+ type: 'string',
123
+ name: 'grantedByName',
124
+ header: i18next.t('field.granted by'),
125
+ record: { editable: false },
126
+ width: 160
127
+ },
128
+ {
129
+ type: 'datetime',
130
+ name: 'grantedAt',
131
+ header: i18next.t('field.granted at'),
132
+ width: 180
133
+ }
134
+ ]
135
+ };
136
+ }
137
+ async pageUpdated(_changes, _lifecycle) {
138
+ if (this.active) {
139
+ this._refreshPermissions();
140
+ await this.updateComplete;
141
+ this.grist.fetch();
142
+ }
143
+ }
144
+ _refreshPermissions() {
145
+ const __state__ = store.getState();
146
+ const me = __state__.auth?.user || {};
147
+ this.isDomainOwner = !!me.domainOwner || !!me.super;
148
+ }
149
+ async _fetchHandler() {
150
+ const response = await client.query({
151
+ query: gql `
152
+ query {
153
+ domainOwners {
154
+ id
155
+ user {
156
+ id
157
+ name
158
+ email
159
+ username
160
+ }
161
+ grantedBy {
162
+ id
163
+ name
164
+ email
165
+ }
166
+ grantedAt
167
+ reason
168
+ }
169
+ }
170
+ `,
171
+ context: gqlContext(),
172
+ fetchPolicy: 'network-only'
173
+ });
174
+ const items = (response.data?.domainOwners || []).map((ow) => ({
175
+ ...ow,
176
+ userName: ow.user?.name,
177
+ userEmail: ow.user?.email,
178
+ grantedByName: ow.grantedBy?.name || ''
179
+ }));
180
+ return { total: items.length, records: items };
181
+ }
182
+ _onAddOwner() {
183
+ openPopup(html `
184
+ <add-domain-owner-popup
185
+ @domain-owner-added=${() => {
186
+ this.grist.fetch();
187
+ this._notify(i18next.t('text.domain owner added successfully'));
188
+ }}
189
+ ></add-domain-owner-popup>
190
+ `, {
191
+ size: 'small',
192
+ title: i18next.t('title.add domain owner')
193
+ });
194
+ }
195
+ async _onRemoveOwner(record) {
196
+ const username = record.user?.username || record.user?.email;
197
+ const displayName = record.user?.name || username;
198
+ const confirmed = await OxPrompt.open({
199
+ type: 'warning',
200
+ title: i18next.t('text.are_you_sure'),
201
+ text: i18next.t('text.are_you_sure_to_remove_domain_owner', { x: displayName }),
202
+ confirmButton: { text: i18next.t('button.remove owner') },
203
+ cancelButton: { text: i18next.t('button.cancel') }
204
+ });
205
+ if (!confirmed)
206
+ return;
207
+ try {
208
+ const response = await client.mutate({
209
+ mutation: gql `
210
+ mutation removeDomainOwner($username: String!) {
211
+ removeDomainOwner(username: $username)
212
+ }
213
+ `,
214
+ variables: { username },
215
+ context: gqlContext()
216
+ });
217
+ if (!response.errors?.length) {
218
+ this._notify(i18next.t('text.domain owner removed successfully'));
219
+ this.grist.fetch();
220
+ }
221
+ }
222
+ catch (err) {
223
+ await OxPrompt.open({
224
+ type: 'error',
225
+ title: i18next.t('text.failed'),
226
+ text: err?.message || String(err),
227
+ confirmButton: { text: i18next.t('button.confirm') }
228
+ });
229
+ }
230
+ }
231
+ _notify(message) {
232
+ document.dispatchEvent(new CustomEvent('notify', { detail: { message, option: { timer: 1500 } } }));
233
+ }
234
+ };
235
+ __decorate([
236
+ property({ type: Boolean }),
237
+ __metadata("design:type", Boolean)
238
+ ], DomainOwnerManagement.prototype, "active", void 0);
239
+ __decorate([
240
+ property({ type: Object }),
241
+ __metadata("design:type", Object)
242
+ ], DomainOwnerManagement.prototype, "config", void 0);
243
+ __decorate([
244
+ property({ type: String }),
245
+ __metadata("design:type", String)
246
+ ], DomainOwnerManagement.prototype, "mode", void 0);
247
+ __decorate([
248
+ state(),
249
+ __metadata("design:type", Boolean)
250
+ ], DomainOwnerManagement.prototype, "isDomainOwner", void 0);
251
+ __decorate([
252
+ query('ox-grist'),
253
+ __metadata("design:type", DataGrist)
254
+ ], DomainOwnerManagement.prototype, "grist", void 0);
255
+ DomainOwnerManagement = __decorate([
256
+ customElement('domain-owner-management')
257
+ ], DomainOwnerManagement);
258
+ export { DomainOwnerManagement };
259
+ //# sourceMappingURL=domain-owner-management.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain-owner-management.js","sourceRoot":"","sources":["../../../client/pages/domain-owner/domain-owner-management.ts"],"names":[],"mappings":";AAAA,OAAO,4BAA4B,CAAA;AACnC,OAAO,yCAAyC,CAAA;AAEhD,OAAO,iCAAiC,CAAA;AACxC,OAAO,6CAA6C,CAAA;AAEpD,OAAO,4CAA4C,CAAA;AAEnD,OAAO,GAAG,MAAM,aAAa,CAAA;AAC7B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAC/B,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAEzE,OAAO,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAA;AAC3D,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAA;AACtD,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACxF,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AAEpC;;;;;;GAMG;AAEI,IAAM,qBAAqB,GAA3B,MAAM,qBAAsB,SAAQ,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC;IAArE;;QAkBwB,WAAM,GAAY,KAAK,CAAA;QAExB,SAAI,GAA6B,cAAc,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAA;QAE9E,kBAAa,GAAY,KAAK,CAAA;IA2MjD,CAAC;aAhOQ,WAAM,GAAG;QACd,iBAAiB;QACjB,kBAAkB;QAClB,eAAe;QACf,GAAG,CAAA;;;;;;;;;;KAUF;KACF,AAfY,CAeZ;IAUD,IAAI,OAAO;QACT,OAAO;YACL,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC;YACtC,IAAI,EAAE,oBAAoB;YAC1B,mDAAmD;YACnD,8CAA8C;YAC9C,4CAA4C;YAC5C,OAAO,EAAE;gBACP;oBACE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC;oBAC9B,MAAM,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC;oBACnC,IAAI,EAAE,YAAY;iBACnB;aACF;YACD,OAAO,EAAE,KAAK;SACf,CAAA;IACH,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAA;;gBAEC,IAAI,CAAC,IAAI;kBACP,IAAI,CAAC,MAAM;wBACL,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC;kCACnB,IAAI,CAAC,yBAAyB,CAAC,UAAU,CAAE;;;8DAGf,IAAI,CAAC,OAAO;;;KAGrE,CAAA;IACH,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,IAAI,CAAC,MAAM,GAAG;YACZ,IAAI,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE;YAC5D,OAAO,EAAE;gBACP,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE;gBAC1C;oBACE,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE,QAAQ;oBACpB,IAAI,EAAE,eAAe;oBACrB,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,qBAAqB,CAAC;oBACvC,QAAQ,EAAE;wBACR,KAAK,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE;4BACrD,yDAAyD;4BACzD,8CAA8C;4BAC9C,IAAI,MAAM,EAAE,IAAI,EAAE,CAAC;gCACjB,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAA;4BAC7B,CAAC;wBACH,CAAC;qBACF;iBACF;gBACD;oBACE,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,UAAU;oBAChB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC;oBAC/B,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;oBAC3B,KAAK,EAAE,GAAG;iBACX;gBACD;oBACE,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,WAAW;oBACjB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC;oBAChC,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;oBAC3B,KAAK,EAAE,GAAG;iBACX;gBACD;oBACE,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,QAAQ;oBACd,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC;oBACjC,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;oBAC3B,KAAK,EAAE,GAAG;iBACX;gBACD;oBACE,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,eAAe;oBACrB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,kBAAkB,CAAC;oBACrC,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;oBAC3B,KAAK,EAAE,GAAG;iBACX;gBACD;oBACE,IAAI,EAAE,UAAU;oBAChB,IAAI,EAAE,WAAW;oBACjB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,kBAAkB,CAAC;oBACrC,KAAK,EAAE,GAAG;iBACX;aACF;SACF,CAAA;IACH,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,QAAa,EAAE,UAAe;QAC9C,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,CAAC,mBAAmB,EAAE,CAAA;YAC1B,MAAM,IAAI,CAAC,cAAc,CAAA;YACzB,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;QACpB,CAAC;IACH,CAAC;IAEO,mBAAmB;QACzB,MAAM,SAAS,GAAG,KAAK,CAAC,QAAQ,EAAS,CAAA;QACzC,MAAM,EAAE,GAAG,SAAS,CAAC,IAAI,EAAE,IAAI,IAAI,EAAE,CAAA;QACrC,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC,EAAE,CAAC,WAAW,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAA;IACrD,CAAC;IAEO,KAAK,CAAC,aAAa;QACzB,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC;YAClC,KAAK,EAAE,GAAG,CAAA;;;;;;;;;;;;;;;;;;;OAmBT;YACD,OAAO,EAAE,UAAU,EAAE;YACrB,WAAW,EAAE,cAAc;SAC5B,CAAC,CAAA;QAEF,MAAM,KAAK,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,YAAY,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAO,EAAE,EAAE,CAAC,CAAC;YAClE,GAAG,EAAE;YACL,QAAQ,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI;YACvB,SAAS,EAAE,EAAE,CAAC,IAAI,EAAE,KAAK;YACzB,aAAa,EAAE,EAAE,CAAC,SAAS,EAAE,IAAI,IAAI,EAAE;SACxC,CAAC,CAAC,CAAA;QAEH,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAA;IAChD,CAAC;IAEO,WAAW;QACjB,SAAS,CACP,IAAI,CAAA;;gCAEsB,GAAG,EAAE;YACzB,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;YAClB,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,sCAAsC,CAAC,CAAC,CAAA;QACjE,CAAC;;OAEJ,EACD;YACE,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,wBAAwB,CAAC;SAC3C,CACF,CAAA;IACH,CAAC;IAEO,KAAK,CAAC,cAAc,CAAC,MAAW;QACtC,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,EAAE,QAAQ,IAAI,MAAM,CAAC,IAAI,EAAE,KAAK,CAAA;QAC5D,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,EAAE,IAAI,IAAI,QAAQ,CAAA;QAEjD,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC;YACpC,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC;YACrC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,0CAA0C,EAAE,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC;YAC/E,aAAa,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,qBAAqB,CAAC,EAAE;YACzD,YAAY,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,EAAE;SACnD,CAAC,CAAA;QACF,IAAI,CAAC,SAAS;YAAE,OAAM;QAEtB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC;gBACnC,QAAQ,EAAE,GAAG,CAAA;;;;SAIZ;gBACD,SAAS,EAAE,EAAE,QAAQ,EAAE;gBACvB,OAAO,EAAE,UAAU,EAAE;aACtB,CAAC,CAAA;YAEF,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;gBAC7B,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,wCAAwC,CAAC,CAAC,CAAA;gBACjE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;YACpB,CAAC;QACH,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,MAAM,QAAQ,CAAC,IAAI,CAAC;gBAClB,IAAI,EAAE,OAAO;gBACb,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC;gBAC/B,IAAI,EAAE,GAAG,EAAE,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC;gBACjC,aAAa,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC,EAAE;aACrD,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAEO,OAAO,CAAC,OAAe;QAC7B,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;;AA9M4B;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;;qDAAwB;AACxB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;qDAAY;AACX;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;mDAAoE;AAE9E;IAAhB,KAAK,EAAE;;4DAAuC;AAEpB;IAA1B,KAAK,CAAC,UAAU,CAAC;8BAAiB,SAAS;oDAAA;AAxBjC,qBAAqB;IADjC,aAAa,CAAC,yBAAyB,CAAC;GAC5B,qBAAqB,CAiOjC","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@material/web/button/elevated-button.js'\n\nimport '@operato/data-grist/ox-grist.js'\nimport '@operato/context/ox-context-page-toolbar.js'\n\nimport '../../components/add-domain-owner-popup.js'\n\nimport gql from 'graphql-tag'\nimport { css, html } from 'lit'\nimport { customElement, property, query, state } from 'lit/decorators.js'\n\nimport { DataGrist } from '@operato/data-grist/ox-grist.js'\nimport { client, gqlContext } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { openPopup } from '@operato/layout'\nimport { PageView, store } from '@operato/shell'\nimport { OxPrompt } from '@operato/popup/ox-prompt.js'\nimport { CommonHeaderStyles, CommonGristStyles, ScrollbarStyles } from '@operato/styles'\nimport { isMobileDevice } from '@operato/utils'\nimport { p13n } from '@operato/p13n'\n\n/**\n * Domain Owners Management Page\n *\n * 현재 도메인의 owner 목록을 표시하고, 추가/제거를 관리한다.\n * 멀티 오너 지원 — 모든 owner는 동등한 권한을 가짐 (primary 없음).\n * Domain.owner legacy cache 필드는 대표 owner 표시용으로 유지됨.\n */\n@customElement('domain-owner-management')\nexport class DomainOwnerManagement extends p13n(localize(i18next)(PageView)) {\n static styles = [\n CommonGristStyles,\n CommonHeaderStyles,\n ScrollbarStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n ox-grist {\n overflow-y: auto;\n flex: 1;\n }\n `\n ]\n\n @property({ type: Boolean }) active: boolean = false\n @property({ type: Object }) config: any\n @property({ type: String }) mode: 'CARD' | 'LIST' | 'GRID' = isMobileDevice() ? 'CARD' : 'GRID'\n\n @state() private isDomainOwner: boolean = false\n\n @query('ox-grist') private grist!: DataGrist\n\n get context() {\n return {\n title: i18next.t('text.domain owners'),\n help: 'auth/domain-owners',\n // 서버의 @privilege 가 최종 게이트. 버튼 표시/숨김은 isDomainOwner\n // 클라이언트 플래그에 의존하지 않음 — auth profile 이 해당 플래그를\n // 제대로 세팅 안 했을 때 버튼이 사라져 조작 불가 상태가 되는 것을 방지.\n actions: [\n {\n title: i18next.t('button.add'),\n action: this._onAddOwner.bind(this),\n icon: 'person_add'\n }\n ],\n toolbar: false\n }\n }\n\n render() {\n return html`\n <ox-grist\n .mode=${this.mode}\n .config=${this.config}\n .fetchHandler=${this._fetchHandler.bind(this)}\n .personalConfigProvider=${this.getPagePreferenceProvider('ox-grist')!}\n >\n <div slot=\"headroom\" class=\"header\">\n <ox-context-page-toolbar class=\"actions\" .context=${this.context}></ox-context-page-toolbar>\n </div>\n </ox-grist>\n `\n }\n\n async pageInitialized() {\n this.config = {\n rows: { appendable: false, selectable: { multiple: false } },\n columns: [\n { type: 'gutter', gutterName: 'sequence' },\n {\n type: 'gutter',\n gutterName: 'button',\n icon: 'person_remove',\n title: i18next.t('button.remove owner'),\n handlers: {\n click: (_columns, _data, _column, record, _rowIndex) => {\n // 클라이언트 게이트 없음 — 서버의 @privilege(domainOwnerGranted:true)\n // 가 최종 authoritative. 오너 아니면 서버 에러를 프롬프트로 표시.\n if (record?.user) {\n this._onRemoveOwner(record)\n }\n }\n }\n },\n {\n type: 'string',\n name: 'userName',\n header: i18next.t('field.name'),\n record: { editable: false },\n width: 180\n },\n {\n type: 'string',\n name: 'userEmail',\n header: i18next.t('field.email'),\n record: { editable: false },\n width: 220\n },\n {\n type: 'string',\n name: 'reason',\n header: i18next.t('field.reason'),\n record: { editable: false },\n width: 220\n },\n {\n type: 'string',\n name: 'grantedByName',\n header: i18next.t('field.granted by'),\n record: { editable: false },\n width: 160\n },\n {\n type: 'datetime',\n name: 'grantedAt',\n header: i18next.t('field.granted at'),\n width: 180\n }\n ]\n }\n }\n\n async pageUpdated(_changes: any, _lifecycle: any) {\n if (this.active) {\n this._refreshPermissions()\n await this.updateComplete\n this.grist.fetch()\n }\n }\n\n private _refreshPermissions() {\n const __state__ = store.getState() as any\n const me = __state__.auth?.user || {}\n this.isDomainOwner = !!me.domainOwner || !!me.super\n }\n\n private async _fetchHandler() {\n const response = await client.query({\n query: gql`\n query {\n domainOwners {\n id\n user {\n id\n name\n email\n username\n }\n grantedBy {\n id\n name\n email\n }\n grantedAt\n reason\n }\n }\n `,\n context: gqlContext(),\n fetchPolicy: 'network-only'\n })\n\n const items = (response.data?.domainOwners || []).map((ow: any) => ({\n ...ow,\n userName: ow.user?.name,\n userEmail: ow.user?.email,\n grantedByName: ow.grantedBy?.name || ''\n }))\n\n return { total: items.length, records: items }\n }\n\n private _onAddOwner() {\n openPopup(\n html`\n <add-domain-owner-popup\n @domain-owner-added=${() => {\n this.grist.fetch()\n this._notify(i18next.t('text.domain owner added successfully'))\n }}\n ></add-domain-owner-popup>\n `,\n {\n size: 'small',\n title: i18next.t('title.add domain owner')\n }\n )\n }\n\n private async _onRemoveOwner(record: any) {\n const username = record.user?.username || record.user?.email\n const displayName = record.user?.name || username\n\n const confirmed = await OxPrompt.open({\n type: 'warning',\n title: i18next.t('text.are_you_sure'),\n text: i18next.t('text.are_you_sure_to_remove_domain_owner', { x: displayName }),\n confirmButton: { text: i18next.t('button.remove owner') },\n cancelButton: { text: i18next.t('button.cancel') }\n })\n if (!confirmed) return\n\n try {\n const response = await client.mutate({\n mutation: gql`\n mutation removeDomainOwner($username: String!) {\n removeDomainOwner(username: $username)\n }\n `,\n variables: { username },\n context: gqlContext()\n })\n\n if (!response.errors?.length) {\n this._notify(i18next.t('text.domain owner removed successfully'))\n this.grist.fetch()\n }\n } catch (err: any) {\n await OxPrompt.open({\n type: 'error',\n title: i18next.t('text.failed'),\n text: err?.message || String(err),\n confirmButton: { text: i18next.t('button.confirm') }\n })\n }\n }\n\n private _notify(message: string) {\n document.dispatchEvent(new CustomEvent('notify', { detail: { message, option: { timer: 1500 } } }))\n }\n}\n"]}
@@ -42,6 +42,9 @@ export default function route(page) {
42
42
  case 'domains':
43
43
  import('./pages/domain/domain-management');
44
44
  return page;
45
+ case 'domain-owners':
46
+ import('./pages/domain-owner/domain-owner-management');
47
+ return page;
45
48
  case 'env-vars':
46
49
  import('./pages/env-var/env-var-list-page');
47
50
  return page;
@@ -1 +1 @@
1
- {"version":3,"file":"route.js","sourceRoot":"","sources":["../client/route.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,IAAI;IAChC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,SAAS;YACZ,MAAM;YACJ,2BAA2B;YAC3B,iCAAiC;YACjC,oBAAoB,CACrB,CAAA;YACD,OAAO,IAAI,CAAA;QAEb,KAAK,oBAAoB;YACvB,MAAM,CAAC,4BAA4B,CAAC,CAAA;YACpC,OAAO,IAAI,CAAA;QAEb,KAAK,aAAa;YAChB,MAAM,CAAC,iCAAiC,CAAC,CAAA;YACzC,OAAO,IAAI,CAAA;QAEb,KAAK,cAAc;YACjB,MAAM,CAAC,kCAAkC,CAAC,CAAA;YAC1C,OAAO,IAAI,CAAA;QAEb,KAAK,sBAAsB;YACzB,MAAM,CAAC,8BAA8B,CAAC,CAAA;YACtC,OAAO,IAAI,CAAA;QAEb,KAAK,cAAc;YACjB,MAAM,CAAC,kCAAkC,CAAC,CAAA;YAC1C,OAAO,IAAI,CAAA;QAEb,KAAK,aAAa;YAChB,MAAM,CAAC,iCAAiC,CAAC,CAAA;YACzC,OAAO,IAAI,CAAA;QAEb,KAAK,gBAAgB;YACnB,MAAM,CAAC,wBAAwB,CAAC,CAAA;YAChC,OAAO,IAAI,CAAA;QAEb,KAAK,WAAW;YACd,MAAM,CAAC,6BAA6B,CAAC,CAAA;YACrC,OAAO,IAAI,CAAA;QAEb,KAAK,OAAO;YACV,MAAM,CAAC,8BAA8B,CAAC,CAAA;YACtC,OAAO,IAAI,CAAA;QAEb,KAAK,OAAO;YACV,MAAM,CAAC,8BAA8B,CAAC,CAAA;YACtC,OAAO,IAAI,CAAA;QAEb,KAAK,UAAU;YACb,MAAM,CAAC,oCAAoC,CAAC,CAAA;YAC5C,OAAO,IAAI,CAAA;QAEb,KAAK,SAAS;YACZ,MAAM,CAAC,kCAAkC,CAAC,CAAA;YAC1C,OAAO,IAAI,CAAA;QAEb,KAAK,UAAU;YACb,MAAM,CAAC,mCAAmC,CAAC,CAAA;YAC3C,OAAO,IAAI,CAAA;QAEb,KAAK,YAAY;YACf,MAAM,CAAC,+CAA+C,CAAC,CAAA;YACvD,OAAO,IAAI,CAAA;QAEb,KAAK,gBAAgB;YACnB,MAAM,CAAC,mDAAmD,CAAC,CAAA;YAC3D,OAAO,IAAI,CAAA;QAEb,KAAK,cAAc;YACjB,MAAM,CAAC,+CAA+C,CAAC,CAAA;YACvD,OAAO,IAAI,CAAA;IACf,CAAC;AACH,CAAC","sourcesContent":["export default function route(page) {\n switch (page) {\n case 'profile':\n import(\n /* webpackPrefetch: true */\n /* webpackChunkName: \"profile\" */\n './pages/profile.js'\n )\n return page\n\n case 'appliance-register':\n import('./pages/appliance/register')\n return page\n\n case 'application':\n import('./pages/application/application')\n return page\n\n case 'applications':\n import('./pages/application/applications')\n return page\n\n case 'application-register':\n import('./pages/application/register')\n return page\n\n case 'app-bindings':\n import('./pages/app-binding/app-bindings')\n return page\n\n case 'app-binding':\n import('./pages/app-binding/app-binding')\n return page\n\n case 'appliance-home':\n import('./pages/appliance/home')\n return page\n\n case 'appliance':\n import('./pages/appliance/appliance')\n return page\n\n case 'roles':\n import('./pages/role/role-management')\n return page\n\n case 'users':\n import('./pages/user/user-management')\n return page\n\n case 'partners':\n import('./pages/partner/partner-management')\n return page\n\n case 'domains':\n import('./pages/domain/domain-management')\n return page\n\n case 'env-vars':\n import('./pages/env-var/env-var-list-page')\n return page\n\n case 'attributes':\n import('./pages/attribute/attribute-set-management.js')\n return page\n\n case 'auth-providers':\n import('./pages/auth-provider/auth-provider-management.js')\n return page\n\n case 'domain-links':\n import('./pages/domain-link/domain-link-management.js')\n return page\n }\n}\n"]}
1
+ {"version":3,"file":"route.js","sourceRoot":"","sources":["../client/route.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,IAAI;IAChC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,SAAS;YACZ,MAAM;YACJ,2BAA2B;YAC3B,iCAAiC;YACjC,oBAAoB,CACrB,CAAA;YACD,OAAO,IAAI,CAAA;QAEb,KAAK,oBAAoB;YACvB,MAAM,CAAC,4BAA4B,CAAC,CAAA;YACpC,OAAO,IAAI,CAAA;QAEb,KAAK,aAAa;YAChB,MAAM,CAAC,iCAAiC,CAAC,CAAA;YACzC,OAAO,IAAI,CAAA;QAEb,KAAK,cAAc;YACjB,MAAM,CAAC,kCAAkC,CAAC,CAAA;YAC1C,OAAO,IAAI,CAAA;QAEb,KAAK,sBAAsB;YACzB,MAAM,CAAC,8BAA8B,CAAC,CAAA;YACtC,OAAO,IAAI,CAAA;QAEb,KAAK,cAAc;YACjB,MAAM,CAAC,kCAAkC,CAAC,CAAA;YAC1C,OAAO,IAAI,CAAA;QAEb,KAAK,aAAa;YAChB,MAAM,CAAC,iCAAiC,CAAC,CAAA;YACzC,OAAO,IAAI,CAAA;QAEb,KAAK,gBAAgB;YACnB,MAAM,CAAC,wBAAwB,CAAC,CAAA;YAChC,OAAO,IAAI,CAAA;QAEb,KAAK,WAAW;YACd,MAAM,CAAC,6BAA6B,CAAC,CAAA;YACrC,OAAO,IAAI,CAAA;QAEb,KAAK,OAAO;YACV,MAAM,CAAC,8BAA8B,CAAC,CAAA;YACtC,OAAO,IAAI,CAAA;QAEb,KAAK,OAAO;YACV,MAAM,CAAC,8BAA8B,CAAC,CAAA;YACtC,OAAO,IAAI,CAAA;QAEb,KAAK,UAAU;YACb,MAAM,CAAC,oCAAoC,CAAC,CAAA;YAC5C,OAAO,IAAI,CAAA;QAEb,KAAK,SAAS;YACZ,MAAM,CAAC,kCAAkC,CAAC,CAAA;YAC1C,OAAO,IAAI,CAAA;QAEb,KAAK,eAAe;YAClB,MAAM,CAAC,8CAA8C,CAAC,CAAA;YACtD,OAAO,IAAI,CAAA;QAEb,KAAK,UAAU;YACb,MAAM,CAAC,mCAAmC,CAAC,CAAA;YAC3C,OAAO,IAAI,CAAA;QAEb,KAAK,YAAY;YACf,MAAM,CAAC,+CAA+C,CAAC,CAAA;YACvD,OAAO,IAAI,CAAA;QAEb,KAAK,gBAAgB;YACnB,MAAM,CAAC,mDAAmD,CAAC,CAAA;YAC3D,OAAO,IAAI,CAAA;QAEb,KAAK,cAAc;YACjB,MAAM,CAAC,+CAA+C,CAAC,CAAA;YACvD,OAAO,IAAI,CAAA;IACf,CAAC;AACH,CAAC","sourcesContent":["export default function route(page) {\n switch (page) {\n case 'profile':\n import(\n /* webpackPrefetch: true */\n /* webpackChunkName: \"profile\" */\n './pages/profile.js'\n )\n return page\n\n case 'appliance-register':\n import('./pages/appliance/register')\n return page\n\n case 'application':\n import('./pages/application/application')\n return page\n\n case 'applications':\n import('./pages/application/applications')\n return page\n\n case 'application-register':\n import('./pages/application/register')\n return page\n\n case 'app-bindings':\n import('./pages/app-binding/app-bindings')\n return page\n\n case 'app-binding':\n import('./pages/app-binding/app-binding')\n return page\n\n case 'appliance-home':\n import('./pages/appliance/home')\n return page\n\n case 'appliance':\n import('./pages/appliance/appliance')\n return page\n\n case 'roles':\n import('./pages/role/role-management')\n return page\n\n case 'users':\n import('./pages/user/user-management')\n return page\n\n case 'partners':\n import('./pages/partner/partner-management')\n return page\n\n case 'domains':\n import('./pages/domain/domain-management')\n return page\n\n case 'domain-owners':\n import('./pages/domain-owner/domain-owner-management')\n return page\n\n case 'env-vars':\n import('./pages/env-var/env-var-list-page')\n return page\n\n case 'attributes':\n import('./pages/attribute/attribute-set-management.js')\n return page\n\n case 'auth-providers':\n import('./pages/auth-provider/auth-provider-management.js')\n return page\n\n case 'domain-links':\n import('./pages/domain-link/domain-link-management.js')\n return page\n }\n}\n"]}