@things-factory/auth-ui 8.0.0-alpha.8 → 8.0.0-beta.1
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/client/components/abstract-auth-page.ts +10 -10
- package/client/components/abstract-password-reset.ts +9 -14
- package/client/components/abstract-sign.ts +13 -13
- package/client/components/change-password.ts +1 -0
- package/client/components/contact-us.ts +11 -8
- package/client/components/create-role.ts +1 -0
- package/client/components/create-user.ts +27 -5
- package/client/components/invite-user.ts +19 -10
- package/client/components/ownership-transfer-popup.ts +3 -3
- package/client/components/profile-component.ts +68 -4
- package/client/components/role-privilege-editor.ts +34 -6
- package/client/components/user-role-editor.ts +20 -20
- package/client/entries/auth/checkin.ts +1 -1
- package/client/entries/auth/forgot-password.ts +11 -2
- package/client/entries/auth/signup.ts +13 -7
- package/client/index.ts +1 -1
- package/client/pages/attribute/attribute-set-item-list.ts +1 -1
- package/client/pages/role/role-management.ts +2 -0
- package/client/pages/user/user-management.ts +10 -11
- package/dist-client/components/abstract-auth-page.js +10 -10
- package/dist-client/components/abstract-auth-page.js.map +1 -1
- package/dist-client/components/abstract-password-reset.d.ts +1 -2
- package/dist-client/components/abstract-password-reset.js +7 -14
- package/dist-client/components/abstract-password-reset.js.map +1 -1
- package/dist-client/components/abstract-sign.js +12 -11
- package/dist-client/components/abstract-sign.js.map +1 -1
- package/dist-client/components/change-password.js +1 -0
- package/dist-client/components/change-password.js.map +1 -1
- package/dist-client/components/contact-us.d.ts +1 -1
- package/dist-client/components/contact-us.js +10 -7
- package/dist-client/components/contact-us.js.map +1 -1
- package/dist-client/components/create-role.js +1 -0
- package/dist-client/components/create-role.js.map +1 -1
- package/dist-client/components/create-user.js +28 -5
- package/dist-client/components/create-user.js.map +1 -1
- package/dist-client/components/invite-user.js +20 -11
- package/dist-client/components/invite-user.js.map +1 -1
- package/dist-client/components/ownership-transfer-popup.js +3 -3
- package/dist-client/components/ownership-transfer-popup.js.map +1 -1
- package/dist-client/components/profile-component.d.ts +5 -1
- package/dist-client/components/profile-component.js +68 -4
- package/dist-client/components/profile-component.js.map +1 -1
- package/dist-client/components/role-privilege-editor.js +41 -10
- package/dist-client/components/role-privilege-editor.js.map +1 -1
- package/dist-client/components/user-role-editor.js +20 -20
- package/dist-client/components/user-role-editor.js.map +1 -1
- package/dist-client/entries/auth/checkin.js +1 -1
- package/dist-client/entries/auth/checkin.js.map +1 -1
- package/dist-client/entries/auth/forgot-password.js +11 -2
- package/dist-client/entries/auth/forgot-password.js.map +1 -1
- package/dist-client/entries/auth/signup.js +13 -7
- package/dist-client/entries/auth/signup.js.map +1 -1
- package/dist-client/index.js +1 -1
- package/dist-client/index.js.map +1 -1
- package/dist-client/pages/attribute/attribute-set-item-list.js +1 -1
- package/dist-client/pages/attribute/attribute-set-item-list.js.map +1 -1
- package/dist-client/pages/role/role-management.js +2 -0
- package/dist-client/pages/role/role-management.js.map +1 -1
- package/dist-client/pages/user/user-management.d.ts +29 -0
- package/dist-client/pages/user/user-management.js +9 -9
- package/dist-client/pages/user/user-management.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +12 -12
- package/translations/en.json +8 -2
- package/translations/ja.json +7 -2
- package/translations/ko.json +7 -2
- package/translations/ms.json +8 -2
- package/translations/zh.json +8 -2
|
@@ -176,22 +176,22 @@ export abstract class AbstractAuthPage extends localize(i18next)(LitElement) {
|
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
get formfields() {
|
|
179
|
-
const
|
|
180
|
-
// .validationMessage=${String(i18next.t('text.invalid-
|
|
179
|
+
const username = this.data?.username || ''
|
|
180
|
+
// .validationMessage=${String(i18next.t('text.invalid-username'))}
|
|
181
181
|
|
|
182
182
|
return html`
|
|
183
183
|
<input id="redirectTo" type="hidden" name="redirectTo" .value=${this.redirectTo || '/'} />
|
|
184
184
|
|
|
185
185
|
<div class="field">
|
|
186
186
|
<md-filled-text-field
|
|
187
|
-
name="
|
|
188
|
-
type="
|
|
189
|
-
label=${String(i18next.t('field.email'))}
|
|
187
|
+
name="username"
|
|
188
|
+
type="text"
|
|
189
|
+
label=${String(i18next.t('field.user-id or email'))}
|
|
190
190
|
required
|
|
191
|
-
.value=${
|
|
191
|
+
.value=${username}
|
|
192
192
|
autocomplete="off"
|
|
193
193
|
autocapitalize="off"
|
|
194
|
-
><md-icon slot="leading-icon">
|
|
194
|
+
><md-icon slot="leading-icon">id_card</md-icon></md-filled-text-field
|
|
195
195
|
>
|
|
196
196
|
</div>
|
|
197
197
|
<div class="field">
|
|
@@ -201,11 +201,11 @@ export abstract class AbstractAuthPage extends localize(i18next)(LitElement) {
|
|
|
201
201
|
label=${String(i18next.t('field.password'))}
|
|
202
202
|
autocomplete="off"
|
|
203
203
|
required
|
|
204
|
-
><md-icon slot="leading-icon">
|
|
204
|
+
><md-icon slot="leading-icon">password</md-icon></md-filled-text-field
|
|
205
205
|
>
|
|
206
206
|
</div>
|
|
207
207
|
|
|
208
|
-
<md-elevated-button class="ui" type="
|
|
208
|
+
<md-elevated-button class="ui" type="button" raised @click=${e => this._onSubmit(e)}>
|
|
209
209
|
<ox-i18n msgid="field.${this.pageName}"> </ox-i18n>
|
|
210
210
|
</md-elevated-button>
|
|
211
211
|
`
|
|
@@ -235,7 +235,7 @@ export abstract class AbstractAuthPage extends localize(i18next)(LitElement) {
|
|
|
235
235
|
sso => html`
|
|
236
236
|
<a class="link" href=${sso.link}>
|
|
237
237
|
<md-text-button>
|
|
238
|
-
<md-icon slot="icon">
|
|
238
|
+
<md-icon slot="icon">id_card</md-icon>
|
|
239
239
|
${i18next.t('label.signin with', { title: sso.title })}
|
|
240
240
|
</md-text-button>
|
|
241
241
|
</a>
|
|
@@ -2,17 +2,16 @@ import '@material/web/icon/icon.js'
|
|
|
2
2
|
import '@material/web/button/elevated-button.js'
|
|
3
3
|
import '@material/web/textfield/filled-text-field.js'
|
|
4
4
|
|
|
5
|
-
import '@operato/lottie-player'
|
|
6
|
-
import '../components/profile-component'
|
|
7
5
|
import '@operato/i18n/ox-i18n.js'
|
|
8
6
|
import '@operato/i18n/ox-i18n-selector.js'
|
|
9
7
|
import '@operato/layout/ox-snack-bar.js'
|
|
10
8
|
|
|
9
|
+
import '../components/profile-component'
|
|
10
|
+
|
|
11
11
|
import { css, html, nothing } from 'lit'
|
|
12
12
|
import { property, query } from 'lit/decorators.js'
|
|
13
13
|
|
|
14
14
|
import { i18next } from '@operato/i18n'
|
|
15
|
-
import { isSafari } from '@operato/utils'
|
|
16
15
|
|
|
17
16
|
import { AUTH_STYLE_SIGN } from '../auth-style-sign'
|
|
18
17
|
import { generatePasswordPatternHelp, generatePasswordPatternRegExp } from '../utils/password-rule'
|
|
@@ -91,7 +90,8 @@ export abstract class AbstractPasswordReset extends AbstractAuthPage {
|
|
|
91
90
|
this.confirmPass.setAttribute('pattern', val.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '[$&]'))
|
|
92
91
|
}}
|
|
93
92
|
required
|
|
94
|
-
|
|
93
|
+
><md-icon slot="leading-icon">password</md-icon></md-filled-text-field
|
|
94
|
+
>
|
|
95
95
|
</div>
|
|
96
96
|
|
|
97
97
|
<div class="field">
|
|
@@ -102,10 +102,11 @@ export abstract class AbstractPasswordReset extends AbstractAuthPage {
|
|
|
102
102
|
label=${String(i18next.t('field.confirm password'))}
|
|
103
103
|
autocomplete="off"
|
|
104
104
|
required
|
|
105
|
-
|
|
105
|
+
><md-icon slot="leading-icon">password</md-icon></md-filled-text-field
|
|
106
|
+
>
|
|
106
107
|
</div>
|
|
107
108
|
|
|
108
|
-
<md-elevated-button id="submit-button" type="
|
|
109
|
+
<md-elevated-button id="submit-button" type="button" @click=${e => this._onSubmit(e)}>
|
|
109
110
|
<ox-i18n msgid="${this.submitButtonLabel}"></ox-i18n>
|
|
110
111
|
</md-elevated-button>
|
|
111
112
|
|
|
@@ -128,20 +129,14 @@ export abstract class AbstractPasswordReset extends AbstractAuthPage {
|
|
|
128
129
|
</form>
|
|
129
130
|
</div>
|
|
130
131
|
</div>
|
|
131
|
-
<ox-snack-bar id="snackbar" level="error" .message=${this.message}></ox-snack-bar>
|
|
132
132
|
|
|
133
|
-
|
|
134
|
-
? html``
|
|
135
|
-
: html`
|
|
136
|
-
<div class="lottie-container">
|
|
137
|
-
<lottie-player autoplay loop src="../../assets/images/background-animation.json"></lottie-player>
|
|
138
|
-
</div>
|
|
139
|
-
`}
|
|
133
|
+
<ox-snack-bar id="snackbar" level="error" .message=${this.message}></ox-snack-bar>
|
|
140
134
|
`
|
|
141
135
|
}
|
|
142
136
|
|
|
143
137
|
updated(changed) {
|
|
144
138
|
super.updated(changed)
|
|
139
|
+
|
|
145
140
|
if (changed.has('data')) {
|
|
146
141
|
this.token = this.data.token
|
|
147
142
|
}
|
|
@@ -44,7 +44,7 @@ export abstract class AbstractSign extends AbstractAuthPage {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
get formfields() {
|
|
47
|
-
const
|
|
47
|
+
const username = this.data?.username || ''
|
|
48
48
|
const autocompletable = this.autocompletable
|
|
49
49
|
|
|
50
50
|
return html`
|
|
@@ -52,22 +52,23 @@ export abstract class AbstractSign extends AbstractAuthPage {
|
|
|
52
52
|
|
|
53
53
|
<div class="field">
|
|
54
54
|
<md-filled-text-field
|
|
55
|
-
name="
|
|
56
|
-
type="
|
|
57
|
-
label=${String(i18next.t('field.email'))}
|
|
55
|
+
name="username"
|
|
56
|
+
type="text"
|
|
57
|
+
label=${String(i18next.t('field.user-id or email'))}
|
|
58
58
|
required
|
|
59
|
-
.value=${
|
|
60
|
-
autocomplete=${autocompletable ?
|
|
59
|
+
.value=${username}
|
|
60
|
+
autocomplete=${autocompletable ? 'username' : 'off'}
|
|
61
61
|
autocapitalize="off"
|
|
62
|
+
pattern="^(?:[A-Za-z0-9]*|[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,})$"
|
|
62
63
|
@input=${(e: Event) => {
|
|
63
64
|
const target = e.target as HTMLInputElement
|
|
64
65
|
if (target.validity.typeMismatch) {
|
|
65
|
-
target.setCustomValidity(i18next.t('text.invalid-
|
|
66
|
+
target.setCustomValidity(i18next.t('text.invalid-username'))
|
|
66
67
|
} else {
|
|
67
68
|
target.setCustomValidity('')
|
|
68
69
|
}
|
|
69
70
|
}}
|
|
70
|
-
><md-icon slot="leading-icon">
|
|
71
|
+
><md-icon slot="leading-icon">id_card</md-icon></md-filled-text-field
|
|
71
72
|
>
|
|
72
73
|
</div>
|
|
73
74
|
<div class="field">
|
|
@@ -75,14 +76,14 @@ export abstract class AbstractSign extends AbstractAuthPage {
|
|
|
75
76
|
name="password"
|
|
76
77
|
type="password"
|
|
77
78
|
label=${String(i18next.t('field.password'))}
|
|
78
|
-
autocomplete=${autocompletable ?
|
|
79
|
+
autocomplete=${autocompletable ? 'current-password' : 'off'}
|
|
79
80
|
required
|
|
80
|
-
><md-icon slot="leading-icon">
|
|
81
|
+
><md-icon slot="leading-icon">password</md-icon></md-filled-text-field
|
|
81
82
|
>
|
|
82
83
|
</div>
|
|
83
84
|
|
|
84
85
|
<div class="submit-buttons-container">
|
|
85
|
-
<md-elevated-button class="submit-button" type="
|
|
86
|
+
<md-elevated-button class="submit-button" type="button" raised @click=${e => this._onSubmit(e)}>
|
|
86
87
|
<ox-i18n msgid="field.${this.pageName}"> </ox-i18n>
|
|
87
88
|
</md-elevated-button>
|
|
88
89
|
${isAvailableWebauthn
|
|
@@ -106,7 +107,7 @@ export abstract class AbstractSign extends AbstractAuthPage {
|
|
|
106
107
|
|
|
107
108
|
if (verification.verified) {
|
|
108
109
|
const { redirectURL } = verification
|
|
109
|
-
|
|
110
|
+
|
|
110
111
|
if (redirectURL) {
|
|
111
112
|
window.location.href = redirectURL
|
|
112
113
|
}
|
|
@@ -116,7 +117,6 @@ export abstract class AbstractSign extends AbstractAuthPage {
|
|
|
116
117
|
message: verification.message
|
|
117
118
|
})
|
|
118
119
|
}
|
|
119
|
-
|
|
120
120
|
} catch (error) {
|
|
121
121
|
notify({
|
|
122
122
|
level: 'error',
|
|
@@ -33,22 +33,23 @@ export class ContactUs extends localize(i18next)(LitElement) {
|
|
|
33
33
|
]
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
@query('#dialog') dialog!: HTMLElement & {
|
|
36
|
+
@query('#dialog') dialog!: HTMLElement & { show: () => void }
|
|
37
37
|
@query('#subject-input') subjectInput!: HTMLInputElement
|
|
38
38
|
@query('#sender-input') senderInput!: HTMLInputElement
|
|
39
39
|
@query('#content-input') contentInput!: HTMLInputElement
|
|
40
40
|
|
|
41
41
|
render() {
|
|
42
42
|
return html`
|
|
43
|
-
<md-elevated-button @click=${e =>
|
|
43
|
+
<md-elevated-button @click=${e => this.dialog.show()}>${i18next.t('button.need help')}</md-elevated-button>
|
|
44
44
|
|
|
45
45
|
<md-dialog id="dialog" heading=${i18next.t('title.need help')}>
|
|
46
|
-
<form action="" method="
|
|
46
|
+
<form action="" method="post">
|
|
47
47
|
<input id="subject-input" name="subject" type="hidden" />
|
|
48
48
|
<input id="sender-input" name="sender" type="hidden" />
|
|
49
49
|
<input id="content-input" name="content" type="hidden" />
|
|
50
50
|
</form>
|
|
51
|
-
|
|
51
|
+
|
|
52
|
+
<div id="input-form" slot="content">
|
|
52
53
|
<md-filled-text-field
|
|
53
54
|
type="text"
|
|
54
55
|
label=${i18next.t('label.subject')}
|
|
@@ -59,6 +60,7 @@ export class ContactUs extends localize(i18next)(LitElement) {
|
|
|
59
60
|
this.subjectInput.value = val
|
|
60
61
|
}}
|
|
61
62
|
></md-filled-text-field>
|
|
63
|
+
|
|
62
64
|
<md-filled-text-field
|
|
63
65
|
type="text"
|
|
64
66
|
name="sender"
|
|
@@ -69,6 +71,7 @@ export class ContactUs extends localize(i18next)(LitElement) {
|
|
|
69
71
|
this.senderInput.value = val
|
|
70
72
|
}}
|
|
71
73
|
></md-filled-text-field>
|
|
74
|
+
|
|
72
75
|
<md-filled-text-field
|
|
73
76
|
name="content"
|
|
74
77
|
type="textarea"
|
|
@@ -80,11 +83,11 @@ export class ContactUs extends localize(i18next)(LitElement) {
|
|
|
80
83
|
this.contentInput.value = val
|
|
81
84
|
}}
|
|
82
85
|
></md-filled-text-field>
|
|
86
|
+
|
|
87
|
+
<md-elevated-button slot="primaryAction" type="button" @click=${e => this._submit(e)}
|
|
88
|
+
>${i18next.t('button.submit')}</md-elevated-button
|
|
89
|
+
>
|
|
83
90
|
</div>
|
|
84
|
-
<md-elevated-button slot="primaryAction" type="submit" @click=${e => this._submit(e)}
|
|
85
|
-
>${i18next.t('button.submit')}</md-elevated-button
|
|
86
|
-
>
|
|
87
|
-
<md-text-button slot="secondaryAction" dialogAction="cancel">${i18next.t('button.cancel')}</md-text-button>
|
|
88
91
|
</md-dialog>
|
|
89
92
|
`
|
|
90
93
|
}
|
|
@@ -6,6 +6,10 @@ import { customElement, query } from 'lit/decorators.js'
|
|
|
6
6
|
|
|
7
7
|
import { i18next } from '@operato/i18n'
|
|
8
8
|
|
|
9
|
+
function capitalize(str) {
|
|
10
|
+
return str ? str.charAt(0).toUpperCase() + str.slice(1) : ''
|
|
11
|
+
}
|
|
12
|
+
|
|
9
13
|
@customElement('create-user')
|
|
10
14
|
class CreateUser extends LitElement {
|
|
11
15
|
static styles = css`
|
|
@@ -18,7 +22,7 @@ class CreateUser extends LitElement {
|
|
|
18
22
|
box-shadow: var(--box-shadow);
|
|
19
23
|
|
|
20
24
|
display: grid;
|
|
21
|
-
grid-template-columns: 1fr 2fr auto;
|
|
25
|
+
grid-template-columns: 1fr 2fr 2fr auto;
|
|
22
26
|
gap: 5px 15px;
|
|
23
27
|
clear: both;
|
|
24
28
|
max-width: var(--input-container-max-width);
|
|
@@ -44,26 +48,42 @@ class CreateUser extends LitElement {
|
|
|
44
48
|
`
|
|
45
49
|
|
|
46
50
|
@query('[name=name]') nameInput!: HTMLInputElement
|
|
51
|
+
@query('[name=username]') usernameInput!: HTMLInputElement
|
|
47
52
|
@query('[name=email]') emailInput!: HTMLInputElement
|
|
48
53
|
|
|
49
54
|
render() {
|
|
50
55
|
return html`
|
|
56
|
+
<md-filled-text-field
|
|
57
|
+
type="text"
|
|
58
|
+
name="username"
|
|
59
|
+
label=${capitalize(i18next.t('label.user-id'))}
|
|
60
|
+
pattern="^(?:[A-Za-z0-9]*|[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,})$"
|
|
61
|
+
><md-icon slot="leading-icon">badge</md-icon></md-filled-text-field
|
|
62
|
+
>
|
|
63
|
+
|
|
51
64
|
<md-filled-text-field
|
|
52
65
|
type="text"
|
|
53
66
|
name="name"
|
|
54
|
-
label=${
|
|
55
|
-
|
|
67
|
+
label=${capitalize(i18next.t('label.x name', { x: i18next.t('label.user') }))}
|
|
68
|
+
><md-icon slot="leading-icon">id_card</md-icon></md-filled-text-field
|
|
69
|
+
>
|
|
56
70
|
|
|
57
|
-
<md-filled-text-field type="email" name="email" label=${
|
|
71
|
+
<md-filled-text-field type="email" name="email" label=${capitalize(i18next.t('field.email'))}
|
|
72
|
+
><md-icon slot="leading-icon">mail</md-icon></md-filled-text-field
|
|
73
|
+
>
|
|
58
74
|
|
|
59
75
|
<md-outlined-button @click=${this.onCreateUser.bind(this)}
|
|
60
|
-
>${
|
|
76
|
+
>${capitalize(i18next.t('button.create'))}</md-outlined-button
|
|
61
77
|
>
|
|
62
78
|
`
|
|
63
79
|
}
|
|
64
80
|
|
|
65
81
|
async onCreateUser() {
|
|
66
82
|
try {
|
|
83
|
+
if (!this.usernameInput.value) {
|
|
84
|
+
throw new Error(i18next.t('error.value is empty', { value: 'name' }))
|
|
85
|
+
}
|
|
86
|
+
|
|
67
87
|
if (!this.emailInput.checkValidity()) {
|
|
68
88
|
throw new Error(i18next.t('error.not valid pattern of type', { type: 'e-mail' }))
|
|
69
89
|
}
|
|
@@ -73,12 +93,14 @@ class CreateUser extends LitElement {
|
|
|
73
93
|
}
|
|
74
94
|
|
|
75
95
|
const user = {
|
|
96
|
+
username: this.usernameInput.value.trim(),
|
|
76
97
|
name: this.nameInput.value.trim(),
|
|
77
98
|
email: this.emailInput.value.trim()
|
|
78
99
|
}
|
|
79
100
|
|
|
80
101
|
await this.dispatchEvent(new CustomEvent('create-user', { detail: user }))
|
|
81
102
|
|
|
103
|
+
this.usernameInput.value = ''
|
|
82
104
|
this.nameInput.value = ''
|
|
83
105
|
this.emailInput.value = ''
|
|
84
106
|
} catch (e: any) {
|
|
@@ -28,14 +28,23 @@ class InviteUser extends localize(i18next)(LitElement) {
|
|
|
28
28
|
|
|
29
29
|
md-outlined-button {
|
|
30
30
|
margin: var(--input-margin);
|
|
31
|
+
text-transform: capitalize;
|
|
31
32
|
}
|
|
32
33
|
`
|
|
33
34
|
|
|
34
|
-
@query('input[name=
|
|
35
|
+
@query('input[name=username]') userIdInput!: HTMLInputElement
|
|
35
36
|
|
|
36
37
|
render() {
|
|
37
38
|
return html`
|
|
38
|
-
<input
|
|
39
|
+
<input
|
|
40
|
+
name="username"
|
|
41
|
+
type="text"
|
|
42
|
+
required
|
|
43
|
+
name="username"
|
|
44
|
+
autocapitalize="off"
|
|
45
|
+
placeholder=${String(i18next.t('text.user invitation prompt'))}
|
|
46
|
+
pattern="^(?:[A-Za-z0-9]*|[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,})$"
|
|
47
|
+
/>
|
|
39
48
|
<md-outlined-button @click=${this.invite.bind(this)}>
|
|
40
49
|
<md-icon slot="icon">group_add</md-icon>${String(i18next.t('label.invite user'))}
|
|
41
50
|
</md-outlined-button>
|
|
@@ -44,8 +53,8 @@ class InviteUser extends localize(i18next)(LitElement) {
|
|
|
44
53
|
|
|
45
54
|
async invite() {
|
|
46
55
|
try {
|
|
47
|
-
if (!this.
|
|
48
|
-
throw new Error(i18next.t('error.not valid pattern of type', { type: '
|
|
56
|
+
if (!this.userIdInput.checkValidity()) {
|
|
57
|
+
throw new Error(i18next.t('error.not valid pattern of type', { type: 'user-id or email' }))
|
|
49
58
|
}
|
|
50
59
|
|
|
51
60
|
if (
|
|
@@ -56,7 +65,7 @@ class InviteUser extends localize(i18next)(LitElement) {
|
|
|
56
65
|
cancelButton: { text: i18next.t('button.cancel') }
|
|
57
66
|
})
|
|
58
67
|
) {
|
|
59
|
-
await this.inviteUser(this.
|
|
68
|
+
await this.inviteUser(this.userIdInput.value)
|
|
60
69
|
|
|
61
70
|
this.dispatchEvent(new CustomEvent('invitationCompleted'))
|
|
62
71
|
}
|
|
@@ -72,14 +81,14 @@ class InviteUser extends localize(i18next)(LitElement) {
|
|
|
72
81
|
}
|
|
73
82
|
}
|
|
74
83
|
|
|
75
|
-
async inviteUser(
|
|
84
|
+
async inviteUser(username) {
|
|
76
85
|
const response = await client.mutate({
|
|
77
86
|
mutation: gql`
|
|
78
|
-
mutation inviteUser($
|
|
79
|
-
inviteUser(
|
|
87
|
+
mutation inviteUser($username: String!) {
|
|
88
|
+
inviteUser(username: $username)
|
|
80
89
|
}
|
|
81
90
|
`,
|
|
82
|
-
variables: {
|
|
91
|
+
variables: { username },
|
|
83
92
|
context: gqlContext()
|
|
84
93
|
})
|
|
85
94
|
|
|
@@ -89,7 +98,7 @@ class InviteUser extends localize(i18next)(LitElement) {
|
|
|
89
98
|
confirmButton: { text: i18next.t('button.confirm') }
|
|
90
99
|
})
|
|
91
100
|
|
|
92
|
-
this.
|
|
101
|
+
this.userIdInput.value = ''
|
|
93
102
|
}
|
|
94
103
|
}
|
|
95
104
|
}
|
|
@@ -75,11 +75,11 @@ class OwnershipTransferPopup extends localize(i18next)(LitElement) {
|
|
|
75
75
|
) {
|
|
76
76
|
const response = await client.mutate({
|
|
77
77
|
mutation: gql`
|
|
78
|
-
mutation transferOwner($
|
|
79
|
-
transferOwner(
|
|
78
|
+
mutation transferOwner($username: String!) {
|
|
79
|
+
transferOwner(username: $username)
|
|
80
80
|
}
|
|
81
81
|
`,
|
|
82
|
-
variables: {
|
|
82
|
+
variables: { username: this.user.username || this.user.email },
|
|
83
83
|
context: gqlContext()
|
|
84
84
|
})
|
|
85
85
|
|
|
@@ -88,6 +88,10 @@ export class ProfileComponent extends localize(i18next)(LitElement) {
|
|
|
88
88
|
margin: var(--change-password-field-margin);
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
md-text-button {
|
|
92
|
+
text-transform: capitalize;
|
|
93
|
+
}
|
|
94
|
+
|
|
91
95
|
footer {
|
|
92
96
|
padding: 20px;
|
|
93
97
|
text-align: center;
|
|
@@ -107,7 +111,7 @@ export class ProfileComponent extends localize(i18next)(LitElement) {
|
|
|
107
111
|
`
|
|
108
112
|
]
|
|
109
113
|
|
|
110
|
-
@property({ type: String })
|
|
114
|
+
@property({ type: String }) username?: string
|
|
111
115
|
@property({ type: String }) email?: string
|
|
112
116
|
@property({ type: String }) name?: string
|
|
113
117
|
|
|
@@ -124,7 +128,9 @@ export class ProfileComponent extends localize(i18next)(LitElement) {
|
|
|
124
128
|
looseCharacterLength?: number
|
|
125
129
|
} = {}
|
|
126
130
|
|
|
131
|
+
@query('#username') usernameEl!: HTMLInputElement
|
|
127
132
|
@query('#name') nameEl!: HTMLInputElement
|
|
133
|
+
@query('#email') emailEl!: HTMLInputElement
|
|
128
134
|
@query('#locale') localeEl!: HTMLInputElement
|
|
129
135
|
|
|
130
136
|
async connectedCallback(): Promise<void> {
|
|
@@ -163,11 +169,11 @@ export class ProfileComponent extends localize(i18next)(LitElement) {
|
|
|
163
169
|
|
|
164
170
|
setCredential(credential) {
|
|
165
171
|
if (credential) {
|
|
166
|
-
this.
|
|
172
|
+
this.username = credential.username
|
|
167
173
|
this.name = credential.name
|
|
168
174
|
this.email = credential.email
|
|
169
175
|
} else {
|
|
170
|
-
this.
|
|
176
|
+
this.username = ''
|
|
171
177
|
this.name = ''
|
|
172
178
|
this.email = ''
|
|
173
179
|
}
|
|
@@ -176,13 +182,23 @@ export class ProfileComponent extends localize(i18next)(LitElement) {
|
|
|
176
182
|
render() {
|
|
177
183
|
return html`
|
|
178
184
|
<div class="wrap">
|
|
179
|
-
<div class="user">${this.
|
|
185
|
+
<div class="user">${this.username || ''}</div>
|
|
186
|
+
|
|
187
|
+
<label for="username"><ox-i18n slot="title" msgid="label.user-id"></ox-i18n></label>
|
|
188
|
+
<input id="username" @change=${e => this.onUsernameChanged(e.target.value)} .value=${this.username || ''} />
|
|
189
|
+
|
|
190
|
+
<hr />
|
|
180
191
|
|
|
181
192
|
<label for="name"><ox-i18n slot="title" msgid="label.name"></ox-i18n></label>
|
|
182
193
|
<input id="name" @change=${e => this.onNameChanged(e.target.value)} .value=${this.name || ''} />
|
|
183
194
|
|
|
184
195
|
<hr />
|
|
185
196
|
|
|
197
|
+
<label for="email"><ox-i18n slot="title" msgid="label.email"></ox-i18n></label>
|
|
198
|
+
<input id="email" type="email" @change=${e => this.onEmailChanged(e.target.value)} .value=${this.email || ''} />
|
|
199
|
+
|
|
200
|
+
<hr />
|
|
201
|
+
|
|
186
202
|
<label for="locale"><ox-i18n slot="title" msgid="label.language"></ox-i18n></label>
|
|
187
203
|
<ox-i18n-selector
|
|
188
204
|
id="locale"
|
|
@@ -219,6 +235,30 @@ export class ProfileComponent extends localize(i18next)(LitElement) {
|
|
|
219
235
|
`
|
|
220
236
|
}
|
|
221
237
|
|
|
238
|
+
async onUsernameChanged(username) {
|
|
239
|
+
if (!username) return
|
|
240
|
+
|
|
241
|
+
var oldUsername = this.username
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
const message = await auth.updateProfile({
|
|
245
|
+
username
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
notify({
|
|
249
|
+
level: 'info',
|
|
250
|
+
message
|
|
251
|
+
})
|
|
252
|
+
} catch (e: any) {
|
|
253
|
+
this.usernameEl.value = oldUsername || ''
|
|
254
|
+
|
|
255
|
+
notify({
|
|
256
|
+
level: 'error',
|
|
257
|
+
message: 'message' in e ? e.message : e
|
|
258
|
+
})
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
222
262
|
async onNameChanged(name) {
|
|
223
263
|
if (!name) return
|
|
224
264
|
|
|
@@ -243,6 +283,30 @@ export class ProfileComponent extends localize(i18next)(LitElement) {
|
|
|
243
283
|
}
|
|
244
284
|
}
|
|
245
285
|
|
|
286
|
+
async onEmailChanged(email) {
|
|
287
|
+
if (!email) return
|
|
288
|
+
|
|
289
|
+
var oldEmail = this.email
|
|
290
|
+
|
|
291
|
+
try {
|
|
292
|
+
const message = await auth.updateProfile({
|
|
293
|
+
email
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
notify({
|
|
297
|
+
level: 'info',
|
|
298
|
+
message
|
|
299
|
+
})
|
|
300
|
+
} catch (e: any) {
|
|
301
|
+
this.emailEl.value = oldEmail || ''
|
|
302
|
+
|
|
303
|
+
notify({
|
|
304
|
+
level: 'error',
|
|
305
|
+
message: 'message' in e ? e.message : e
|
|
306
|
+
})
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
246
310
|
async onLocaleChanged(value) {
|
|
247
311
|
if (!value) return
|
|
248
312
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import gql from 'graphql-tag'
|
|
2
2
|
import { css, html, LitElement } from 'lit'
|
|
3
|
-
import { customElement, property } from 'lit/decorators.js'
|
|
3
|
+
import { customElement, property, state } from 'lit/decorators.js'
|
|
4
4
|
|
|
5
5
|
import { client, gqlContext } from '@operato/graphql'
|
|
6
6
|
import { i18next, localize } from '@operato/i18n'
|
|
@@ -18,18 +18,32 @@ class RolePrivilegeEditor extends localize(i18next)(LitElement) {
|
|
|
18
18
|
|
|
19
19
|
border: 1px solid var(--md-sys-color-primary);
|
|
20
20
|
font: normal 15px var(--theme-font);
|
|
21
|
-
color: var(--md-sys-color-secondary);
|
|
22
21
|
}
|
|
22
|
+
|
|
23
23
|
div {
|
|
24
24
|
margin: var(--spacing-medium);
|
|
25
25
|
}
|
|
26
|
+
|
|
27
|
+
div[users] {
|
|
28
|
+
margin: 0;
|
|
29
|
+
background-color: var(--md-sys-color-surface-variant);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
div[titler] {
|
|
33
|
+
color: var(--md-sys-color-secondary);
|
|
34
|
+
font: bold 1.2em var(--theme-font);
|
|
35
|
+
text-transform: capitalize;
|
|
36
|
+
}
|
|
37
|
+
|
|
26
38
|
ul {
|
|
27
39
|
flex: 1;
|
|
40
|
+
color: var(--md-sys-color-secondary);
|
|
28
41
|
background-color: var(--md-sys-color-surface-variant);
|
|
29
42
|
overflow: auto;
|
|
30
43
|
display: grid;
|
|
31
44
|
grid-template-columns: 1fr 1fr;
|
|
32
|
-
margin: 0;
|
|
45
|
+
margin-block-start: 0;
|
|
46
|
+
margin-block-end: 0;
|
|
33
47
|
padding: var(--spacing-medium);
|
|
34
48
|
list-style: none;
|
|
35
49
|
border: 1px dashed rgba(0, 0, 0, 0.1);
|
|
@@ -70,14 +84,23 @@ class RolePrivilegeEditor extends localize(i18next)(LitElement) {
|
|
|
70
84
|
|
|
71
85
|
@property({ type: Object }) role: any
|
|
72
86
|
@property({ type: Array }) allPrivileges: any[] = []
|
|
73
|
-
@property({ type: Array }) privileges: any[] = []
|
|
74
87
|
@property({ type: Object }) updateRoleObj: any
|
|
75
88
|
|
|
89
|
+
@state() privileges: { id: string; description: string }[] = []
|
|
90
|
+
@state() users: { /* id: string; username: string; */ name: string; email: string }[] = []
|
|
91
|
+
|
|
76
92
|
render() {
|
|
77
93
|
const allPrivileges = this.allPrivileges || []
|
|
78
94
|
const privileges = this.privileges || []
|
|
95
|
+
const users = this.users || []
|
|
79
96
|
|
|
80
97
|
return html`
|
|
98
|
+
<div titler>${String(i18next.t('label.user'))}</div>
|
|
99
|
+
|
|
100
|
+
<div users>${users.map(user => html`<div>${user.name} (${user.email})</div>`)}</div>
|
|
101
|
+
|
|
102
|
+
<div titler>${String(i18next.t('field.privileges'))}</div>
|
|
103
|
+
|
|
81
104
|
<div>
|
|
82
105
|
<input id="checkAll" type="checkbox" @change=${e => this.oncheckAll(e)} />
|
|
83
106
|
<label for="checkAll">Check all</label>
|
|
@@ -114,7 +137,7 @@ class RolePrivilegeEditor extends localize(i18next)(LitElement) {
|
|
|
114
137
|
|
|
115
138
|
async updated(changes) {
|
|
116
139
|
if (changes.has('role')) {
|
|
117
|
-
|
|
140
|
+
await this.fetchPrivilegesOnRole(this.role.name)
|
|
118
141
|
}
|
|
119
142
|
}
|
|
120
143
|
|
|
@@ -224,6 +247,10 @@ class RolePrivilegeEditor extends localize(i18next)(LitElement) {
|
|
|
224
247
|
id
|
|
225
248
|
description
|
|
226
249
|
}
|
|
250
|
+
users {
|
|
251
|
+
name
|
|
252
|
+
email
|
|
253
|
+
}
|
|
227
254
|
}
|
|
228
255
|
}
|
|
229
256
|
`,
|
|
@@ -231,7 +258,8 @@ class RolePrivilegeEditor extends localize(i18next)(LitElement) {
|
|
|
231
258
|
context: gqlContext()
|
|
232
259
|
})
|
|
233
260
|
|
|
234
|
-
|
|
261
|
+
this.privileges = response.data.role?.privileges
|
|
262
|
+
this.users = response.data.role?.users
|
|
235
263
|
}
|
|
236
264
|
|
|
237
265
|
showToast(message) {
|