@live-change/user-frontend 0.0.3

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.
Files changed (82) hide show
  1. package/LICENSE +21 -0
  2. package/e2e/codecept.conf.js +60 -0
  3. package/e2e/connectEmailCode.test.js +61 -0
  4. package/e2e/connectEmailLink.test.js +60 -0
  5. package/e2e/delete.test.js +44 -0
  6. package/e2e/disconnectEmail.test.js +42 -0
  7. package/e2e/resetPasswordWithEmailCode.test.js +62 -0
  8. package/e2e/resetPasswordWithEmailLink.test.js +62 -0
  9. package/e2e/setPassword.test.js +70 -0
  10. package/e2e/signInEmailCode.test.js +52 -0
  11. package/e2e/signInEmailLink.test.js +52 -0
  12. package/e2e/signInEmailPassword.test.js +47 -0
  13. package/e2e/signOut.test.js +41 -0
  14. package/e2e/signUpEmailCode.test.js +41 -0
  15. package/e2e/signUpEmailLink.test.js +41 -0
  16. package/e2e/steps.d.ts +12 -0
  17. package/e2e/steps_file.js +89 -0
  18. package/front/index.html +11 -0
  19. package/front/public/favicon.ico +0 -0
  20. package/front/public/images/empty-photo.svg +38 -0
  21. package/front/public/images/empty-user-photo.svg +33 -0
  22. package/front/public/images/logo.svg +34 -0
  23. package/front/public/images/logo128.png +0 -0
  24. package/front/src/App.vue +31 -0
  25. package/front/src/Index.vue +14 -0
  26. package/front/src/NavBar.vue +103 -0
  27. package/front/src/SettingsTabs.vue +48 -0
  28. package/front/src/connected/Connect.vue +58 -0
  29. package/front/src/connected/ConnectFinished.vue +16 -0
  30. package/front/src/connected/Connected.vue +84 -0
  31. package/front/src/connected/routes.js +16 -0
  32. package/front/src/delete/Delete.vue +53 -0
  33. package/front/src/delete/DeleteFeedbackSent.vue +16 -0
  34. package/front/src/delete/DeleteFinished.vue +32 -0
  35. package/front/src/delete/routes.js +16 -0
  36. package/front/src/entry-client.js +6 -0
  37. package/front/src/entry-server.js +6 -0
  38. package/front/src/identification/IdentificationSettings.vue +116 -0
  39. package/front/src/identification/ObjectIdentification.vue +36 -0
  40. package/front/src/identification/UserIdentification.vue +101 -0
  41. package/front/src/identification/routes.js +12 -0
  42. package/front/src/message-auth/ConnectEmail.vue +105 -0
  43. package/front/src/message-auth/MessageLink.vue +95 -0
  44. package/front/src/message-auth/MessageSent.vue +103 -0
  45. package/front/src/message-auth/ResetPasswordEmail.vue +105 -0
  46. package/front/src/message-auth/SignInEmail.vue +105 -0
  47. package/front/src/message-auth/SignUpEmail.vue +105 -0
  48. package/front/src/message-auth/routes.js +25 -0
  49. package/front/src/notifications/NotificationButtons.vue +70 -0
  50. package/front/src/notifications/NotificationListPage.vue +22 -0
  51. package/front/src/notifications/NotificationsIcon.vue +75 -0
  52. package/front/src/notifications/NotificationsList.vue +144 -0
  53. package/front/src/notifications/NotificationsSettings.vue +117 -0
  54. package/front/src/notifications/SimpleNotification.vue +34 -0
  55. package/front/src/notifications/TestNotification.vue +25 -0
  56. package/front/src/notifications/UnknownNotification.vue +25 -0
  57. package/front/src/notifications/notificationTypes.js +11 -0
  58. package/front/src/notifications/routes.js +37 -0
  59. package/front/src/password/ChangePassword.vue +106 -0
  60. package/front/src/password/ChangePasswordFinished.vue +16 -0
  61. package/front/src/password/ResetPassword.vue +56 -0
  62. package/front/src/password/ResetPasswordFinished.vue +16 -0
  63. package/front/src/password/ResetPasswordForm.vue +118 -0
  64. package/front/src/password/routes.js +41 -0
  65. package/front/src/router.js +90 -0
  66. package/front/src/settings/Settings.vue +33 -0
  67. package/front/src/settings/SettingsIndex.vue +22 -0
  68. package/front/src/settings/SettingsMenu.vue +81 -0
  69. package/front/src/settings/SettingsMenuItem.vue +35 -0
  70. package/front/src/sign/SignIn.vue +93 -0
  71. package/front/src/sign/SignInFinished.vue +27 -0
  72. package/front/src/sign/SignOut.vue +37 -0
  73. package/front/src/sign/SignOutFinished.vue +16 -0
  74. package/front/src/sign/SignUp.vue +51 -0
  75. package/front/src/sign/SignUpFinished.vue +16 -0
  76. package/front/src/sign/routes.js +24 -0
  77. package/front/vite.config.js +11 -0
  78. package/index.js +11 -0
  79. package/package.json +87 -0
  80. package/server/init.js +53 -0
  81. package/server/security.config.js +53 -0
  82. package/server/services.config.js +74 -0
