@live-change/user-frontend 0.3.23 → 0.3.24
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/front/components.d.ts +13 -0
- package/front/src/NavBar.vue +6 -3
- package/front/src/password/ChangePassword.vue +35 -30
- package/front/src/password/Password.vue +498 -0
- package/front/src/sign/SignInFinished.vue +2 -0
- package/front/src/sign/SignUpFinished.vue +111 -2
- package/front/src/sign/routes.js +2 -2
- package/index.js +4 -1
- package/package.json +20 -20
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/* prettier-ignore */
|
|
3
|
+
// @ts-nocheck
|
|
4
|
+
// Generated by unplugin-vue-components
|
|
5
|
+
// Read more: https://github.com/vuejs/core/pull/3399
|
|
6
|
+
export {}
|
|
7
|
+
|
|
8
|
+
declare module 'vue' {
|
|
9
|
+
export interface GlobalComponents {
|
|
10
|
+
RouterLink: typeof import('vue-router')['RouterLink']
|
|
11
|
+
RouterView: typeof import('vue-router')['RouterView']
|
|
12
|
+
}
|
|
13
|
+
}
|
package/front/src/NavBar.vue
CHANGED
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
<div class="surface-overlay py-3 px-6 shadow-2 flex align-items-center justify-content-between
|
|
3
3
|
relative md:sticky top-0 z-5"
|
|
4
4
|
style="min-height: 80px" key="navbar">
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
<router-link to="/">
|
|
7
|
+
<img src="/images/logo.svg" alt="Image" height="40" class="mr-0 lg:mr-6">
|
|
8
|
+
</router-link>
|
|
6
9
|
<div class="hidden lg:flex">
|
|
7
10
|
<!-- place for desktop menu -->
|
|
8
11
|
</div>
|
|
9
12
|
<div class="flex flex-grow-1"></div>
|
|
10
13
|
<UserIcon />
|
|
11
14
|
|
|
12
|
-
|
|
13
15
|
<div class="static w-auto w-full surface-overlay left-0 top-100 z-1 shadow-none hidden">
|
|
14
16
|
<ul class="list-none p-0 m-0 flex align-items-center select-none flex-row border-top-none">
|
|
15
17
|
<li>
|
|
@@ -27,11 +29,11 @@
|
|
|
27
29
|
</ul>
|
|
28
30
|
</div>
|
|
29
31
|
|
|
30
|
-
|
|
31
32
|
<a v-ripple class="cursor-pointer block lg:hidden text-700 p-ripple ml-2 hover:surface-100 p-2"
|
|
32
33
|
v-styleclass="{ selector: '@next', enterClass: 'hidden', leaveToClass: 'hidden', hideOnOutsideClick: true }">
|
|
33
34
|
<i class="pi pi-bars text-4xl"></i>
|
|
34
35
|
</a>
|
|
36
|
+
|
|
35
37
|
<div class="align-items-center flex-grow-1 justify-content-between hidden absolute lg:static w-full surface-overlay left-0 top-100 z-1 shadow-2 lg:shadow-none">
|
|
36
38
|
<ul class="list-none p-0 m-0 flex lg:align-items-center select-none flex-column lg:flex-row">
|
|
37
39
|
<li>
|
|
@@ -109,6 +111,7 @@
|
|
|
109
111
|
</li>
|
|
110
112
|
</ul>
|
|
111
113
|
</div>
|
|
114
|
+
|
|
112
115
|
</div>
|
|
113
116
|
</template>
|
|
114
117
|
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
|
|
17
17
|
<div class="p-field mb-3" v-if="passwordExists">
|
|
18
18
|
<label for="currentPassword" class="block text-900 font-medium mb-2">Current password</label>
|
|
19
|
-
<Password id="currentPassword" class="w-full" inputClass="w-full"
|
|
19
|
+
<Password id="currentPassword" class="w-full" inputClass="w-full"
|
|
20
|
+
v-model:masked="masked"
|
|
20
21
|
:class="{ 'p-invalid': data.currentPasswordHashError }"
|
|
21
22
|
v-model="data.currentPasswordHash" />
|
|
22
23
|
<small id="currentPassword-help" class="p-error">{{ data.currentPasswordHashError }}</small>
|
|
@@ -24,7 +25,8 @@
|
|
|
24
25
|
|
|
25
26
|
<div class="p-field mb-3">
|
|
26
27
|
<label for="newPassword" class="block text-900 font-medium mb-2">New password</label>
|
|
27
|
-
<Password id="newPassword" class="w-full" inputClass="w-full"
|
|
28
|
+
<Password id="newPassword" class="w-full" inputClass="w-full"
|
|
29
|
+
v-model:masked="masked"
|
|
28
30
|
:class="{ 'p-invalid': data.passwordHashError }"
|
|
29
31
|
v-model="data.passwordHash">
|
|
30
32
|
<template #footer>
|
|
@@ -44,8 +46,9 @@
|
|
|
44
46
|
<div class="p-field mb-3">
|
|
45
47
|
<label for="reenterPassword" class="block text-900 font-medium mb-2">Re-enter password</label>
|
|
46
48
|
<Password id="reenterPassword" class="w-full" inputClass="w-full"
|
|
49
|
+
v-model:masked="masked"
|
|
47
50
|
v-model="secondPassword"
|
|
48
|
-
:feedback="false"
|
|
51
|
+
:feedback="false" />
|
|
49
52
|
</div>
|
|
50
53
|
|
|
51
54
|
</template>
|
|
@@ -62,42 +65,44 @@
|
|
|
62
65
|
|
|
63
66
|
<script setup>
|
|
64
67
|
|
|
65
|
-
import InputText from "primevue/inputtext"
|
|
66
|
-
import Checkbox from "primevue/checkbox"
|
|
67
|
-
import Button from "primevue/button"
|
|
68
|
-
import Divider from "primevue/divider"
|
|
69
|
-
import Password from "primevue/password"
|
|
70
|
-
import SettingsTabs from "../SettingsTabs.vue"
|
|
68
|
+
import InputText from "primevue/inputtext"
|
|
69
|
+
import Checkbox from "primevue/checkbox"
|
|
70
|
+
import Button from "primevue/button"
|
|
71
|
+
import Divider from "primevue/divider"
|
|
72
|
+
import Password from "primevue/password"
|
|
73
|
+
import SettingsTabs from "../SettingsTabs.vue"
|
|
71
74
|
|
|
72
|
-
import { live, path } from '@live-change/vue3-ssr'
|
|
73
|
-
import { computed, ref, onMounted } from 'vue'
|
|
75
|
+
import { live, path } from '@live-change/vue3-ssr'
|
|
76
|
+
import { computed, ref, onMounted } from 'vue'
|
|
74
77
|
|
|
75
|
-
import { useRouter } from 'vue-router'
|
|
76
|
-
const router = useRouter()
|
|
78
|
+
import { useRouter } from 'vue-router'
|
|
79
|
+
const router = useRouter()
|
|
77
80
|
|
|
78
|
-
const isMounted = ref(false)
|
|
79
|
-
onMounted(() => isMounted.value = true)
|
|
81
|
+
const isMounted = ref(false)
|
|
82
|
+
onMounted(() => isMounted.value = true)
|
|
80
83
|
|
|
81
|
-
const secondPassword = ref('')
|
|
82
|
-
const form = ref()
|
|
84
|
+
const secondPassword = ref('')
|
|
85
|
+
const form = ref()
|
|
83
86
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
const masked = ref(true)
|
|
88
|
+
|
|
89
|
+
onMounted(() => {
|
|
90
|
+
form.value.addValidator('passwordHash', () => {
|
|
91
|
+
const value = form.value.getFieldValue('passwordHash')
|
|
92
|
+
console.log("PASSWORDS MATCH?", secondPassword.value, value)
|
|
93
|
+
if(value != secondPassword.value) return "passwordsNotMatch"
|
|
94
|
+
})
|
|
89
95
|
})
|
|
90
|
-
})
|
|
91
96
|
|
|
92
97
|
|
|
93
|
-
const passwordExists = await live(path().passwordAuthentication.myUserPasswordAuthenticationExists())
|
|
98
|
+
const passwordExists = await live(path().passwordAuthentication.myUserPasswordAuthenticationExists())
|
|
94
99
|
|
|
95
|
-
function handleDone({ parameters, result }) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
100
|
+
function handleDone({ parameters, result }) {
|
|
101
|
+
console.log("FORM DONE", parameters, result)
|
|
102
|
+
router.push({
|
|
103
|
+
name: 'user:changePasswordFinished',
|
|
104
|
+
})
|
|
105
|
+
}
|
|
101
106
|
|
|
102
107
|
</script>
|
|
103
108
|
|
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="containerClass">
|
|
3
|
+
<PInputText
|
|
4
|
+
ref="input"
|
|
5
|
+
:id="inputId"
|
|
6
|
+
:type="inputType"
|
|
7
|
+
:class="inputFieldClass"
|
|
8
|
+
:style="inputStyle"
|
|
9
|
+
:value="modelValue"
|
|
10
|
+
:aria-labelledby="ariaLabelledby"
|
|
11
|
+
:aria-label="ariaLabel"
|
|
12
|
+
:aria-controls="(panelProps && panelProps.id) || panelId || panelUniqueId"
|
|
13
|
+
:aria-expanded="overlayVisible"
|
|
14
|
+
:aria-haspopup="true"
|
|
15
|
+
:placeholder="placeholder"
|
|
16
|
+
:required="required"
|
|
17
|
+
@input="onInput"
|
|
18
|
+
@focus="onFocus"
|
|
19
|
+
@blur="onBlur"
|
|
20
|
+
@keyup="onKeyUp"
|
|
21
|
+
@invalid="onInvalid"
|
|
22
|
+
v-bind="inputProps"
|
|
23
|
+
/>
|
|
24
|
+
<i v-if="toggleMask" :class="toggleIconClass" @click="onMaskToggle" />
|
|
25
|
+
<span class="p-hidden-accessible" aria-live="polite">
|
|
26
|
+
{{ infoText }}
|
|
27
|
+
</span>
|
|
28
|
+
<Portal :appendTo="appendTo">
|
|
29
|
+
<transition name="p-connected-overlay" @enter="onOverlayEnter" @leave="onOverlayLeave" @after-leave="onOverlayAfterLeave">
|
|
30
|
+
<div v-if="overlayVisible" :ref="overlayRef" :id="panelId || panelUniqueId" :class="panelStyleClass" :style="panelStyle" @click="onOverlayClick" v-bind="panelProps">
|
|
31
|
+
<slot name="header"></slot>
|
|
32
|
+
<slot name="content">
|
|
33
|
+
<div class="p-password-meter">
|
|
34
|
+
<div :class="strengthClass" :style="{ width: meter ? meter.width : '' }"></div>
|
|
35
|
+
</div>
|
|
36
|
+
<div class="p-password-info">{{ infoText }}</div>
|
|
37
|
+
</slot>
|
|
38
|
+
<slot name="footer"></slot>
|
|
39
|
+
</div>
|
|
40
|
+
</transition>
|
|
41
|
+
</Portal>
|
|
42
|
+
</div>
|
|
43
|
+
</template>
|
|
44
|
+
|
|
45
|
+
<script>
|
|
46
|
+
import InputText from 'primevue/inputtext';
|
|
47
|
+
import OverlayEventBus from 'primevue/overlayeventbus';
|
|
48
|
+
import Portal from 'primevue/portal';
|
|
49
|
+
import { ConnectedOverlayScrollHandler, DomHandler, UniqueComponentId, ZIndexUtils } from 'primevue/utils';
|
|
50
|
+
|
|
51
|
+
function absolutePosition(element, target) {
|
|
52
|
+
if (element) {
|
|
53
|
+
const elementDimensions = element.offsetParent ? { width: element.offsetWidth, height: element.offsetHeight } : this.getHiddenElementDimensions(element);
|
|
54
|
+
const elementOuterHeight = elementDimensions.height;
|
|
55
|
+
const elementOuterWidth = elementDimensions.width;
|
|
56
|
+
const targetOuterHeight = target.offsetHeight;
|
|
57
|
+
const targetOuterWidth = target.offsetWidth;
|
|
58
|
+
const targetOffset = target.getBoundingClientRect();
|
|
59
|
+
const windowScrollTop = DomHandler.getWindowScrollTop();
|
|
60
|
+
const windowScrollLeft = DomHandler.getWindowScrollLeft();
|
|
61
|
+
const viewport = DomHandler.getViewport();
|
|
62
|
+
let top, left;
|
|
63
|
+
|
|
64
|
+
//if (targetOffset.top + targetOuterHeight + elementOuterHeight > viewport.height) {
|
|
65
|
+
top = targetOffset.top + windowScrollTop - elementOuterHeight;
|
|
66
|
+
element.style.transformOrigin = 'bottom';
|
|
67
|
+
|
|
68
|
+
if (top < 0) {
|
|
69
|
+
top = windowScrollTop;
|
|
70
|
+
}
|
|
71
|
+
/* } else {
|
|
72
|
+
top = targetOuterHeight + targetOffset.top + windowScrollTop;
|
|
73
|
+
element.style.transformOrigin = 'top';
|
|
74
|
+
}*/
|
|
75
|
+
|
|
76
|
+
if (targetOffset.left + elementOuterWidth > viewport.width) left = Math.max(0, targetOffset.left + windowScrollLeft + targetOuterWidth - elementOuterWidth);
|
|
77
|
+
else left = targetOffset.left + windowScrollLeft;
|
|
78
|
+
|
|
79
|
+
element.style.top = top + 'px';
|
|
80
|
+
element.style.left = left + 'px';
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export default {
|
|
85
|
+
name: 'Password',
|
|
86
|
+
emits: ['update:modelValue', 'change', 'focus', 'blur', 'invalid', 'update:masked'],
|
|
87
|
+
props: {
|
|
88
|
+
modelValue: String,
|
|
89
|
+
promptLabel: {
|
|
90
|
+
type: String,
|
|
91
|
+
default: null
|
|
92
|
+
},
|
|
93
|
+
mediumRegex: {
|
|
94
|
+
type: String,
|
|
95
|
+
default: '^(((?=.*[a-z])(?=.*[A-Z]))|((?=.*[a-z])(?=.*[0-9]))|((?=.*[A-Z])(?=.*[0-9])))(?=.{6,})' // eslint-disable-line
|
|
96
|
+
},
|
|
97
|
+
strongRegex: {
|
|
98
|
+
type: String,
|
|
99
|
+
default: '^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.{8,})' // eslint-disable-line
|
|
100
|
+
},
|
|
101
|
+
weakLabel: {
|
|
102
|
+
type: String,
|
|
103
|
+
default: null
|
|
104
|
+
},
|
|
105
|
+
mediumLabel: {
|
|
106
|
+
type: String,
|
|
107
|
+
default: null
|
|
108
|
+
},
|
|
109
|
+
strongLabel: {
|
|
110
|
+
type: String,
|
|
111
|
+
default: null
|
|
112
|
+
},
|
|
113
|
+
feedback: {
|
|
114
|
+
type: Boolean,
|
|
115
|
+
default: true
|
|
116
|
+
},
|
|
117
|
+
appendTo: {
|
|
118
|
+
type: String,
|
|
119
|
+
default: 'body'
|
|
120
|
+
},
|
|
121
|
+
toggleMask: {
|
|
122
|
+
type: Boolean,
|
|
123
|
+
default: false
|
|
124
|
+
},
|
|
125
|
+
hideIcon: {
|
|
126
|
+
type: String,
|
|
127
|
+
default: 'pi pi-eye-slash'
|
|
128
|
+
},
|
|
129
|
+
showIcon: {
|
|
130
|
+
type: String,
|
|
131
|
+
default: 'pi pi-eye'
|
|
132
|
+
},
|
|
133
|
+
disabled: {
|
|
134
|
+
type: Boolean,
|
|
135
|
+
default: false
|
|
136
|
+
},
|
|
137
|
+
placeholder: {
|
|
138
|
+
type: String,
|
|
139
|
+
default: null
|
|
140
|
+
},
|
|
141
|
+
required: {
|
|
142
|
+
type: Boolean,
|
|
143
|
+
default: false
|
|
144
|
+
},
|
|
145
|
+
inputId: {
|
|
146
|
+
type: String,
|
|
147
|
+
default: null
|
|
148
|
+
},
|
|
149
|
+
inputClass: {
|
|
150
|
+
type: [String, Object],
|
|
151
|
+
default: null
|
|
152
|
+
},
|
|
153
|
+
inputStyle: {
|
|
154
|
+
type: Object,
|
|
155
|
+
default: null
|
|
156
|
+
},
|
|
157
|
+
inputProps: {
|
|
158
|
+
type: null,
|
|
159
|
+
default: null
|
|
160
|
+
},
|
|
161
|
+
panelId: {
|
|
162
|
+
type: String,
|
|
163
|
+
default: null
|
|
164
|
+
},
|
|
165
|
+
panelClass: {
|
|
166
|
+
type: [String, Object],
|
|
167
|
+
default: null
|
|
168
|
+
},
|
|
169
|
+
panelStyle: {
|
|
170
|
+
type: Object,
|
|
171
|
+
default: null
|
|
172
|
+
},
|
|
173
|
+
panelProps: {
|
|
174
|
+
type: null,
|
|
175
|
+
default: null
|
|
176
|
+
},
|
|
177
|
+
'aria-labelledby': {
|
|
178
|
+
type: String,
|
|
179
|
+
default: null
|
|
180
|
+
},
|
|
181
|
+
'aria-label': {
|
|
182
|
+
type: String,
|
|
183
|
+
default: null
|
|
184
|
+
},
|
|
185
|
+
masked: {
|
|
186
|
+
type: Boolean,
|
|
187
|
+
default: true
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
data() {
|
|
191
|
+
return {
|
|
192
|
+
overlayVisible: false,
|
|
193
|
+
meter: null,
|
|
194
|
+
infoText: null,
|
|
195
|
+
focused: false,
|
|
196
|
+
unmasked: !this.masked
|
|
197
|
+
};
|
|
198
|
+
},
|
|
199
|
+
mediumCheckRegExp: null,
|
|
200
|
+
strongCheckRegExp: null,
|
|
201
|
+
resizeListener: null,
|
|
202
|
+
scrollHandler: null,
|
|
203
|
+
overlay: null,
|
|
204
|
+
mounted() {
|
|
205
|
+
this.infoText = this.promptText;
|
|
206
|
+
this.mediumCheckRegExp = new RegExp(this.mediumRegex);
|
|
207
|
+
this.strongCheckRegExp = new RegExp(this.strongRegex);
|
|
208
|
+
},
|
|
209
|
+
beforeUnmount() {
|
|
210
|
+
this.unbindResizeListener();
|
|
211
|
+
|
|
212
|
+
if (this.scrollHandler) {
|
|
213
|
+
this.scrollHandler.destroy();
|
|
214
|
+
this.scrollHandler = null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (this.overlay) {
|
|
218
|
+
ZIndexUtils.clear(this.overlay);
|
|
219
|
+
this.overlay = null;
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
methods: {
|
|
223
|
+
onOverlayEnter(el) {
|
|
224
|
+
ZIndexUtils.set('overlay', el, this.$primevue.config.zIndex.overlay);
|
|
225
|
+
this.alignOverlay();
|
|
226
|
+
this.bindScrollListener();
|
|
227
|
+
this.bindResizeListener();
|
|
228
|
+
},
|
|
229
|
+
onOverlayLeave() {
|
|
230
|
+
this.unbindScrollListener();
|
|
231
|
+
this.unbindResizeListener();
|
|
232
|
+
this.overlay = null;
|
|
233
|
+
},
|
|
234
|
+
onOverlayAfterLeave(el) {
|
|
235
|
+
ZIndexUtils.clear(el);
|
|
236
|
+
},
|
|
237
|
+
alignOverlay() {
|
|
238
|
+
if (this.appendTo === 'self') {
|
|
239
|
+
DomHandler.relativePosition(this.overlay, this.$refs.input.$el);
|
|
240
|
+
} else {
|
|
241
|
+
this.overlay.style.minWidth = DomHandler.getOuterWidth(this.$refs.input.$el) + 'px';
|
|
242
|
+
absolutePosition(this.overlay, this.$refs.input.$el);
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
testStrength(str) {
|
|
246
|
+
let level = 0;
|
|
247
|
+
|
|
248
|
+
if (this.strongCheckRegExp.test(str)) level = 3;
|
|
249
|
+
else if (this.mediumCheckRegExp.test(str)) level = 2;
|
|
250
|
+
else if (str.length) level = 1;
|
|
251
|
+
|
|
252
|
+
return level;
|
|
253
|
+
},
|
|
254
|
+
onInput(event) {
|
|
255
|
+
this.$emit('update:modelValue', event.target.value);
|
|
256
|
+
},
|
|
257
|
+
onFocus(event) {
|
|
258
|
+
this.focused = true;
|
|
259
|
+
|
|
260
|
+
if (this.feedback) {
|
|
261
|
+
this.setPasswordMeter(this.modelValue);
|
|
262
|
+
this.overlayVisible = true;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
this.$emit('focus', event);
|
|
266
|
+
},
|
|
267
|
+
onBlur(event) {
|
|
268
|
+
this.focused = false;
|
|
269
|
+
|
|
270
|
+
if (this.feedback) {
|
|
271
|
+
this.overlayVisible = false;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
this.$emit('blur', event);
|
|
275
|
+
},
|
|
276
|
+
onKeyUp(event) {
|
|
277
|
+
if (this.feedback) {
|
|
278
|
+
const value = event.target.value;
|
|
279
|
+
const { meter, label } = this.checkPasswordStrength(value);
|
|
280
|
+
|
|
281
|
+
this.meter = meter;
|
|
282
|
+
this.infoText = label;
|
|
283
|
+
|
|
284
|
+
if (event.code === 'Escape') {
|
|
285
|
+
this.overlayVisible && (this.overlayVisible = false);
|
|
286
|
+
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (!this.overlayVisible) {
|
|
291
|
+
this.overlayVisible = true;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
setPasswordMeter() {
|
|
296
|
+
if (!this.modelValue) return;
|
|
297
|
+
|
|
298
|
+
const { meter, label } = this.checkPasswordStrength(this.modelValue);
|
|
299
|
+
|
|
300
|
+
this.meter = meter;
|
|
301
|
+
this.infoText = label;
|
|
302
|
+
|
|
303
|
+
if (!this.overlayVisible) {
|
|
304
|
+
this.overlayVisible = true;
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
checkPasswordStrength(value) {
|
|
308
|
+
let label = null;
|
|
309
|
+
let meter = null;
|
|
310
|
+
|
|
311
|
+
switch (this.testStrength(value)) {
|
|
312
|
+
case 1:
|
|
313
|
+
label = this.weakText;
|
|
314
|
+
meter = {
|
|
315
|
+
strength: 'weak',
|
|
316
|
+
width: '33.33%'
|
|
317
|
+
};
|
|
318
|
+
break;
|
|
319
|
+
|
|
320
|
+
case 2:
|
|
321
|
+
label = this.mediumText;
|
|
322
|
+
meter = {
|
|
323
|
+
strength: 'medium',
|
|
324
|
+
width: '66.66%'
|
|
325
|
+
};
|
|
326
|
+
break;
|
|
327
|
+
|
|
328
|
+
case 3:
|
|
329
|
+
label = this.strongText;
|
|
330
|
+
meter = {
|
|
331
|
+
strength: 'strong',
|
|
332
|
+
width: '100%'
|
|
333
|
+
};
|
|
334
|
+
break;
|
|
335
|
+
|
|
336
|
+
default:
|
|
337
|
+
label = this.promptText;
|
|
338
|
+
meter = null;
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return { label, meter };
|
|
343
|
+
},
|
|
344
|
+
onInvalid(event) {
|
|
345
|
+
this.$emit('invalid', event);
|
|
346
|
+
},
|
|
347
|
+
bindScrollListener() {
|
|
348
|
+
if (!this.scrollHandler) {
|
|
349
|
+
this.scrollHandler = new ConnectedOverlayScrollHandler(this.$refs.input.$el, () => {
|
|
350
|
+
if (this.overlayVisible) {
|
|
351
|
+
this.overlayVisible = false;
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
this.scrollHandler.bindScrollListener();
|
|
357
|
+
},
|
|
358
|
+
unbindScrollListener() {
|
|
359
|
+
if (this.scrollHandler) {
|
|
360
|
+
this.scrollHandler.unbindScrollListener();
|
|
361
|
+
}
|
|
362
|
+
},
|
|
363
|
+
bindResizeListener() {
|
|
364
|
+
if (!this.resizeListener) {
|
|
365
|
+
this.resizeListener = () => {
|
|
366
|
+
if (this.overlayVisible && !DomHandler.isTouchDevice()) {
|
|
367
|
+
this.overlayVisible = false;
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
window.addEventListener('resize', this.resizeListener);
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
unbindResizeListener() {
|
|
375
|
+
if (this.resizeListener) {
|
|
376
|
+
window.removeEventListener('resize', this.resizeListener);
|
|
377
|
+
this.resizeListener = null;
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
overlayRef(el) {
|
|
381
|
+
this.overlay = el;
|
|
382
|
+
},
|
|
383
|
+
onMaskToggle() {
|
|
384
|
+
this.unmasked = !this.unmasked;
|
|
385
|
+
this.$emit('update:masked', !this.unmasked);
|
|
386
|
+
},
|
|
387
|
+
onOverlayClick(event) {
|
|
388
|
+
OverlayEventBus.emit('overlay-click', {
|
|
389
|
+
originalEvent: event,
|
|
390
|
+
target: this.$el
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
computed: {
|
|
395
|
+
containerClass() {
|
|
396
|
+
return [
|
|
397
|
+
'p-password p-component p-inputwrapper',
|
|
398
|
+
{
|
|
399
|
+
'p-inputwrapper-filled': this.filled,
|
|
400
|
+
'p-inputwrapper-focus': this.focused,
|
|
401
|
+
'p-input-icon-right': this.toggleMask
|
|
402
|
+
}
|
|
403
|
+
];
|
|
404
|
+
},
|
|
405
|
+
inputFieldClass() {
|
|
406
|
+
return [
|
|
407
|
+
'p-password-input',
|
|
408
|
+
this.inputClass,
|
|
409
|
+
{
|
|
410
|
+
'p-disabled': this.disabled
|
|
411
|
+
}
|
|
412
|
+
];
|
|
413
|
+
},
|
|
414
|
+
panelStyleClass() {
|
|
415
|
+
return [
|
|
416
|
+
'p-password-panel p-component',
|
|
417
|
+
this.panelClass,
|
|
418
|
+
{
|
|
419
|
+
'p-input-filled': this.$primevue.config.inputStyle === 'filled',
|
|
420
|
+
'p-ripple-disabled': this.$primevue.config.ripple === false
|
|
421
|
+
}
|
|
422
|
+
];
|
|
423
|
+
},
|
|
424
|
+
toggleIconClass() {
|
|
425
|
+
return this.unmasked ? this.hideIcon : this.showIcon;
|
|
426
|
+
},
|
|
427
|
+
strengthClass() {
|
|
428
|
+
return `p-password-strength ${this.meter ? this.meter.strength : ''}`;
|
|
429
|
+
},
|
|
430
|
+
inputType() {
|
|
431
|
+
return this.unmasked ? 'text' : 'password';
|
|
432
|
+
},
|
|
433
|
+
filled() {
|
|
434
|
+
return this.modelValue != null && this.modelValue.toString().length > 0;
|
|
435
|
+
},
|
|
436
|
+
weakText() {
|
|
437
|
+
return this.weakLabel || this.$primevue.config.locale.weak;
|
|
438
|
+
},
|
|
439
|
+
mediumText() {
|
|
440
|
+
return this.mediumLabel || this.$primevue.config.locale.medium;
|
|
441
|
+
},
|
|
442
|
+
strongText() {
|
|
443
|
+
return this.strongLabel || this.$primevue.config.locale.strong;
|
|
444
|
+
},
|
|
445
|
+
promptText() {
|
|
446
|
+
return this.promptLabel || this.$primevue.config.locale.passwordPrompt;
|
|
447
|
+
},
|
|
448
|
+
panelUniqueId() {
|
|
449
|
+
return UniqueComponentId() + '_panel';
|
|
450
|
+
}
|
|
451
|
+
},
|
|
452
|
+
watch: {
|
|
453
|
+
masked(value) {
|
|
454
|
+
this.unmasked = !value;
|
|
455
|
+
},
|
|
456
|
+
},
|
|
457
|
+
components: {
|
|
458
|
+
PInputText: InputText,
|
|
459
|
+
Portal: Portal
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
</script>
|
|
463
|
+
|
|
464
|
+
<style>
|
|
465
|
+
.p-password {
|
|
466
|
+
position: relative;
|
|
467
|
+
display: inline-flex;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.p-password-panel {
|
|
471
|
+
position: absolute;
|
|
472
|
+
top: 0;
|
|
473
|
+
left: 0;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.p-password .p-password-panel {
|
|
477
|
+
min-width: 100%;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
.p-password-meter {
|
|
481
|
+
height: 10px;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
.p-password-strength {
|
|
485
|
+
height: 100%;
|
|
486
|
+
width: 0;
|
|
487
|
+
transition: width 1s ease-in-out;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.p-fluid .p-password {
|
|
491
|
+
display: flex;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.p-password-input::-ms-reveal,
|
|
495
|
+
.p-password-input::-ms-clear {
|
|
496
|
+
display: none;
|
|
497
|
+
}
|
|
498
|
+
</style>
|
|
@@ -1,14 +1,123 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="w-full lg:w-6 md:w-9" v-shared-element:form="{ duration: '300ms', includeChildren: true }">
|
|
3
3
|
<div class="surface-card border-round shadow-2 p-4">
|
|
4
|
-
<div class="text-
|
|
5
|
-
|
|
4
|
+
<div class="text-center mb-5">
|
|
5
|
+
<div class="text-900 text-3xl font-medium mb-3">
|
|
6
|
+
Signed Up
|
|
7
|
+
</div>
|
|
8
|
+
</div>
|
|
9
|
+
<p class="mt-0 p-0 line-height-3">
|
|
10
|
+
Congratulations! You have successfully created your account.
|
|
11
|
+
<span v-if="!needPassword">
|
|
12
|
+
You can now set password to secure your account.
|
|
13
|
+
</span>
|
|
14
|
+
<p v-else>
|
|
15
|
+
|
|
16
|
+
Setup your <router-link to="{ name: 'user:identificatio' }">profile</router-link>
|
|
17
|
+
or return to the <router-link to="/">index page</router-link>.
|
|
18
|
+
</p>
|
|
19
|
+
</p>
|
|
6
20
|
</div>
|
|
21
|
+
|
|
22
|
+
<div class="surface-card p-4 shadow-2 border-round mt-2" v-if="needPassword">
|
|
23
|
+
<div class="text-center mb-5">
|
|
24
|
+
<div class="text-900 text-3xl font-medium mb-3">
|
|
25
|
+
{{ passwordExists ? 'Change password' : 'Set password' }}
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<command-form service="passwordAuthentication"
|
|
30
|
+
:action="passwordExists ? 'changePassword' : 'setPassword'"
|
|
31
|
+
v-slot="{ data }" ref="form" @done="handleDone">
|
|
32
|
+
|
|
33
|
+
<template v-if="isMounted">
|
|
34
|
+
|
|
35
|
+
<div class="p-field mb-3">
|
|
36
|
+
<label for="newPassword" class="block text-900 font-medium mb-2">New password</label>
|
|
37
|
+
<Password id="newPassword" class="w-full" inputClass="w-full"
|
|
38
|
+
toggleMask v-model:masked="masked"
|
|
39
|
+
:class="{ 'p-invalid': data.passwordHashError }"
|
|
40
|
+
v-model="data.passwordHash">
|
|
41
|
+
<template #footer>
|
|
42
|
+
<Divider />
|
|
43
|
+
<p class="p-mt-2">Suggestions</p>
|
|
44
|
+
<ul class="p-pl-2 p-ml-2 p-mt-0" style="line-height: 1.5">
|
|
45
|
+
<li>At least one lowercase</li>
|
|
46
|
+
<li>At least one uppercase</li>
|
|
47
|
+
<li>At least one numeric</li>
|
|
48
|
+
<li>Minimum 8 characters</li>
|
|
49
|
+
</ul>
|
|
50
|
+
</template>
|
|
51
|
+
</Password>
|
|
52
|
+
<small id="newPassword-help" class="p-error">{{ data.passwordHashError }}</small>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<div class="p-field mb-3">
|
|
56
|
+
<label for="reenterPassword" class="block text-900 font-medium mb-2">Re-enter password</label>
|
|
57
|
+
<Password id="reenterPassword" class="w-full" inputClass="w-full"
|
|
58
|
+
toggleMask v-model:masked="masked"
|
|
59
|
+
v-model="secondPassword"
|
|
60
|
+
:feedback="false" />
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
</template>
|
|
64
|
+
|
|
65
|
+
<Button :label="passwordExists ? 'Change password' : 'Set password'"
|
|
66
|
+
type="submit"
|
|
67
|
+
icon="pi pi-key" class="w-full"></Button>
|
|
68
|
+
|
|
69
|
+
</command-form>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
|
|
7
73
|
</div>
|
|
8
74
|
</template>
|
|
9
75
|
|
|
10
76
|
<script setup>
|
|
11
77
|
|
|
78
|
+
import InputText from "primevue/inputtext"
|
|
79
|
+
import Checkbox from "primevue/checkbox"
|
|
80
|
+
import Button from "primevue/button"
|
|
81
|
+
import Divider from "primevue/divider"
|
|
82
|
+
import Password from "../password/Password.vue"
|
|
83
|
+
import SettingsTabs from "../SettingsTabs.vue"
|
|
84
|
+
|
|
85
|
+
import { live, path } from '@live-change/vue3-ssr'
|
|
86
|
+
import { computed, ref, onMounted } from 'vue'
|
|
87
|
+
|
|
88
|
+
import { useRouter } from 'vue-router'
|
|
89
|
+
const router = useRouter()
|
|
90
|
+
|
|
91
|
+
const isMounted = ref(false)
|
|
92
|
+
onMounted(() => isMounted.value = true)
|
|
93
|
+
|
|
94
|
+
const secondPassword = ref('')
|
|
95
|
+
const form = ref()
|
|
96
|
+
|
|
97
|
+
const masked = ref(true)
|
|
98
|
+
|
|
99
|
+
onMounted(() => {
|
|
100
|
+
form.value.addValidator('passwordHash', () => {
|
|
101
|
+
const value = form.value.getFieldValue('passwordHash')
|
|
102
|
+
console.log("PASSWORDS MATCH?", secondPassword.value, value)
|
|
103
|
+
if(value != secondPassword.value) return "passwordsNotMatch"
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
const [passwordExists, emails] = await Promise.all([
|
|
108
|
+
live(path().passwordAuthentication.myUserPasswordAuthenticationExists()),
|
|
109
|
+
live(path().email.myUserEmails())
|
|
110
|
+
])
|
|
111
|
+
|
|
112
|
+
const needPassword = computed(() => (!passwordExists.value && emails.value?.length > 0))
|
|
113
|
+
|
|
114
|
+
function handleDone({ parameters, result }) {
|
|
115
|
+
console.log("FORM DONE", parameters, result)
|
|
116
|
+
router.push({
|
|
117
|
+
name: 'user:changePasswordFinished',
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
|
|
12
121
|
</script>
|
|
13
122
|
|
|
14
123
|
<style>
|
package/front/src/sign/routes.js
CHANGED
|
@@ -6,12 +6,12 @@ export function routes(config = {}) {
|
|
|
6
6
|
route({ name: 'user:signIn', path: prefix + 'sign-in',
|
|
7
7
|
component: () => import("./SignIn.vue") }),
|
|
8
8
|
route({ name: 'user:signInFinished', path: prefix + 'sign-in-finished',
|
|
9
|
-
component: () => import("./SignInFinished.vue") }),
|
|
9
|
+
component: () => import("./SignInFinished.vue"), meta: { signedIn: true } }),
|
|
10
10
|
|
|
11
11
|
route({ name: 'user:signUp', path: prefix + 'sign-up',
|
|
12
12
|
component: () => import("./SignUp.vue") }),
|
|
13
13
|
route({ name: 'user:signUpFinished', path: prefix + 'sign-up-finished',
|
|
14
|
-
component: () => import("./SignUpFinished.vue") }),
|
|
14
|
+
component: () => import("./SignUpFinished.vue"), meta: { signedIn: true } }),
|
|
15
15
|
|
|
16
16
|
route({ name: 'user:signOut', path: prefix + 'sign-out',
|
|
17
17
|
component: () => import("./SignOut.vue") }),
|
package/index.js
CHANGED
|
@@ -10,4 +10,7 @@ export { NotificationsIcon, SimpleNotification, notificationTypes }
|
|
|
10
10
|
import UserIcon from "./front/src/nav/UserIcon.vue"
|
|
11
11
|
export { UserIcon }
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
import Password from "./front/src/password/Password.vue"
|
|
14
|
+
export { Password }
|
|
15
|
+
|
|
16
|
+
export * from "./front/src/router.js"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@live-change/user-frontend",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.24",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"memDev": "lcli memDev --enableSessions --initScript ./init.js --dbAccess",
|
|
6
6
|
"localDevInit": "rm tmp.db; lcli localDev --enableSessions --initScript ./init.js",
|
|
@@ -20,27 +20,27 @@
|
|
|
20
20
|
"debug": "node --inspect-brk server"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@live-change/cli": "0.7.
|
|
23
|
+
"@live-change/cli": "0.7.39",
|
|
24
24
|
"@live-change/dao": "0.5.22",
|
|
25
25
|
"@live-change/dao-vue3": "0.5.22",
|
|
26
26
|
"@live-change/dao-websocket": "0.5.22",
|
|
27
|
-
"@live-change/email-service": "0.3.
|
|
28
|
-
"@live-change/framework": "0.7.
|
|
29
|
-
"@live-change/identicon-service": "0.3.
|
|
30
|
-
"@live-change/image-frontend": "^0.3.
|
|
31
|
-
"@live-change/message-authentication-service": "0.3.
|
|
32
|
-
"@live-change/notification-service": "0.3.
|
|
33
|
-
"@live-change/password-authentication-service": "0.3.
|
|
27
|
+
"@live-change/email-service": "0.3.41",
|
|
28
|
+
"@live-change/framework": "0.7.39",
|
|
29
|
+
"@live-change/identicon-service": "0.3.41",
|
|
30
|
+
"@live-change/image-frontend": "^0.3.24",
|
|
31
|
+
"@live-change/message-authentication-service": "0.3.41",
|
|
32
|
+
"@live-change/notification-service": "0.3.41",
|
|
33
|
+
"@live-change/password-authentication-service": "0.3.41",
|
|
34
34
|
"@live-change/pattern": "0.2.1",
|
|
35
|
-
"@live-change/secret-code-service": "0.3.
|
|
36
|
-
"@live-change/secret-link-service": "0.3.
|
|
37
|
-
"@live-change/security-frontend": "^0.3.
|
|
38
|
-
"@live-change/security-service": "0.3.
|
|
39
|
-
"@live-change/session-service": "0.3.
|
|
40
|
-
"@live-change/timer-service": "0.3.
|
|
41
|
-
"@live-change/upload-service": "0.3.
|
|
42
|
-
"@live-change/user-identification-service": "0.3.
|
|
43
|
-
"@live-change/user-service": "0.3.
|
|
35
|
+
"@live-change/secret-code-service": "0.3.41",
|
|
36
|
+
"@live-change/secret-link-service": "0.3.41",
|
|
37
|
+
"@live-change/security-frontend": "^0.3.24",
|
|
38
|
+
"@live-change/security-service": "0.3.41",
|
|
39
|
+
"@live-change/session-service": "0.3.41",
|
|
40
|
+
"@live-change/timer-service": "0.3.41",
|
|
41
|
+
"@live-change/upload-service": "0.3.41",
|
|
42
|
+
"@live-change/user-identification-service": "0.3.41",
|
|
43
|
+
"@live-change/user-service": "0.3.41",
|
|
44
44
|
"@live-change/vue3-components": "0.2.34",
|
|
45
45
|
"@live-change/vue3-ssr": "0.2.34",
|
|
46
46
|
"@vueuse/core": "^10.4.1",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"wtfnode": "^0.9.1"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
|
-
"@live-change/codeceptjs-helper": "0.7.
|
|
69
|
+
"@live-change/codeceptjs-helper": "0.7.39",
|
|
70
70
|
"@wdio/selenium-standalone-service": "^8.15.0",
|
|
71
71
|
"codeceptjs": "^3.5.4",
|
|
72
72
|
"generate-password": "1.7.0",
|
|
@@ -78,5 +78,5 @@
|
|
|
78
78
|
"author": "",
|
|
79
79
|
"license": "BSD-3-Clause",
|
|
80
80
|
"description": "",
|
|
81
|
-
"gitHead": "
|
|
81
|
+
"gitHead": "1dd8f7ad59e7e40c25cfde3fed2854bf295d069c"
|
|
82
82
|
}
|