@things-factory/auth-ui 10.1.215 → 10.1.219
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-client/bootstrap.js +18 -14
- package/dist-client/bootstrap.js.map +1 -1
- package/dist-client/components/abstract-password-reset.d.ts +5 -0
- package/dist-client/components/abstract-password-reset.js +20 -0
- package/dist-client/components/abstract-password-reset.js.map +1 -1
- package/dist-client/components/ownership-transfer-popup.d.ts +3 -1
- package/dist-client/components/ownership-transfer-popup.js +38 -41
- package/dist-client/components/ownership-transfer-popup.js.map +1 -1
- package/dist-client/components/password-setup-link-popup.d.ts +46 -0
- package/dist-client/components/password-setup-link-popup.js +254 -0
- package/dist-client/components/password-setup-link-popup.js.map +1 -0
- package/dist-client/components/user-detail.d.ts +20 -30
- package/dist-client/components/user-detail.js +114 -186
- package/dist-client/components/user-detail.js.map +1 -1
- package/dist-client/components/user-invite.d.ts +18 -25
- package/dist-client/components/user-invite.js +157 -181
- package/dist-client/components/user-invite.js.map +1 -1
- package/dist-client/components/user-popups-standard.test.js +69 -0
- package/dist-client/components/user-popups-standard.test.js.map +1 -0
- package/dist-client/components/user-role-editor.js +5 -26
- package/dist-client/components/user-role-editor.js.map +1 -1
- package/dist-client/components/user-status-renderer.js +4 -0
- package/dist-client/components/user-status-renderer.js.map +1 -1
- package/dist-client/entries/auth/reset-password.d.ts +1 -0
- package/dist-client/entries/auth/reset-password.js +5 -1
- package/dist-client/entries/auth/reset-password.js.map +1 -1
- package/dist-client/index.js +10 -28
- package/dist-client/index.js.map +1 -1
- package/dist-client/pages/user/user-list-page.d.ts +5 -1
- package/dist-client/pages/user/user-list-page.js +14 -5
- package/dist-client/pages/user/user-list-page.js.map +1 -1
- package/dist-client/route.js +0 -3
- package/dist-client/route.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/utils/link-token.d.ts +3 -3
- package/dist-client/utils/link-token.js +4 -4
- package/dist-client/utils/link-token.js.map +1 -1
- package/dist-client/utils/link-token.test.js +2 -2
- package/dist-client/utils/link-token.test.js.map +1 -1
- package/package.json +6 -5
- package/things-factory.config.js +0 -1
- package/translations/en.json +30 -20
- package/translations/es.json +30 -20
- package/translations/ja.json +30 -20
- package/translations/ko.json +30 -20
- package/translations/ms.json +30 -20
- package/translations/zh.json +30 -20
- package/dist-client/components/accept-invitation.d.ts +0 -47
- package/dist-client/components/accept-invitation.js +0 -258
- package/dist-client/components/accept-invitation.js.map +0 -1
- package/dist-client/entries/auth/accept-invitation.d.ts +0 -3
- package/dist-client/entries/auth/accept-invitation.js +0 -10
- package/dist-client/entries/auth/accept-invitation.js.map +0 -1
- package/dist-client/pages/invitation/invitation-list-query.test.js +0 -69
- package/dist-client/pages/invitation/invitation-list-query.test.js.map +0 -1
- package/dist-client/pages/invitation/invitations.d.ts +0 -63
- package/dist-client/pages/invitation/invitations.js +0 -284
- package/dist-client/pages/invitation/invitations.js.map +0 -1
- /package/dist-client/{pages/invitation/invitation-list-query.test.d.ts → components/user-popups-standard.test.d.ts} +0 -0
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { __decorate, __metadata } from "tslib";
|
|
2
2
|
import '@material/web/icon/icon.js';
|
|
3
|
-
import '@material/web/button/
|
|
3
|
+
import '@material/web/button/text-button.js';
|
|
4
|
+
import '@material/web/button/filled-button.js';
|
|
4
5
|
import '@material/web/button/outlined-button.js';
|
|
5
|
-
import '@
|
|
6
|
+
import '@operato/popup/ox-popup-actions.js';
|
|
6
7
|
import './ownership-transfer-popup.js';
|
|
8
|
+
import { statusRenderer } from './user-status-renderer.js';
|
|
9
|
+
import { openPasswordSetupLink } from './password-setup-link-popup.js';
|
|
7
10
|
import gql from 'graphql-tag';
|
|
8
11
|
import { lockedParentRoles } from '../utils/role-origin.js';
|
|
9
12
|
import { css, html, LitElement, nothing } from 'lit';
|
|
@@ -13,7 +16,7 @@ import { i18next, localize } from '@operato/i18n';
|
|
|
13
16
|
import { openPopup } from '@operato/layout';
|
|
14
17
|
import { OxPrompt } from '@operato/popup/ox-prompt.js';
|
|
15
18
|
import { InheritedValueType, store } from '@operato/shell';
|
|
16
|
-
import {
|
|
19
|
+
import { CommonPopupStyles, FormFieldStyles, PopupBodyStyles } from '@operato/styles';
|
|
17
20
|
import { hasPrivilege } from '@things-factory/auth-base/dist-client';
|
|
18
21
|
/**
|
|
19
22
|
* 한 사람의 역할을 고치는 자리.
|
|
@@ -68,15 +71,13 @@ let UserDetail = class UserDetail extends localize(i18next)(LitElement) {
|
|
|
68
71
|
this.canManage = false;
|
|
69
72
|
this.canOwn = false;
|
|
70
73
|
this.myEmail = '';
|
|
71
|
-
/** 기본 비밀번호가 설정돼 있지 않으면 초기화는 서버에서 던진다 — 단추를 내주지 않는다. */
|
|
72
|
-
this.defaultPasswordConfigured = false;
|
|
73
74
|
/**
|
|
74
|
-
* 이
|
|
75
|
+
* 이 사람에게 이 도메인이 비밀번호 설정 링크를 발급해도 되는가 — 서버 규칙(`password-reset-rule.ts`)의 답.
|
|
75
76
|
*
|
|
76
77
|
* 비밀번호는 계정 하나에 하나다. 다른 도메인에서도 일하는 사람, 어느 도메인이든 오너인 사람
|
|
77
|
-
* (수퍼유저 포함)
|
|
78
|
+
* (수퍼유저 포함)의 비밀번호는 이 도메인이 거두지 않는다(ADR-0161 결정 4).
|
|
78
79
|
*/
|
|
79
|
-
this.
|
|
80
|
+
this.setupLinkIssuable = false;
|
|
80
81
|
/**
|
|
81
82
|
* 이 도메인(과 부모)의 역할 id.
|
|
82
83
|
*
|
|
@@ -88,36 +89,37 @@ let UserDetail = class UserDetail extends localize(i18next)(LitElement) {
|
|
|
88
89
|
/** The parent domain's roles this person may not change here - locked, with the reason under the list. */
|
|
89
90
|
this.locked = new Set();
|
|
90
91
|
}
|
|
92
|
+
/*
|
|
93
|
+
* 팝업의 집 모양을 그대로 쓴다(page-standard §7·§8) — 본문만 스크롤하고 단추 줄은 `ox-popup-actions`
|
|
94
|
+
* 하나다. 제목은 이 팝업이 무엇인지(사용자 상세 정보)를 말하고, 누구인지는 대상 띠(`.what`)가 말한다.
|
|
95
|
+
*/
|
|
91
96
|
static { this.styles = [
|
|
92
|
-
|
|
93
|
-
|
|
97
|
+
CommonPopupStyles,
|
|
98
|
+
PopupBodyStyles,
|
|
99
|
+
FormFieldStyles,
|
|
94
100
|
css `
|
|
95
|
-
|
|
101
|
+
/* 상태 한 줄 — 그 줄에 대한 동작은 줄 끝에 둔다(§8 「줄 하나」). */
|
|
102
|
+
[state-line] {
|
|
96
103
|
display: flex;
|
|
97
|
-
|
|
104
|
+
align-items: center;
|
|
98
105
|
gap: var(--spacing-medium, 8px);
|
|
99
|
-
padding: var(--spacing-large, 12px);
|
|
100
|
-
background-color: var(--md-sys-color-surface);
|
|
101
|
-
color: var(--md-sys-color-on-surface);
|
|
102
|
-
overflow: auto;
|
|
103
106
|
}
|
|
104
107
|
|
|
105
|
-
[
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
font-
|
|
108
|
+
[state-line] [label],
|
|
109
|
+
[group-label] {
|
|
110
|
+
color: var(--ui-muted);
|
|
111
|
+
font-size: var(--ui-t-small, 11.5px);
|
|
109
112
|
}
|
|
110
113
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
+
/* 팝업 안의 컨트롤은 담는 카드를 따라 알약이다 — 공용 규칙은 단추 줄 안에만 걸린다. */
|
|
115
|
+
[state-line] md-outlined-button {
|
|
116
|
+
margin-left: auto;
|
|
117
|
+
--md-outlined-button-container-height: var(--popup-action-height, 32px);
|
|
118
|
+
--md-outlined-button-container-shape: var(--popup-control-radius);
|
|
114
119
|
}
|
|
115
120
|
|
|
116
|
-
[
|
|
121
|
+
[group-label] {
|
|
117
122
|
margin-top: var(--spacing-medium, 8px);
|
|
118
|
-
color: var(--md-sys-color-on-surface-variant);
|
|
119
|
-
font-size: 0.8rem;
|
|
120
|
-
text-transform: capitalize;
|
|
121
123
|
}
|
|
122
124
|
|
|
123
125
|
ul[roles] {
|
|
@@ -132,48 +134,18 @@ let UserDetail = class UserDetail extends localize(i18next)(LitElement) {
|
|
|
132
134
|
ul[roles] li {
|
|
133
135
|
display: flex;
|
|
134
136
|
align-items: center;
|
|
135
|
-
gap: 8px;
|
|
136
|
-
padding:
|
|
137
|
+
gap: var(--spacing-medium, 8px);
|
|
138
|
+
padding: 3px 0;
|
|
137
139
|
}
|
|
138
140
|
|
|
139
141
|
ul[roles] li [desc] {
|
|
140
|
-
color: var(--
|
|
141
|
-
font-size:
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/*
|
|
145
|
-
색은 짝으로 쓰고, 「무엇이 나를 기다리나」에만 쓴다.
|
|
146
|
-
소유자 안내는 조치가 필요한 것이 아니라 사실이므로 조용한 면을 쓴다.
|
|
147
|
-
*/
|
|
148
|
-
[note] {
|
|
149
|
-
padding: 8px 10px;
|
|
150
|
-
border-radius: var(--md-sys-shape-corner-small, 6px);
|
|
151
|
-
font-size: 0.82rem;
|
|
152
|
-
background-color: var(--md-sys-color-surface-variant);
|
|
153
|
-
color: var(--md-sys-color-on-surface-variant);
|
|
142
|
+
color: var(--ui-muted);
|
|
143
|
+
font-size: var(--ui-t-small, 11.5px);
|
|
154
144
|
}
|
|
155
145
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/*
|
|
162
|
-
되돌릴 수 없는 조치 — 선으로 갈라 두고, 색은 글자에만 쓴다.
|
|
163
|
-
면을 error 로 칠하면 「지금 문제가 있다」로 읽힌다.
|
|
164
|
-
*/
|
|
165
|
-
[danger-zone] {
|
|
166
|
-
margin-top: var(--spacing-large, 12px);
|
|
167
|
-
border-top: 1px solid var(--md-sys-color-outline-variant);
|
|
168
|
-
padding-top: var(--spacing-small, 4px);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
[danger-zone] md-outlined-button {
|
|
172
|
-
--md-outlined-button-label-text-color: var(--md-sys-color-error);
|
|
173
|
-
--md-outlined-button-outline-color: var(--md-sys-color-outline-variant);
|
|
174
|
-
--md-outlined-button-hover-label-text-color: var(--md-sys-color-error);
|
|
175
|
-
--md-outlined-button-hover-state-layer-color: var(--md-sys-color-error);
|
|
176
|
-
--md-outlined-button-pressed-label-text-color: var(--md-sys-color-error);
|
|
146
|
+
.note md-icon {
|
|
147
|
+
--md-icon-size: 16px;
|
|
148
|
+
vertical-align: -3px;
|
|
177
149
|
}
|
|
178
150
|
`
|
|
179
151
|
]; }
|
|
@@ -194,47 +166,44 @@ let UserDetail = class UserDetail extends localize(i18next)(LitElement) {
|
|
|
194
166
|
render() {
|
|
195
167
|
const user = this.user || {};
|
|
196
168
|
return html `
|
|
197
|
-
<div
|
|
198
|
-
|
|
169
|
+
<div class="body">
|
|
170
|
+
<div class="what">${user.name || user.username || user.email} · ${user.email}</div>
|
|
199
171
|
|
|
200
|
-
|
|
201
|
-
? html `<div note>
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
172
|
+
${user.owner
|
|
173
|
+
? html `<div class="note">
|
|
174
|
+
<md-icon>supervisor_account</md-icon>
|
|
175
|
+
${i18next.t('text.owner opens everything without a role')}
|
|
176
|
+
</div>`
|
|
205
177
|
: nothing}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
<div section>${i18next.t('field.roles')}</div>
|
|
209
|
-
${this.renderRoles()}
|
|
178
|
+
${this.renderAccountState(user)}
|
|
210
179
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
${i18next.t('button.save')}
|
|
214
|
-
</md-elevated-button>
|
|
215
|
-
<md-elevated-button @click=${() => this.dispatchEvent(new CustomEvent('close'))}>
|
|
216
|
-
${i18next.t('button.cancel')}
|
|
217
|
-
</md-elevated-button>
|
|
180
|
+
<div group-label>${i18next.t('field.roles')}</div>
|
|
181
|
+
${this.renderRoles()}
|
|
218
182
|
</div>
|
|
219
183
|
|
|
220
|
-
|
|
184
|
+
<ox-popup-actions>
|
|
185
|
+
${this.renderIrreversible(user)}
|
|
186
|
+
<md-text-button slot="cancel" @click=${() => this.dispatchEvent(new CustomEvent('close'))}>
|
|
187
|
+
${i18next.t('button.cancel')}
|
|
188
|
+
</md-text-button>
|
|
189
|
+
<md-filled-button slot="confirm" ?disabled=${!this.savable} @click=${() => this.onSave()}>
|
|
190
|
+
${i18next.t('button.save')}
|
|
191
|
+
</md-filled-button>
|
|
192
|
+
</ox-popup-actions>
|
|
221
193
|
`;
|
|
222
194
|
}
|
|
223
195
|
/**
|
|
224
|
-
* 되돌릴 수 없는
|
|
196
|
+
* 되돌릴 수 없는 조치 — 단추 줄의 왼쪽 끝, 빨간 글자(page-standard §8, `ox-popup-actions` 의 `start`).
|
|
225
197
|
*
|
|
226
|
-
* ── 왜 아래에 따로 두나 ─────────────────────────────────────────────────────
|
|
227
198
|
* 옛 화면은 저장·소유권 이전·초기화·제외·비활성화를 **한 줄에 나란히** 뒀다. 매일 누르는
|
|
228
|
-
* 「저장」 옆에 한 번 누르면 끝인 「제외」가 같은 크기로 있었다.
|
|
229
|
-
*
|
|
230
|
-
* 그래서 선을 하나 긋고, 위는 채운 단추(주 동작), 아래는 테두리 단추로 둔다. 색은
|
|
231
|
-
* error 짝으로 글자에만 쓴다 — 면을 다 칠하면 「지금 문제가 있다」로 읽힌다.
|
|
199
|
+
* 「저장」 옆에 한 번 누르면 끝인 「제외」가 같은 크기로 있었다. 단추 줄이 그 거리를 맡는다 —
|
|
200
|
+
* 저장은 오른쪽 끝, 이것들은 왼쪽 끝이고, 색은 글자에만 쓴다.
|
|
232
201
|
*
|
|
233
202
|
* ── 무엇을 언제 내주나 ──────────────────────────────────────────────────────
|
|
234
203
|
* 조건은 전부 **서버가 실제로 던지는 것**을 그대로 옮긴 것이다. 눌리는 단추를 보여 주고
|
|
235
204
|
* 서버가 거절하면 그것은 안내가 아니라 오류다.
|
|
236
205
|
*
|
|
237
|
-
* 비밀번호
|
|
206
|
+
* 비밀번호 설정 링크 서버가 된다고 하고(`mayIssuePasswordSetupLink`), 내가 아니다
|
|
238
207
|
* 소유권 이전 활성 상태고, 소유자가 아니고, 이 도메인에 역할이 하나라도 있다
|
|
239
208
|
* 도메인에서 제외 소유자가 아니고, 내가 아니다
|
|
240
209
|
*
|
|
@@ -242,42 +211,36 @@ let UserDetail = class UserDetail extends localize(i18next)(LitElement) {
|
|
|
242
211
|
* `deleteDomainUser` 는 계정을 지우지 않는다 — **이 도메인에서만 뗀다**(서버 설명도 그렇게
|
|
243
212
|
* 적혀 있다). 옛 화면의 「사용자 삭제」는 하지 않는 일을 말하고 있었다.
|
|
244
213
|
*/
|
|
245
|
-
|
|
214
|
+
renderIrreversible(user) {
|
|
246
215
|
const me = this.myEmail && user.email === this.myEmail;
|
|
247
|
-
const resettable = this.canManage && this.
|
|
216
|
+
const resettable = this.canManage && this.setupLinkIssuable && !me;
|
|
248
217
|
const transferable = this.canOwn &&
|
|
249
218
|
!user.owner &&
|
|
250
219
|
user.status === 'activated' &&
|
|
251
220
|
(user.roles || []).some((role) => this.domainRoleIds.has(role.id));
|
|
252
221
|
const removable = this.canManage && !user.owner && !me;
|
|
253
|
-
if (!resettable && !transferable && !removable) {
|
|
254
|
-
return nothing;
|
|
255
|
-
}
|
|
256
222
|
return html `
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
</md-outlined-button>`
|
|
223
|
+
${resettable
|
|
224
|
+
? html `<md-text-button slot="start" destructive @click=${() => this.onPasswordSetup()}>
|
|
225
|
+
${user.status === 'password_reset_required'
|
|
226
|
+
? i18next.t('button.reissue setup link')
|
|
227
|
+
: i18next.t('button.password reset link')}
|
|
228
|
+
</md-text-button>`
|
|
264
229
|
: nothing}
|
|
265
|
-
|
|
266
|
-
? html `<md-
|
|
267
|
-
|
|
268
|
-
|
|
230
|
+
${transferable
|
|
231
|
+
? html `<md-text-button slot="start" destructive @click=${() => this.onTransfer()}>
|
|
232
|
+
${i18next.t('button.transfer owner')}
|
|
233
|
+
</md-text-button>`
|
|
269
234
|
: nothing}
|
|
270
|
-
|
|
271
|
-
? html `<md-
|
|
272
|
-
|
|
273
|
-
|
|
235
|
+
${removable
|
|
236
|
+
? html `<md-text-button slot="start" destructive @click=${() => this.onRemoveFromDomain()}>
|
|
237
|
+
${i18next.t('button.remove from domain')}
|
|
238
|
+
</md-text-button>`
|
|
274
239
|
: nothing}
|
|
275
|
-
</div>
|
|
276
|
-
</div>
|
|
277
240
|
`;
|
|
278
241
|
}
|
|
279
242
|
/**
|
|
280
|
-
* 계정
|
|
243
|
+
* 계정 상태 한 줄과, 그것을 바꾸는 단추를 그 줄 끝에(page-standard §8 「줄 하나」).
|
|
281
244
|
*
|
|
282
245
|
* **잠금 해제와 활성화는 서버에서 같은 것이다** — `activateUser` 가 `failCount` 를 0 으로
|
|
283
246
|
* 두고 `ACTIVATED` 로 되돌린다. 그래서 부르는 것은 하나이고, **글자만 상황에 맞춰 바꾼다.**
|
|
@@ -285,61 +248,66 @@ let UserDetail = class UserDetail extends localize(i18next)(LitElement) {
|
|
|
285
248
|
*
|
|
286
249
|
* 비활성화는 소유자와 나 자신에게는 내주지 않는다. 서버도 같은 것을 막지만
|
|
287
250
|
* (`inactivateUser` 가 소유자·admin 을 거절한다), **화면이 눌리는 단추를 보여 주고 서버가
|
|
288
|
-
* 거절하면 그것은 안내가 아니라 오류다.**
|
|
251
|
+
* 거절하면 그것은 안내가 아니라 오류다.** 되돌릴 수 있으므로 빨간 글자가 아니다.
|
|
289
252
|
*/
|
|
290
253
|
renderAccountState(user) {
|
|
291
|
-
const status = user.status;
|
|
292
254
|
if (!this.canManage) {
|
|
293
255
|
return nothing;
|
|
294
256
|
}
|
|
257
|
+
return html `
|
|
258
|
+
<div state-line>
|
|
259
|
+
<span label>${i18next.t('field.status')}</span>
|
|
260
|
+
${statusRenderer(() => 0)(user.status, null, user)} ${this.renderStateAction(user)}
|
|
261
|
+
</div>
|
|
262
|
+
`;
|
|
263
|
+
}
|
|
264
|
+
renderStateAction(user) {
|
|
265
|
+
const status = user.status;
|
|
295
266
|
if (status === 'locked') {
|
|
296
|
-
return html `<
|
|
297
|
-
|
|
298
|
-
|
|
267
|
+
return html `<md-outlined-button @click=${() => this.onActivate()}>${i18next.t('button.unlock')}</md-outlined-button>`;
|
|
268
|
+
}
|
|
269
|
+
/* 설정 대기는 활성화로 열리지 않는다 — 본인이 설정 링크로 연다(단추 줄의 설정 링크). */
|
|
270
|
+
if (status === 'password_reset_required') {
|
|
271
|
+
return nothing;
|
|
299
272
|
}
|
|
300
273
|
if (status !== 'activated') {
|
|
301
|
-
return html `<
|
|
302
|
-
<md-elevated-button @click=${() => this.onActivate()}> ${i18next.t('button.activate')} </md-elevated-button>
|
|
303
|
-
</div>`;
|
|
274
|
+
return html `<md-outlined-button @click=${() => this.onActivate()}>${i18next.t('button.activate')}</md-outlined-button>`;
|
|
304
275
|
}
|
|
305
276
|
/* 활성 상태 — 되돌릴 수 있는 사람에게만, 그리고 자기 자신과 소유자에게는 안 준다. */
|
|
306
277
|
if (user.owner || (this.myEmail && user.email === this.myEmail)) {
|
|
307
278
|
return nothing;
|
|
308
279
|
}
|
|
309
|
-
return html `<
|
|
310
|
-
<md-elevated-button danger @click=${() => this.onInactivate()}>
|
|
311
|
-
${i18next.t('button.deactivate')}
|
|
312
|
-
</md-elevated-button>
|
|
313
|
-
</div>`;
|
|
280
|
+
return html `<md-outlined-button @click=${() => this.onInactivate()}>${i18next.t('button.deactivate')}</md-outlined-button>`;
|
|
314
281
|
}
|
|
315
282
|
renderRoles() {
|
|
316
283
|
if (this.loading) {
|
|
317
|
-
return html `<div
|
|
284
|
+
return html `<div class="note">${i18next.t('text.loading')}</div>`;
|
|
318
285
|
}
|
|
319
286
|
if (!this.availableRoles.length) {
|
|
320
287
|
/*
|
|
321
288
|
* 「역할이 없다」와 「못 읽었다」는 다른 말이다. 여기까지 왔다는 것은 읽었다는 뜻이므로
|
|
322
|
-
* 없는 것이 맞고, 그때는 무엇을 해야 하는지를 말한다.
|
|
289
|
+
* 없는 것이 맞고, 그때는 무엇을 해야 하는지를 말한다. 저장을 막고 있으므로 실패 모양이다.
|
|
323
290
|
*/
|
|
324
|
-
return html `<div
|
|
291
|
+
return html `<div class="failure">${i18next.t('text.no role defined in this domain')}</div>`;
|
|
325
292
|
}
|
|
326
293
|
return html `
|
|
327
294
|
<ul roles>
|
|
328
295
|
${this.availableRoles.map(role => html `
|
|
329
296
|
<li>
|
|
330
|
-
<
|
|
297
|
+
<input
|
|
298
|
+
type="checkbox"
|
|
331
299
|
id="role-${role.id}"
|
|
332
300
|
.checked=${this.selected.has(role.id)}
|
|
333
301
|
?disabled=${this.locked.has(role.id)}
|
|
334
302
|
@change=${(e) => this.onToggle(role.id, e.target.checked)}
|
|
335
|
-
|
|
303
|
+
/>
|
|
336
304
|
<label for="role-${role.id}">${role.name}</label>
|
|
337
305
|
${role.description ? html `<span desc>${role.description}</span>` : nothing}
|
|
338
306
|
</li>
|
|
339
307
|
`)}
|
|
340
308
|
</ul>
|
|
341
309
|
${this.availableRoles.some(role => this.locked.has(role.id))
|
|
342
|
-
? html `<div note>${i18next.t('text.parent roles are changed by the parent domain')}</div>`
|
|
310
|
+
? html `<div class="note">${i18next.t('text.parent roles are changed by the parent domain')}</div>`
|
|
343
311
|
: nothing}
|
|
344
312
|
`;
|
|
345
313
|
}
|
|
@@ -357,36 +325,21 @@ let UserDetail = class UserDetail extends localize(i18next)(LitElement) {
|
|
|
357
325
|
const me = store.getState().auth?.user || {};
|
|
358
326
|
this.myEmail = me.email || '';
|
|
359
327
|
}
|
|
360
|
-
/**
|
|
361
|
-
* 서버 지시자에 적힌 인자를 그대로 되묻는다.
|
|
362
|
-
*
|
|
363
|
-
* 기본 비밀번호 여부도 여기서 함께 읽는다. 서버에 같은 답을 주는 질의가 둘 있는데
|
|
364
|
-
* (`checkDefaultPassword` 와 `checkResettablePasswordToDefault` — 둘 다
|
|
365
|
-
* `Boolean(defaultPassword)`), 하나만 쓴다.
|
|
366
|
-
*/
|
|
328
|
+
/** 서버 지시자에 적힌 인자를 그대로 되묻는다. */
|
|
367
329
|
async checkGates() {
|
|
368
330
|
this.canManage = Boolean(await hasPrivilege({ category: 'user', privilege: 'mutation', domainOwnerGranted: true }));
|
|
369
331
|
this.canOwn = Boolean(await hasPrivilege({ domainOwnerGranted: true, superUserGranted: true }));
|
|
370
|
-
const response = await client.query({
|
|
371
|
-
query: gql `
|
|
372
|
-
query {
|
|
373
|
-
checkDefaultPassword
|
|
374
|
-
}
|
|
375
|
-
`,
|
|
376
|
-
context: gqlContext()
|
|
377
|
-
});
|
|
378
|
-
this.defaultPasswordConfigured = Boolean(response.data?.checkDefaultPassword);
|
|
379
332
|
}
|
|
380
333
|
updated(changes) {
|
|
381
334
|
if (changes.has('user')) {
|
|
382
335
|
this.fetch();
|
|
383
|
-
this.
|
|
336
|
+
this.fetchSetupLinkIssuable();
|
|
384
337
|
}
|
|
385
338
|
}
|
|
386
339
|
/** 관리 권한이 없으면 서버가 이 질의를 거절한다 — 그때도 단추는 없다. */
|
|
387
|
-
async
|
|
340
|
+
async fetchSetupLinkIssuable() {
|
|
388
341
|
const id = this.user?.id;
|
|
389
|
-
this.
|
|
342
|
+
this.setupLinkIssuable = false;
|
|
390
343
|
if (!id) {
|
|
391
344
|
return;
|
|
392
345
|
}
|
|
@@ -394,16 +347,16 @@ let UserDetail = class UserDetail extends localize(i18next)(LitElement) {
|
|
|
394
347
|
const response = await client.query({
|
|
395
348
|
query: gql `
|
|
396
349
|
query ($id: String!) {
|
|
397
|
-
|
|
350
|
+
mayIssuePasswordSetupLink(id: $id)
|
|
398
351
|
}
|
|
399
352
|
`,
|
|
400
353
|
variables: { id },
|
|
401
354
|
context: gqlContext()
|
|
402
355
|
});
|
|
403
|
-
this.
|
|
356
|
+
this.setupLinkIssuable = this.user?.id === id && response.data?.mayIssuePasswordSetupLink === true;
|
|
404
357
|
}
|
|
405
358
|
catch (e) {
|
|
406
|
-
this.
|
|
359
|
+
this.setupLinkIssuable = false;
|
|
407
360
|
}
|
|
408
361
|
}
|
|
409
362
|
/**
|
|
@@ -481,36 +434,15 @@ let UserDetail = class UserDetail extends localize(i18next)(LitElement) {
|
|
|
481
434
|
`);
|
|
482
435
|
}
|
|
483
436
|
/**
|
|
484
|
-
* 비밀번호를
|
|
437
|
+
* 비밀번호 설정 링크를 발급한다 — 관리자는 비밀번호를 정하지 않는다(ADR-0161).
|
|
485
438
|
*
|
|
486
|
-
*
|
|
487
|
-
*
|
|
488
|
-
* 메일을 보내지 않는다).
|
|
439
|
+
* 무엇이 일어나는지 · 어떻게 전할지는 그 팝업이 말하고 묻는다. 발급되면 계정이 「설정 대기」가 되므로
|
|
440
|
+
* 목록을 다시 읽게 알린다(`user-updated`).
|
|
489
441
|
*/
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
title: i18next.t('title.reset password'),
|
|
494
|
-
text: i18next.t('text.password becomes the default and must be told to them'),
|
|
495
|
-
confirmButton: { text: i18next.t('button.confirm') },
|
|
496
|
-
cancelButton: { text: i18next.t('button.cancel') }
|
|
442
|
+
onPasswordSetup() {
|
|
443
|
+
openPasswordSetupLink(this.user, {
|
|
444
|
+
onIssued: () => this.dispatchEvent(new CustomEvent('user-updated', { detail: this.user, bubbles: true, composed: true }))
|
|
497
445
|
});
|
|
498
|
-
if (!confirmed) {
|
|
499
|
-
return;
|
|
500
|
-
}
|
|
501
|
-
const done = await this.runUserMutation(gql `
|
|
502
|
-
mutation ($username: String!) {
|
|
503
|
-
resetPasswordToDefault(username: $username)
|
|
504
|
-
}
|
|
505
|
-
`, { notify: false });
|
|
506
|
-
if (done) {
|
|
507
|
-
await OxPrompt.open({
|
|
508
|
-
type: 'success',
|
|
509
|
-
title: i18next.t('text.completed'),
|
|
510
|
-
text: i18next.t('text.password reset succeed'),
|
|
511
|
-
confirmButton: { text: i18next.t('button.confirm') }
|
|
512
|
-
});
|
|
513
|
-
}
|
|
514
446
|
}
|
|
515
447
|
/**
|
|
516
448
|
* 이 도메인에서만 뗀다 — 계정은 남는다.
|
|
@@ -665,11 +597,7 @@ __decorate([
|
|
|
665
597
|
__decorate([
|
|
666
598
|
state(),
|
|
667
599
|
__metadata("design:type", Boolean)
|
|
668
|
-
], UserDetail.prototype, "
|
|
669
|
-
__decorate([
|
|
670
|
-
state(),
|
|
671
|
-
__metadata("design:type", Boolean)
|
|
672
|
-
], UserDetail.prototype, "passwordResettable", void 0);
|
|
600
|
+
], UserDetail.prototype, "setupLinkIssuable", void 0);
|
|
673
601
|
__decorate([
|
|
674
602
|
state(),
|
|
675
603
|
__metadata("design:type", Set)
|