@@ -0,0 +1,75 @@
1
+ <template>
2
+ <a v-if="unreadNotificationsCount"
3
+ v-ripple
4
+ @click="showNotifications"
5
+ class="flex mx-2 px-3 p-3 py-3 align-items-center text-600 hover:text-900 hover:surface-100
6
+ font-medium border-round cursor-pointer transition-colors transition-duration-150 p-ripple">
7
+ <i class="pi pi-bell text-base text-2xl p-overlay-badge">
8
+ <Badge v-if="unreadNotificationsCount?.count" :value="unreadNotificationsCount?.count ?? 0"></Badge>
9
+ </i>
10
+ </a>
11
+ <OverlayPanel v-if="isMounted" ref="overlayPanel" class="notifications-panel">
12
+ <loading-zone suspense>
13
+ <template v-slot:loading>
14
+ <div class="flex align-items-center justify-content-center top-0 left-0 notifications-loading">
15
+ <ProgressSpinner animationDuration=".5s"/>
16
+ </div>
17
+ </template>
18
+ <template v-slot:default="{ isLoading }">
19
+ <working-zone>
20
+ <template v-slot:working>
21
+ <div class="fixed w-full h-full flex align-items-center justify-content-center top-0 left-0">
22
+ <ProgressSpinner animationDuration=".5s"/>
23
+ </div>
24
+ </template>
25
+ <template v-slot:default="{ isWorking }">
26
+ <div :style="(isWorking || isLoading) ? 'filter: blur(4px)' : ''" class="working-blur">
27
+ <NotificationsList />
28
+ </div>
29
+ </template>
30
+ </working-zone>
31
+ </template>
32
+ </loading-zone>
33
+ </OverlayPanel>
34
+ </template>
35
+
36
+ <script setup>
37
+
38
+ import Badge from "primevue/badge"
39
+ import OverlayPanel from 'primevue/overlaypanel'
40
+ import ProgressSpinner from "primevue/progressspinner"
41
+
42
+ import NotificationsList from "./NotificationsList.vue"
43
+
44
+ import { ref, onMounted } from 'vue'
45
+
46
+ const overlayPanel = ref()
47
+
48
+ const isMounted = ref(false)
49
+ onMounted(() => isMounted.value = true)
50
+
51
+ function showNotifications(event) {
52
+ overlayPanel.value.toggle(event)
53
+ }
54
+
55
+ import { live, path } from '@live-change/vue3-ssr'
56
+
57
+ const unreadNotificationsCount = await live(path().notification.myUnreadCount({ }))
58
+
59
+ </script>
60
+
61
+ <style>
62
+ .notifications-panel .p-overlaypanel-content {
63
+ padding: 0px;
64
+ max-height: calc(90vh - 50px);
65
+ overflow-y: auto;
66
+ }
67
+ .notifications-panel {
68
+ width: 500px;
69
+ max-width: 80%;
70
+ }
71
+ .notifications-loading {
72
+ height: 300px;
73
+ max-height: 80%;
74
+ }
75
+ </style>
@@ -0,0 +1,144 @@
1
+ <template>
2
+ <div class="flex align-items-center justify-content-between mb-1 px-3 pt-1">
3
+ <div class="text-900 font-medium text-xl">Notifications</div>
4
+ <div>
5
+ <Button @click="$refs.menu.toggle($event)"
6
+ icon="pi pi-ellipsis-v" class="p-button-text p-button-plain p-button-rounded" />
7
+ <Menu ref="menu" :popup="true" :model="menuItems"></Menu>
8
+ </div>
9
+ </div>
10
+ <ul class="list-none p-0 m-0 notifications">
11
+ <div v-for="(bucket, bucketIndex) in notificationsBuckets.buckets" :key="bucket.id"
12
+ :style="{ backgroundz: `hsl(${bucket.id * 11}, 100%, 80%)` }">
13
+ <div v-for="(notification, index) in bucket.data" :key="notification.id" :ref="el => bucket.domElements[index] = el"
14
+ class="notification border-bottom-1 surface-border"
15
+ :class="{ selected: selectedNotification == notification.to }">
16
+ <component :is="notificationComponent(notification)" :notification="notification" />
17
+ <Button @click="() => selectNotification(notification)"
18
+ icon="pi pi-ellipsis-h" class="p-button-rounded p-button-text notification-more-button" />
19
+ <NotificationButtons :notification="notification" />
20
+ </div>
21
+ </div>
22
+ <scroll-border placement="bottom"
23
+ :load="notificationsBuckets.loadBottom"
24
+ :canLoad="notificationsBuckets.canLoadBottom" />
25
+ </ul>
26
+ </template>
27
+
28
+ <script setup>
29
+ import Button from "primevue/button"
30
+ import Menu from "primevue/menu"
31
+
32
+ import ScrollBorder from 'vue3-scroll-border'
33
+
34
+ import { useToast } from 'primevue/usetoast'
35
+ import { useConfirm } from 'primevue/useconfirm'
36
+ const confirm = useConfirm()
37
+ const toast = useToast()
38
+
39
+
40
+ import { ref, inject } from 'vue'
41
+
42
+ const workingZone = inject('workingZone')
43
+
44
+ import {notificationTypes} from "./notificationTypes.js"
45
+
46
+ import NotificationButtons from "./NotificationButtons.vue"
47
+
48
+ import { path, live, actions, api, rangeBuckets, reverseRange } from '@live-change/vue3-ssr'
49
+
50
+ const notificationApi = actions().notification
51
+
52
+ function notificationComponent(notification) {
53
+ const known = notificationTypes[notification.notificationType]
54
+ if(known) return known.component
55
+ return notificationTypes.unknown.component
56
+ }
57
+
58
+ const selectedNotification = ref(null)
59
+ function selectNotification(notification) {
60
+ console.log("SELECT NOTIFICATION", notification)
61
+ selectedNotification.value = notification.to
62
+ }
63
+
64
+ const menuItems = [
65
+ {
66
+ label: 'Mark all as read',
67
+ icon: 'pi pi-check',
68
+ command: () => {
69
+ workingZone.addPromise('markNotification', (async () => {
70
+ await notificationApi.markAllAsRead({ })
71
+ toast.add({
72
+ severity: 'success', summary:' Notifications read',
73
+ detail:'all notifications have been marked as read', life: 3000
74
+ })
75
+ })())
76
+ }
77
+ },
78
+ {
79
+ label: 'Delete all',
80
+ icon: 'pi pi-times',
81
+ command: () => {
82
+ workingZone.addPromise('markNotification', (async () => {
83
+ await notificationApi.deleteAll({ })
84
+ toast.add({
85
+ severity: 'warn', summary: 'Notifications deleted',
86
+ detail: 'All notifications have been deleted', life: 3000
87
+ })
88
+ })())
89
+ }
90
+ },
91
+ ]
92
+
93
+ const wait = new Promise(r => setTimeout(r, 100))
94
+
95
+ const [ notificationsBuckets ] = await Promise.all([
96
+ rangeBuckets(
97
+ (range, p) => p.notification.myNotifications(reverseRange(range)),
98
+ { bucketSize: 10 }
99
+ )
100
+ ])
101
+
102
+ await wait
103
+
104
+ </script>
105
+
106
+ <style lang="scss">
107
+ .notifications {
108
+ .notification:last-child {
109
+ border-bottom: none;
110
+ }
111
+ .notification {
112
+ position: relative;
113
+ .notification-buttons {
114
+ visibility: hidden;
115
+ position: absolute;
116
+ right: 40px;
117
+ top: 5px;
118
+ //transform: translate(0, -50%);
119
+ }
120
+ .notification-more-button {
121
+ position: absolute;
122
+ right: 5px;
123
+ top: 2%;
124
+ }
125
+ }
126
+ .notification.selected {
127
+ .notification-buttons {
128
+ visibility: visible;
129
+ }
130
+ .notification-more-button {
131
+ visibility: hidden;
132
+ }
133
+ }
134
+ .notification:hover {
135
+ .notification-buttons {
136
+ visibility: visible;
137
+ }
138
+ .notification-more-button {
139
+ visibility: hidden;
140
+ }
141
+ }
142
+
143
+ }
144
+ </style>
@@ -0,0 +1,117 @@
1
+ <template>
2
+ <div class="w-full lg:w-6 md:w-9" v-shared-element:form="{ duration: '300ms', includeChildren: true }">
3
+ <div class="surface-card p-4 shadow-2 border-round">
4
+ <div>
5
+ <h1>Notifications settings</h1>
6
+ </div>
7
+ <div v-for="notificationType in settings">
8
+ <div>
9
+ <h2>{{ notificationType.type }}</h2>
10
+ </div>
11
+ <div>
12
+ <div v-for="contact in notificationType.contacts"
13
+ class="flex flex-row align-items-center mb-3">
14
+ <div class="flex-grow-1 md:mb-2">
15
+ <i class="pi" :class="contactTypesIcons[contact.contactType]"></i>
16
+ <span class="ml-2">{{ contactText(contact.contact, contact.contactType) }}</span>
17
+ </div>
18
+ <div class="">
19
+ <InputSwitch v-model="contact.setting.value.active"/>
20
+ </div>
21
+ </div>
22
+ </div>
23
+ </div>
24
+ </div>
25
+ </div>
26
+ </template>
27
+
28
+ <script setup>
29
+
30
+ import InputSwitch from 'primevue/inputswitch'
31
+
32
+ import pluralize from 'pluralize'
33
+ import { synchronized } from "@live-change/vue3-components"
34
+ import { computed, ref } from 'vue'
35
+ import { useToast } from 'primevue/usetoast'
36
+ const toast = useToast()
37
+
38
+ const checked = ref()
39
+
40
+ import { api as useApi, live, path, actions } from '@live-change/vue3-ssr'
41
+ const api = useApi()
42
+
43
+ const clientConfig = api.getServiceDefinition('notification')?.clientConfig
44
+
45
+ const notificationApi = actions().notification
46
+
47
+
48
+ const contactTypesIcons = {
49
+ email: 'pi-at',
50
+ web: 'pi-globe',
51
+ phone: 'pi-phone'
52
+ }
53
+ function contactText(contact, type) {
54
+ if(type == 'web') return 'Web'
55
+ return contact
56
+ }
57
+
58
+ const allContacts = await Promise.all(clientConfig.contactTypes.map(async contactType => {
59
+ const contactTypeUpper = contactType[0].toUpperCase() + contactType.slice(1)
60
+ const contactTypeLong = contactType + '_' + contactTypeUpper
61
+ let p = path()[contactType]['myUser' + pluralize(contactTypeUpper)]({})
62
+ p = p.with(contact =>
63
+ path().notification.contactOwnedNotificationSettings({
64
+ contactType: contactTypeLong, contact: contact[contactType]
65
+ }).bind('settings')
66
+ )
67
+ /*for(const notificationType of clientConfig.notificationTypes) {
68
+ p = p.with(contact => path().notification.contactAndNotificationOwnedNotificationSetting({
69
+ contactType, contact: contact[contactType], notificationType, notification: ''
70
+ }).bind(notificationType))
71
+ }*/
72
+ return {
73
+ type: contactType,
74
+ contactType,
75
+ contactTypeUpper,
76
+ contactTypeLong,
77
+ list: await live(p)
78
+ }
79
+ }))
80
+
81
+ const contacts = computed(() => {
82
+ const obj = {}
83
+ for(const type of allContacts) {
84
+ obj[type.type] = type.list.value
85
+ }
86
+ return obj
87
+ })
88
+
89
+ const settings = computed(() => clientConfig.notificationTypes.map(notificationType => {
90
+ const contacts = allContacts.map(contactsData => contactsData.list.value.map(contact => {
91
+ const contactType = contactsData.type
92
+ const settingSource = computed(() => contact.settings.find(s => s.notificationType == notificationType))
93
+ const setting = synchronized({
94
+ source: settingSource,
95
+ update: notificationApi.setOrUpdateContactAndNotificationOwnedNotificationSetting,
96
+ identifiers: {
97
+ contact: contact[contactType], contactType: contactsData.contactTypeLong,
98
+ notificationType, notification: notificationType
99
+ },
100
+ recursive: true,
101
+ onSave: () => toast.add({ severity: 'info', summary: 'Notification settings saved', life: 1500 })
102
+ }).value
103
+ return {
104
+ contactType,
105
+ contact: contact[contactType],
106
+ settingSource,
107
+ setting
108
+ }
109
+ })).flat()
110
+ return {
111
+ type: notificationType,
112
+ contacts
113
+ }
114
+ }))
115
+
116
+
117
+ </script>
@@ -0,0 +1,34 @@
1
+ <template>
2
+ <li class="px-3 py-2 flex align-items-start
3
+ justify-content-between flex-column hover:surface-100">
4
+ <slot></slot>
5
+ <span class="block text-500 font-medium mt-2">
6
+ {{ DateTime.fromISO(notification.time).toRelative({ base: DateTime.fromMillis(now) }) }}
7
+ </span>
8
+ </li>
9
+ </template>
10
+
11
+ <script setup>
12
+
13
+ const props = defineProps({
14
+ notification: {
15
+ type: Object,
16
+ required: true
17
+ },
18
+ image: {
19
+ type: String
20
+ }
21
+ })
22
+
23
+ import { useTimestamp, toRefs } from '@vueuse/core'
24
+
25
+ const { notification, image } = toRefs(props)
26
+
27
+ const now = useTimestamp({ interval: 1000 })
28
+ import { DateTime } from 'luxon'
29
+
30
+ </script>
31
+
32
+ <style scoped>
33
+
34
+ </style>
@@ -0,0 +1,25 @@
1
+ <template>
2
+ <SimpleNotification :notification="notification">
3
+ Test Notification
4
+ </SimpleNotification>
5
+ </template>
6
+
7
+ <script setup>
8
+
9
+ import SimpleNotification from "./SimpleNotification.vue"
10
+ import { toRefs } from "@vueuse/core"
11
+
12
+ const props = defineProps({
13
+ notification: {
14
+ type: Object,
15
+ required: true
16
+ }
17
+ })
18
+
19
+ const { notification } = toRefs(props)
20
+
21
+ </script>
22
+
23
+ <style scoped>
24
+
25
+ </style>
@@ -0,0 +1,25 @@
1
+ <template>
2
+ <SimpleNotification :notification="notification">
3
+ <div class="text-orange-600 text-2xl">
4
+ Unknown notification!
5
+ </div>
6
+ <pre class="w-full overflow-hidden">{{ notification }}</pre>
7
+ </SimpleNotification>
8
+ </template>
9
+
10
+ <script setup>
11
+
12
+ import SimpleNotification from "./SimpleNotification.vue"
13
+
14
+ const { notification } = defineProps({
15
+ notification: {
16
+ type: Object,
17
+ required: true
18
+ }
19
+ })
20
+
21
+ </script>
22
+
23
+ <style scoped>
24
+
25
+ </style>
@@ -0,0 +1,11 @@
1
+ import UnknownNotification from "./UnknownNotification.vue"
2
+ import TestNotification from "./TestNotification.vue"
3
+
4
+ export const notificationTypes = {
5
+ unknown: {
6
+ component: UnknownNotification
7
+ },
8
+ example_TestNotification: {
9
+ component: TestNotification
10
+ }
11
+ }
@@ -0,0 +1,37 @@
1
+
2
+
3
+ export function notificationsSettingsRoutes(config = {}) {
4
+ const { prefix = '/', route = (r) => r } = config
5
+
6
+ return [
7
+
8
+ route({ name: 'user:notificationsSettings', path: prefix + 'notifications-settings', props: true,
9
+ component: () => import("./NotificationsSettings.vue"), meta: { signedIn: true } }),
10
+
11
+ ]
12
+ }
13
+
14
+
15
+ export function notificationsRoutes(config = {}) {
16
+ const { prefix = '/', route = (r) => r } = config
17
+
18
+ return [
19
+
20
+ route({ name: 'user:notificationsList', path: prefix + 'notifications', props: true,
21
+ component: () => import("./NotificationListPage.vue"), meta: { signedIn: true } }),
22
+
23
+ ]
24
+ }
25
+
26
+ export function routes(config = {}) {
27
+ const { prefix = '/', route = (r) => r } = config
28
+
29
+ return [
30
+
31
+ ...notificationsRoutes(config),
32
+ ...notificationsRoutes(config)
33
+
34
+ ]
35
+ }
36
+
37
+ export default routes
@@ -0,0 +1,106 @@
1
+ <template>
2
+ <div class="w-full lg:w-6 md:w-9">
3
+
4
+ <div class="surface-card p-4 shadow-2 border-round">
5
+ <div class="text-center mb-5">
6
+ <div class="text-900 text-3xl font-medium mb-3">
7
+ {{ passwordExists ? 'Change password' : 'Set password' }}
8
+ </div>
9
+ </div>
10
+
11
+ <command-form service="passwordAuthentication"
12
+ :action="passwordExists ? 'changePassword' : 'setPassword'"
13
+ v-slot="{ data }" ref="form" @done="handleDone">
14
+
15
+ <template v-if="isMounted">
16
+
17
+ <div class="p-field mb-3" v-if="passwordExists">
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" toggleMask
20
+ :class="{ 'p-invalid': data.currentPasswordHashError }"
21
+ v-model="data.currentPasswordHash" />
22
+ <small id="currentPassword-help" class="p-error">{{ data.currentPasswordHashError }}</small>
23
+ </div>
24
+
25
+ <div class="p-field mb-3">
26
+ <label for="newPassword" class="block text-900 font-medium mb-2">New password</label>
27
+ <Password id="newPassword" class="w-full" inputClass="w-full" toggleMask
28
+ :class="{ 'p-invalid': data.passwordHashError }"
29
+ v-model="data.passwordHash">
30
+ <template #footer>
31
+ <Divider />
32
+ <p class="p-mt-2">Suggestions</p>
33
+ <ul class="p-pl-2 p-ml-2 p-mt-0" style="line-height: 1.5">
34
+ <li>At least one lowercase</li>
35
+ <li>At least one uppercase</li>
36
+ <li>At least one numeric</li>
37
+ <li>Minimum 8 characters</li>
38
+ </ul>
39
+ </template>
40
+ </Password>
41
+ <small id="newPassword-help" class="p-error">{{ data.passwordHashError }}</small>
42
+ </div>
43
+
44
+ <div class="p-field mb-3">
45
+ <label for="reenterPassword" class="block text-900 font-medium mb-2">Re-enter password</label>
46
+ <Password id="reenterPassword" class="w-full" inputClass="w-full"
47
+ v-model="secondPassword"
48
+ :feedback="false" toggleMask />
49
+ </div>
50
+
51
+ </template>
52
+
53
+ <Button :label="passwordExists ? 'Change password' : 'Set password'"
54
+ type="submit"
55
+ icon="pi pi-key" class="w-full"></Button>
56
+
57
+ </command-form>
58
+ </div>
59
+
60
+ </div>
61
+ </template>
62
+
63
+ <script setup>
64
+
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"
71
+
72
+ import { live, path } from '@live-change/vue3-ssr'
73
+ import { computed, ref, onMounted } from 'vue'
74
+
75
+ import { useRouter } from 'vue-router'
76
+ const router = useRouter()
77
+
78
+ const isMounted = ref(false)
79
+ onMounted(() => isMounted.value = true)
80
+
81
+ const secondPassword = ref('')
82
+ const form = ref()
83
+
84
+ onMounted(() => {
85
+ form.value.addValidator('passwordHash', () => {
86
+ const value = form.value.getFieldValue('passwordHash')
87
+ console.log("PASSWORDS MATCH?", secondPassword.value, value)
88
+ if(value != secondPassword.value) return "passwordsNotMatch"
89
+ })
90
+ })
91
+
92
+
93
+ const passwordExists = await live(path().passwordAuthentication.myUserPasswordAuthenticationExists())
94
+
95
+ function handleDone({ parameters, result }) {
96
+ console.log("FORM DONE", parameters, result)
97
+ router.push({
98
+ name: 'user:changePasswordFinished',
99
+ })
100
+ }
101
+
102
+ </script>
103
+
104
+ <style>
105
+
106
+ </style>
@@ -0,0 +1,16 @@
1
+ <template>
2
+ <div class="w-full lg:w-6 md:w-9" v-shared-element:form="{ duration: '300ms', includeChildren: true }">
3
+ <div class="surface-card border-round shadow-2 p-4">
4
+ <div class="text-900 font-medium mb-3 text-xl mb-4">Password changed</div>
5
+ <p class="mt-0 p-0 line-height-3">You have successfully set your password.</p>
6
+ </div>
7
+ </div>
8
+ </template>
9
+
10
+ <script setup>
11
+
12
+ </script>
13
+
14
+ <style>
15
+
16
+ </style>
@@ -0,0 +1,56 @@
1
+ <template>
2
+ <div class="w-full lg:w-6 md:w-9" v-shared-element:form="{ duration: '300ms', includeChildren: true }">
3
+ <div class="surface-card p-4 shadow-2 border-round">
4
+ <div class="text-center mb-5">
5
+ <div class="text-900 text-3xl font-medium mb-3">Reset password</div>
6
+ </div>
7
+
8
+ <command-form service="passwordAuthentication" action="resetPasswordEmail" v-slot="{ data }"
9
+ @done="handleDone" keepOnDone v-if="isMounted">
10
+
11
+ <div class="p-field mb-3">
12
+ <label for="email" class="block text-900 font-medium mb-2">
13
+ Email address
14
+ </label>
15
+ <InputText id="email" type="text" class="w-full"
16
+ v-model="data.email" :class="{ 'p-invalid': data.emailError }"
17
+ aria-describedby="email-help" />
18
+ <small id="email-help" class="p-error">{{ data.emailError }}</small>
19
+ </div>
20
+
21
+ <Button type="submit" label="Reset password" icon="pi pi-key" class="w-full"></Button>
22
+
23
+ </command-form>
24
+ </div>
25
+ </div>
26
+ </template>
27
+
28
+ <script setup>
29
+ import InputText from "primevue/inputtext"
30
+ import Button from "primevue/button"
31
+
32
+ import { onMounted, ref } from "vue"
33
+ const isMounted = ref(false)
34
+ onMounted(() => isMounted.value = true)
35
+
36
+ import { useRouter } from 'vue-router'
37
+ const router = useRouter()
38
+
39
+ function handleDone({ parameters, result }) {
40
+ console.log("DONE RESULT", result)
41
+ const { authentication } = result
42
+ router.push({
43
+ name: 'user:sent',
44
+ params: {
45
+ authentication
46
+ }
47
+ })
48
+ }
49
+
50
+ await new Promise(r=>setTimeout(r, 800))
51
+
52
+ </script>
53
+
54
+ <style>
55
+
56
+ </style>
@@ -0,0 +1,16 @@
1
+ <template>
2
+ <div class="w-full lg:w-6 md:w-9" v-shared-element:form="{ duration: '300ms', includeChildren: true }">
3
+ <div class="surface-card border-round shadow-2 p-4">
4
+ <div class="text-900 font-medium mb-3 text-xl mb-4">Password changed</div>
5
+ <p class="mt-0 p-0 line-height-3">You have successfully changed your password.</p>
6
+ </div>
7
+ </div>
8
+ </template>
9
+
10
+ <script setup>
11
+
12
+ </script>
13
+
14
+ <style>
15
+
16
+ </style>