@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.
- package/LICENSE +21 -0
- package/e2e/codecept.conf.js +60 -0
- package/e2e/connectEmailCode.test.js +61 -0
- package/e2e/connectEmailLink.test.js +60 -0
- package/e2e/delete.test.js +44 -0
- package/e2e/disconnectEmail.test.js +42 -0
- package/e2e/resetPasswordWithEmailCode.test.js +62 -0
- package/e2e/resetPasswordWithEmailLink.test.js +62 -0
- package/e2e/setPassword.test.js +70 -0
- package/e2e/signInEmailCode.test.js +52 -0
- package/e2e/signInEmailLink.test.js +52 -0
- package/e2e/signInEmailPassword.test.js +47 -0
- package/e2e/signOut.test.js +41 -0
- package/e2e/signUpEmailCode.test.js +41 -0
- package/e2e/signUpEmailLink.test.js +41 -0
- package/e2e/steps.d.ts +12 -0
- package/e2e/steps_file.js +89 -0
- package/front/index.html +11 -0
- package/front/public/favicon.ico +0 -0
- package/front/public/images/empty-photo.svg +38 -0
- package/front/public/images/empty-user-photo.svg +33 -0
- package/front/public/images/logo.svg +34 -0
- package/front/public/images/logo128.png +0 -0
- package/front/src/App.vue +31 -0
- package/front/src/Index.vue +14 -0
- package/front/src/NavBar.vue +103 -0
- package/front/src/SettingsTabs.vue +48 -0
- package/front/src/connected/Connect.vue +58 -0
- package/front/src/connected/ConnectFinished.vue +16 -0
- package/front/src/connected/Connected.vue +84 -0
- package/front/src/connected/routes.js +16 -0
- package/front/src/delete/Delete.vue +53 -0
- package/front/src/delete/DeleteFeedbackSent.vue +16 -0
- package/front/src/delete/DeleteFinished.vue +32 -0
- package/front/src/delete/routes.js +16 -0
- package/front/src/entry-client.js +6 -0
- package/front/src/entry-server.js +6 -0
- package/front/src/identification/IdentificationSettings.vue +116 -0
- package/front/src/identification/ObjectIdentification.vue +36 -0
- package/front/src/identification/UserIdentification.vue +101 -0
- package/front/src/identification/routes.js +12 -0
- package/front/src/message-auth/ConnectEmail.vue +105 -0
- package/front/src/message-auth/MessageLink.vue +95 -0
- package/front/src/message-auth/MessageSent.vue +103 -0
- package/front/src/message-auth/ResetPasswordEmail.vue +105 -0
- package/front/src/message-auth/SignInEmail.vue +105 -0
- package/front/src/message-auth/SignUpEmail.vue +105 -0
- package/front/src/message-auth/routes.js +25 -0
- package/front/src/notifications/NotificationButtons.vue +70 -0
- package/front/src/notifications/NotificationListPage.vue +22 -0
- package/front/src/notifications/NotificationsIcon.vue +75 -0
- package/front/src/notifications/NotificationsList.vue +144 -0
- package/front/src/notifications/NotificationsSettings.vue +117 -0
- package/front/src/notifications/SimpleNotification.vue +34 -0
- package/front/src/notifications/TestNotification.vue +25 -0
- package/front/src/notifications/UnknownNotification.vue +25 -0
- package/front/src/notifications/notificationTypes.js +11 -0
- package/front/src/notifications/routes.js +37 -0
- package/front/src/password/ChangePassword.vue +106 -0
- package/front/src/password/ChangePasswordFinished.vue +16 -0
- package/front/src/password/ResetPassword.vue +56 -0
- package/front/src/password/ResetPasswordFinished.vue +16 -0
- package/front/src/password/ResetPasswordForm.vue +118 -0
- package/front/src/password/routes.js +41 -0
- package/front/src/router.js +90 -0
- package/front/src/settings/Settings.vue +33 -0
- package/front/src/settings/SettingsIndex.vue +22 -0
- package/front/src/settings/SettingsMenu.vue +81 -0
- package/front/src/settings/SettingsMenuItem.vue +35 -0
- package/front/src/sign/SignIn.vue +93 -0
- package/front/src/sign/SignInFinished.vue +27 -0
- package/front/src/sign/SignOut.vue +37 -0
- package/front/src/sign/SignOutFinished.vue +16 -0
- package/front/src/sign/SignUp.vue +51 -0
- package/front/src/sign/SignUpFinished.vue +16 -0
- package/front/src/sign/routes.js +24 -0
- package/front/vite.config.js +11 -0
- package/index.js +11 -0
- package/package.json +87 -0
- package/server/init.js +53 -0
- package/server/security.config.js +53 -0
- package/server/services.config.js +74 -0
|
@@ -0,0 +1,118 @@
|
|
|
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" v-if="isUnknown">
|
|
4
|
+
<div class="text-900 font-medium mb-3 text-xl">Unknown link</div>
|
|
5
|
+
<p class="mt-0 mb-2 p-0 line-height-3">We can't find your secret link. Check if you copied the address correctly.</p>
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<div class="surface-card border-round shadow-2 p-4" v-if="isUsed">
|
|
9
|
+
<div class="text-900 font-medium mb-3 text-xl">Link used</div>
|
|
10
|
+
<p class="mt-0 mb-2 p-0 line-height-3">This link was already used.</p>
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<div class="surface-card border-round shadow-2 p-4" v-if="isExpired && !isUsed">
|
|
14
|
+
<div class="text-900 font-medium mb-3 text-xl">Link expired</div>
|
|
15
|
+
<p class="mt-0 mb-4 p-0 line-height-3">Your password reset authentication already expired.</p>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<div class="surface-card p-4 shadow-2 border-round" v-if="isReady">
|
|
19
|
+
<div class="text-center mb-5">
|
|
20
|
+
<div class="text-900 text-3xl font-medium mb-3">Reset password</div>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<command-form service="passwordAuthentication" action="finishResetPassword" v-slot="{ data }"
|
|
24
|
+
:parameters="{ key: resetKey }" ref="form"
|
|
25
|
+
@done="handleDone" keepOnDone>
|
|
26
|
+
|
|
27
|
+
<template v-if="isMounted">
|
|
28
|
+
<div class="p-field mb-3">
|
|
29
|
+
<label for="newPassword" class="block text-900 font-medium mb-2">New password</label>
|
|
30
|
+
<Password id="newPassword" class="w-full" inputClass="w-full" toggleMask
|
|
31
|
+
:class="{ 'p-invalid': data.passwordHashError }"
|
|
32
|
+
v-model="data.passwordHash">
|
|
33
|
+
<template #footer>
|
|
34
|
+
<Divider />
|
|
35
|
+
<p class="p-mt-2">Suggestions</p>
|
|
36
|
+
<ul class="p-pl-2 p-ml-2 p-mt-0" style="line-height: 1.5">
|
|
37
|
+
<li>At least one lowercase</li>
|
|
38
|
+
<li>At least one uppercase</li>
|
|
39
|
+
<li>At least one numeric</li>
|
|
40
|
+
<li>Minimum 8 characters</li>
|
|
41
|
+
</ul>
|
|
42
|
+
</template>
|
|
43
|
+
</Password>
|
|
44
|
+
<small id="newPassword-help" class="p-error">{{ data.passwordHashError }}</small>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<div class="p-field mb-3">
|
|
48
|
+
<label for="reenterPassword" class="block text-900 font-medium mb-2">Re-enter password</label>
|
|
49
|
+
<Password id="reenterPassword" class="w-full" inputClass="w-full"
|
|
50
|
+
v-model="secondPassword"
|
|
51
|
+
:feedback="false" toggleMask />
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
</template>
|
|
55
|
+
|
|
56
|
+
<Button type="submit" label="Reset password" icon="pi pi-key" class="w-full"></Button>
|
|
57
|
+
|
|
58
|
+
</command-form>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</template>
|
|
62
|
+
|
|
63
|
+
<script setup>
|
|
64
|
+
import InputText from "primevue/inputtext"
|
|
65
|
+
import Checkbox from "primevue/checkbox"
|
|
66
|
+
import Button from "primevue/button"
|
|
67
|
+
import Divider from "primevue/divider"
|
|
68
|
+
import Password from "primevue/password"
|
|
69
|
+
|
|
70
|
+
const { resetKey } = defineProps({
|
|
71
|
+
resetKey: {
|
|
72
|
+
type: String,
|
|
73
|
+
required: true
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
import { useNow } from '@vueuse/core'
|
|
78
|
+
const now = useNow({ interval: 1000 })
|
|
79
|
+
|
|
80
|
+
import { ref, onMounted, computed } from 'vue'
|
|
81
|
+
const secondPassword = ref('')
|
|
82
|
+
const form = ref()
|
|
83
|
+
onMounted(() => {
|
|
84
|
+
form.value.addValidator('passwordHash', () => {
|
|
85
|
+
const value = form.value.getFieldValue('passwordHash')
|
|
86
|
+
console.log("PASSWORDS MATCH?", secondPassword.value, value)
|
|
87
|
+
if(value != secondPassword.value) return "passwordsNotMatch"
|
|
88
|
+
})
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
import { useRouter } from 'vue-router'
|
|
92
|
+
const router = useRouter()
|
|
93
|
+
|
|
94
|
+
const isMounted = ref(false)
|
|
95
|
+
onMounted(() => isMounted.value = true)
|
|
96
|
+
|
|
97
|
+
import { live, path } from '@live-change/vue3-ssr'
|
|
98
|
+
const [ authentication ] = await Promise.all([
|
|
99
|
+
live( path().passwordAuthentication.resetPasswordAuthentication({ key: resetKey }) )
|
|
100
|
+
])
|
|
101
|
+
|
|
102
|
+
const isUnknown = computed(() => authentication.value === null)
|
|
103
|
+
const isExpired = computed(() =>
|
|
104
|
+
authentication.value ? (now.value.toISOString() > authentication.value.expire) : false )
|
|
105
|
+
const isUsed = computed(() => authentication.value && authentication.value.state == 'used')
|
|
106
|
+
const isReady = computed(() => !(isUnknown.value || isExpired.value || isUsed.value))
|
|
107
|
+
|
|
108
|
+
function handleDone({ parameters, result }) {
|
|
109
|
+
console.log("DONE RESULT", result)
|
|
110
|
+
router.push({
|
|
111
|
+
name: 'user:resetPasswordFinished'
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
</script>
|
|
115
|
+
|
|
116
|
+
<style>
|
|
117
|
+
|
|
118
|
+
</style>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export function passwordResetRoutes(config = {}) {
|
|
2
|
+
const { prefix = '/', route = (r) => r } = config
|
|
3
|
+
|
|
4
|
+
return [
|
|
5
|
+
|
|
6
|
+
route({ name: 'user:resetPassword', path: prefix + 'reset-password',
|
|
7
|
+
component: () => import("./ResetPassword.vue") }),
|
|
8
|
+
route({ name: 'user:resetPasswordForm', path: prefix + 'set-new-password/:resetKey',
|
|
9
|
+
component: () => import("./ResetPasswordForm.vue"), props: true }),
|
|
10
|
+
route({ name: 'user:resetPasswordFinished', path: prefix + 'reset-password-finished',
|
|
11
|
+
component: () => import("./ResetPasswordFinished.vue") }),
|
|
12
|
+
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
export function passwordChangeRoutes(config = {}) {
|
|
18
|
+
const { prefix = '/', route = (r) => r } = config
|
|
19
|
+
|
|
20
|
+
return [
|
|
21
|
+
|
|
22
|
+
route({ name: 'user:changePassword', path: prefix + 'change-password',
|
|
23
|
+
component: () => import("./ChangePassword.vue") }),
|
|
24
|
+
route({ name: 'user:changePasswordFinished', path: prefix + 'change-password-finished',
|
|
25
|
+
component: () => import("./ChangePasswordFinished.vue") })
|
|
26
|
+
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function routes(config = {}) {
|
|
31
|
+
const { prefix = '/', route = (r) => r } = config
|
|
32
|
+
|
|
33
|
+
return [
|
|
34
|
+
|
|
35
|
+
...passwordResetRoutes(config),
|
|
36
|
+
...passwordChangeRoutes(config)
|
|
37
|
+
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default routes
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createMemoryHistory,
|
|
3
|
+
createRouter as _createRouter,
|
|
4
|
+
createWebHistory
|
|
5
|
+
} from 'vue-router'
|
|
6
|
+
|
|
7
|
+
import messageAuthRoutes from "./message-auth/routes.js"
|
|
8
|
+
import signRoutes from "./sign/routes.js"
|
|
9
|
+
import connectedRoutes from "./connected/routes.js"
|
|
10
|
+
import identificationRoutes from "./identification/routes.js"
|
|
11
|
+
import deleteRoutes from "./delete/routes.js"
|
|
12
|
+
import { passwordResetRoutes, passwordChangeRoutes } from "./password/routes.js"
|
|
13
|
+
import { notificationsSettingsRoutes, notificationsRoutes } from "./notifications/routes.js"
|
|
14
|
+
|
|
15
|
+
import { dbAdminRoutes } from "@live-change/db-admin"
|
|
16
|
+
|
|
17
|
+
export function userRoutes(config = {}) {
|
|
18
|
+
const { prefix = '/', route = (r) => r } = config
|
|
19
|
+
return [
|
|
20
|
+
|
|
21
|
+
...messageAuthRoutes(config),
|
|
22
|
+
...signRoutes(config),
|
|
23
|
+
...passwordResetRoutes(config),
|
|
24
|
+
...notificationsRoutes(config),
|
|
25
|
+
|
|
26
|
+
route({
|
|
27
|
+
path: prefix + 'settings', meta: { pageType: 'wide' },
|
|
28
|
+
component: () => import("./settings/Settings.vue"),
|
|
29
|
+
children: [
|
|
30
|
+
{
|
|
31
|
+
name: 'user:settings', path: '', meta: { viewType: 'wide', signedIn: true },
|
|
32
|
+
component: () => import("./settings/SettingsIndex.vue")
|
|
33
|
+
},
|
|
34
|
+
...deleteRoutes({ ...config, prefix: '' }),
|
|
35
|
+
...passwordChangeRoutes({ ...config, prefix: '' }),
|
|
36
|
+
...connectedRoutes({ ...config, prefix: '' }),
|
|
37
|
+
...identificationRoutes({ ...config, prefix: '' }),
|
|
38
|
+
...notificationsSettingsRoutes({ ...config, prefix: '' })
|
|
39
|
+
]
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function sitemap(route, api) {
|
|
46
|
+
route({ name: 'SignIn' })
|
|
47
|
+
route({ name: 'SignUp' })
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
import { client as useClient } from '@live-change/vue3-ssr'
|
|
51
|
+
|
|
52
|
+
export function installUserRedirects(router, app, config) {
|
|
53
|
+
const client = useClient(app._context)
|
|
54
|
+
router.beforeEach(async (to, from) => {
|
|
55
|
+
if(to?.matched.find(m => m?.meta.signedIn)) {
|
|
56
|
+
if(!client.value.user) {
|
|
57
|
+
console.log("REDIRECT TO LOGIN BECAUSE PAGE REQUIRES LOGIN!")
|
|
58
|
+
router.redirectAfterSignIn = to.fullPath
|
|
59
|
+
return { name: 'user:signIn' }
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if(to?.matched.find(m => m?.meta.signedOut)) {
|
|
63
|
+
if(client.value.user) {
|
|
64
|
+
console.log("REDIRECT TO USER INDEX BECAUSE PAGE REQUIRES LOGOUT!")
|
|
65
|
+
return { name: 'user:settings' }
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if(to && to.name == 'user:signIn' && from?.matched.find(m => m?.meta.saveForSignIn)) {
|
|
69
|
+
console.log("SAVE FOR LOGIN", from.fullPath)
|
|
70
|
+
localStorage.redirectAfterLogin = from.fullPath
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function createRouter(app, config) {
|
|
76
|
+
//console.log("APP CTX", app._context)
|
|
77
|
+
const router = _createRouter({
|
|
78
|
+
// use appropriate history implementation for server/client
|
|
79
|
+
// import.meta.env.SSR is injected by Vite.
|
|
80
|
+
history: import.meta.env.SSR ? createMemoryHistory() : createWebHistory(),
|
|
81
|
+
routes: [
|
|
82
|
+
{ path: '/', component: () => import('./Index.vue') },
|
|
83
|
+
...userRoutes(config),
|
|
84
|
+
...dbAdminRoutes({ prefix: '/_db', route: r => ({ ...r, meta: { ...r.meta, raw: true }}) })
|
|
85
|
+
]
|
|
86
|
+
})
|
|
87
|
+
installUserRedirects(router, app, config)
|
|
88
|
+
return router
|
|
89
|
+
}
|
|
90
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flex relative lg:static surface-ground" style="height: calc(100vh - 88px)">
|
|
3
|
+
<div id="settings-menu" class="surface-section hidden flex-shrink-0 absolute left-0 h-full
|
|
4
|
+
top-0 z-1 border-right-1 surface-border select-none md:static md:block" style="width:280px">
|
|
5
|
+
<SettingsMenu />
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<div class="flex flex-column relative flex-auto">
|
|
9
|
+
<div v-if="viewType == 'simple'" class="p-5 flex flex-column flex-auto align-items-center">
|
|
10
|
+
<router-view></router-view>
|
|
11
|
+
</div>
|
|
12
|
+
<template v-if="viewType == 'wide'">
|
|
13
|
+
<router-view></router-view>
|
|
14
|
+
</template>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<script setup>
|
|
20
|
+
import SettingsMenu from "./SettingsMenu.vue"
|
|
21
|
+
|
|
22
|
+
import { computed } from 'vue'
|
|
23
|
+
|
|
24
|
+
import { useRoute } from 'vue-router'
|
|
25
|
+
const route = useRoute()
|
|
26
|
+
|
|
27
|
+
const viewType = computed(() => route.meta.viewType ?? 'simple' )
|
|
28
|
+
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<style scoped>
|
|
32
|
+
|
|
33
|
+
</style>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
|
|
3
|
+
<SettingsMenu class="h-full block md:hidden surface-section" />
|
|
4
|
+
|
|
5
|
+
<div class="flex-column flex-auto align-items-center p-5 hidden md:flex">
|
|
6
|
+
<Connected class="mb-5" />
|
|
7
|
+
<ChangePassword />
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script setup>
|
|
13
|
+
import SettingsMenu from "./SettingsMenu.vue"
|
|
14
|
+
|
|
15
|
+
import ChangePassword from "../password/ChangePassword.vue"
|
|
16
|
+
import Connected from "../connected/Connected.vue"
|
|
17
|
+
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<style scoped>
|
|
21
|
+
|
|
22
|
+
</style>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flex flex-column h-full">
|
|
3
|
+
<div class="overflow-y-auto">
|
|
4
|
+
<ul class="list-none p-2 m-0">
|
|
5
|
+
<li>
|
|
6
|
+
<div class="p-3 text-500 font-medium">SETTINGS</div>
|
|
7
|
+
</li>
|
|
8
|
+
|
|
9
|
+
<SettingsMenuItem name="user:identification" icon="id-card" label="Identification" class="hidden md:block" />
|
|
10
|
+
|
|
11
|
+
<SettingsMenuItem name="user:settings" icon="id-card" label="General Settings" class="hidden md:block" />
|
|
12
|
+
|
|
13
|
+
<SettingsMenuItem name="user:notificationsSettings" icon="exclamation-circle" label="Notifications" />
|
|
14
|
+
|
|
15
|
+
</ul>
|
|
16
|
+
|
|
17
|
+
<ul class="list-none p-2 m-0 border-top-1 surface-border">
|
|
18
|
+
<li>
|
|
19
|
+
<div class="p-3 text-500 font-medium">AUTHORIZATION</div>
|
|
20
|
+
</li>
|
|
21
|
+
|
|
22
|
+
<SettingsMenuItem name="user:connected" icon="users" label="Connected Accounts" />
|
|
23
|
+
|
|
24
|
+
<SettingsMenuItem name="user:changePassword" icon="key" label="ChangePassword" />
|
|
25
|
+
|
|
26
|
+
</ul>
|
|
27
|
+
|
|
28
|
+
<ul class="list-none p-2 m-0 border-top-1 surface-border">
|
|
29
|
+
|
|
30
|
+
<SettingsMenuItem name="user:delete" icon="trash" label="Delete Account" />
|
|
31
|
+
|
|
32
|
+
</ul>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<!-- <div class="p-2 mt-auto border-top-1 surface-border">
|
|
36
|
+
<ul class="list-none p-2 m-0 hidden origin-bottom animation-duration-150">
|
|
37
|
+
<li>
|
|
38
|
+
<a v-ripple class="flex align-items-center cursor-pointer p-3 text-700 hover:surface-100
|
|
39
|
+
border-round transition-colors transition-duration-150 p-ripple">
|
|
40
|
+
<i class="pi pi-user mr-2"></i>
|
|
41
|
+
<span class="font-medium">Profile</span>
|
|
42
|
+
</a>
|
|
43
|
+
</li>
|
|
44
|
+
<li>
|
|
45
|
+
<a v-ripple class="flex align-items-center cursor-pointer p-3 text-700 hover:surface-100
|
|
46
|
+
border-round transition-colors transition-duration-150 p-ripple">
|
|
47
|
+
<i class="pi pi-cog mr-2"></i>
|
|
48
|
+
<span class="font-medium">Settings</span>
|
|
49
|
+
</a>
|
|
50
|
+
</li>
|
|
51
|
+
<li>
|
|
52
|
+
<a v-ripple class="flex align-items-center cursor-pointer p-3 text-700 hover:surface-100
|
|
53
|
+
border-round transition-colors transition-duration-150 p-ripple">
|
|
54
|
+
<i class="pi pi-sign-out mr-2"></i>
|
|
55
|
+
<span class="font-medium">Sign Out</span>
|
|
56
|
+
</a>
|
|
57
|
+
</li>
|
|
58
|
+
</ul>
|
|
59
|
+
<a v-ripple class="p-3 flex align-items-center cursor-pointer text-700 hover:surface-100
|
|
60
|
+
border-round transition-colors transition-duration-150 p-ripple"
|
|
61
|
+
v-styleclass="{ selector: '@prev', enterClass: 'hidden', enterActiveClass: 'scalein',
|
|
62
|
+
leaveToClass: 'hidden', leaveActiveClass: 'fadeout' }">
|
|
63
|
+
<img src="/images/empty-user-photo.svg" class="mr-2 border-circle" style="width: 28px; height: 28px"/>
|
|
64
|
+
<span class="font-medium">Amy Elsner</span>
|
|
65
|
+
<i class="pi pi-chevron-up ml-auto"></i>
|
|
66
|
+
</a>
|
|
67
|
+
</div>-->
|
|
68
|
+
</div>
|
|
69
|
+
</template>
|
|
70
|
+
|
|
71
|
+
<script setup>
|
|
72
|
+
import SettingsMenuItem from "./SettingsMenuItem.vue"
|
|
73
|
+
|
|
74
|
+
import { useRoute } from 'vue-router'
|
|
75
|
+
const route = useRoute()
|
|
76
|
+
|
|
77
|
+
</script>
|
|
78
|
+
|
|
79
|
+
<style scoped>
|
|
80
|
+
|
|
81
|
+
</style>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<li class="block">
|
|
3
|
+
<router-link :to="{ name }"
|
|
4
|
+
:class="route.name == name ? 'text-blue-500' : 'text-700'"
|
|
5
|
+
v-ripple
|
|
6
|
+
class="flex align-items-center cursor-pointer p-3 hover:surface-100
|
|
7
|
+
border-round transition-colors transition-duration-150 p-ripple no-underline">
|
|
8
|
+
<i class="pi mr-2" :class="'pi-'+icon"></i>
|
|
9
|
+
<span class="font-medium">{{ label }}</span>
|
|
10
|
+
</router-link>
|
|
11
|
+
</li>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script setup>
|
|
15
|
+
|
|
16
|
+
import { useRoute } from 'vue-router'
|
|
17
|
+
const route = useRoute()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
const { name, label, icon } = defineProps({
|
|
21
|
+
name: {
|
|
22
|
+
type: String,
|
|
23
|
+
required: true
|
|
24
|
+
},
|
|
25
|
+
label: {
|
|
26
|
+
type: String,
|
|
27
|
+
required: true
|
|
28
|
+
},
|
|
29
|
+
icon: {
|
|
30
|
+
type: String,
|
|
31
|
+
required: true
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
</script>
|
|
@@ -0,0 +1,93 @@
|
|
|
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">Welcome Back</div>
|
|
6
|
+
<span class="text-600 font-medium line-height-3">Don't have an account?</span>
|
|
7
|
+
<router-link :to="{ name: 'user:signUp' }"
|
|
8
|
+
class="font-medium no-underline ml-2 text-blue-500 cursor-pointer">
|
|
9
|
+
Create today!</router-link>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<command-form service="passwordAuthentication" action="signInEmail" v-slot="{ data }"
|
|
13
|
+
@done="handleDone" keepOnDone v-if="isMounted">
|
|
14
|
+
|
|
15
|
+
<div class="p-field mb-3">
|
|
16
|
+
<label for="email" class="block text-900 font-medium mb-2">
|
|
17
|
+
Email address
|
|
18
|
+
</label>
|
|
19
|
+
<InputText id="email" type="text" class="w-full"
|
|
20
|
+
aria-describedby="email-help" :class="{ 'p-invalid': data.emailError }"
|
|
21
|
+
v-model="data.email" />
|
|
22
|
+
<small id="email-help" class="p-error">{{ data.emailError }}</small>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<div class="p-field mb-3">
|
|
26
|
+
<label for="password" class="block text-900 font-medium mb-2">Password (optional)</label>
|
|
27
|
+
<Password id="password" class="w-full" inputClass="w-full" toggleMask
|
|
28
|
+
aria-describedby="password-help" :class="{ 'p-invalid': data.passwordHashError }"
|
|
29
|
+
v-model="data.passwordHash" />
|
|
30
|
+
<small id="password-help" class="p-error">{{ data.passwordHashError }}</small>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div class="flex align-items-center justify-content-between mb-6">
|
|
34
|
+
<div class="flex align-items-center">
|
|
35
|
+
<Checkbox id="rememberme" :binary="true" class="mr-2"></Checkbox>
|
|
36
|
+
<label for="rememberme">Remember me</label>
|
|
37
|
+
</div>
|
|
38
|
+
<router-link :to="{ name: 'user:resetPassword' }"
|
|
39
|
+
class="font-medium no-underline ml-2 text-blue-500 text-right cursor-pointer">
|
|
40
|
+
Forgot password?
|
|
41
|
+
</router-link>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<Button label="Sign In" icon="pi pi-user" class="w-full" type="submit"></Button>
|
|
45
|
+
|
|
46
|
+
<Divider align="center" class="my-4">
|
|
47
|
+
<span class="text-600 font-normal text-sm">OR</span>
|
|
48
|
+
</Divider>
|
|
49
|
+
|
|
50
|
+
<Button label="Sign In with GitHub" icon="pi pi-github" class="w-full p-button-secondary mb-2"></Button>
|
|
51
|
+
<Button label="Sign In with Google" icon="pi pi-google" class="w-full p-button-secondary mb-1"></Button>
|
|
52
|
+
|
|
53
|
+
</command-form>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</template>
|
|
57
|
+
|
|
58
|
+
<script setup>
|
|
59
|
+
import InputText from "primevue/inputtext"
|
|
60
|
+
import Checkbox from "primevue/checkbox"
|
|
61
|
+
import Button from "primevue/button"
|
|
62
|
+
import Divider from "primevue/divider"
|
|
63
|
+
import Password from "primevue/password"
|
|
64
|
+
|
|
65
|
+
import { onMounted, ref } from 'vue'
|
|
66
|
+
const isMounted = ref(false)
|
|
67
|
+
onMounted(() => isMounted.value = true)
|
|
68
|
+
|
|
69
|
+
import { useRouter } from 'vue-router'
|
|
70
|
+
const router = useRouter()
|
|
71
|
+
|
|
72
|
+
function handleDone({ parameters, result }) {
|
|
73
|
+
console.log("DONE RESULT", result)
|
|
74
|
+
if(result.type == 'sent') {
|
|
75
|
+
const { authentication } = result
|
|
76
|
+
router.push({
|
|
77
|
+
name: 'user:sent',
|
|
78
|
+
params: {
|
|
79
|
+
authentication
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
} else {
|
|
83
|
+
router.push({
|
|
84
|
+
name: 'user:signInFinished',
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
</script>
|
|
90
|
+
|
|
91
|
+
<style>
|
|
92
|
+
|
|
93
|
+
</style>
|
|
@@ -0,0 +1,27 @@
|
|
|
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">Signed In</div>
|
|
5
|
+
<p class="mt-0 p-0 line-height-3">Congratulations! You have successfully logged in to your account.</p>
|
|
6
|
+
<div v-if="afterSignIn" class="flex flex-row align-items-center">
|
|
7
|
+
<Button label="Next" />
|
|
8
|
+
</div>
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script setup>
|
|
14
|
+
import Button from 'primevue/button'
|
|
15
|
+
|
|
16
|
+
import { onMounted, ref } from 'vue'
|
|
17
|
+
const isMounted = ref(false)
|
|
18
|
+
onMounted(() => isMounted.value = true)
|
|
19
|
+
|
|
20
|
+
import { computed } from 'vue'
|
|
21
|
+
|
|
22
|
+
const afterSignIn = computed( () => isMounted.value && localStorage.redirectAfterSignIn )
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<style>
|
|
26
|
+
|
|
27
|
+
</style>
|
|
@@ -0,0 +1,37 @@
|
|
|
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">Sign Out</div>
|
|
5
|
+
<p class="mt-0 p-0 line-height-3">Signing out</p>
|
|
6
|
+
<ProgressSpinner v-if="isMounted" class="m-3" />
|
|
7
|
+
</div>
|
|
8
|
+
</div>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script setup>
|
|
12
|
+
import ProgressSpinner from "primevue/progressspinner"
|
|
13
|
+
|
|
14
|
+
import { onMounted, ref } from 'vue'
|
|
15
|
+
const isMounted = ref(false)
|
|
16
|
+
onMounted(() => isMounted.value = true)
|
|
17
|
+
|
|
18
|
+
import { actions } from '@live-change/vue3-ssr'
|
|
19
|
+
import { inject } from 'vue'
|
|
20
|
+
import { useRouter } from 'vue-router'
|
|
21
|
+
const router = useRouter()
|
|
22
|
+
|
|
23
|
+
const workingZone = inject('workingZone')
|
|
24
|
+
|
|
25
|
+
const { signOut } = actions().user
|
|
26
|
+
|
|
27
|
+
if(typeof window != 'undefined') {
|
|
28
|
+
workingZone.addPromise('signOut', (async () => {
|
|
29
|
+
await signOut({})
|
|
30
|
+
router.push({name: 'user:signOutFinished'})
|
|
31
|
+
})())
|
|
32
|
+
}
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<style>
|
|
36
|
+
|
|
37
|
+
</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">Signed Out</div>
|
|
5
|
+
<p class="mt-0 p-0 line-height-3">You have successfully signed out of your account.</p>
|
|
6
|
+
</div>
|
|
7
|
+
</div>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script setup>
|
|
11
|
+
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<style>
|
|
15
|
+
|
|
16
|
+
</style>
|
|
@@ -0,0 +1,51 @@
|
|
|
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">Sign Up</div>
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<command-form service="messageAuthentication" action="signUpEmail" v-slot="{ data, submit }"
|
|
9
|
+
@done="handleSent" keepOnDone>
|
|
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
|
+
aria-describedby="email-help" :class="{ 'p-invalid': data.emailError}"
|
|
17
|
+
v-model="data.email" />
|
|
18
|
+
<small v-if="data.emailError" id="email-help" class="p-error">{{ data.emailError }}</small>
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<Button label="Sign Up with email" icon="pi pi-user" class="w-full" type="submit"></Button>
|
|
22
|
+
|
|
23
|
+
</command-form>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<script setup>
|
|
29
|
+
import InputText from "primevue/inputtext"
|
|
30
|
+
import Checkbox from "primevue/checkbox"
|
|
31
|
+
import Button from "primevue/button"
|
|
32
|
+
import Divider from "primevue/divider"
|
|
33
|
+
|
|
34
|
+
import { useRouter } from 'vue-router'
|
|
35
|
+
const router = useRouter()
|
|
36
|
+
|
|
37
|
+
function handleSent({ parameters, result }) {
|
|
38
|
+
const { authentication } = result
|
|
39
|
+
router.push({
|
|
40
|
+
name: 'user:sent',
|
|
41
|
+
params: {
|
|
42
|
+
authentication
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<style>
|
|
50
|
+
|
|
51
|
+
</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">Signed Up</div>
|
|
5
|
+
<p class="mt-0 p-0 line-height-3">Congratulations! You have successfully created your account.</p>
|
|
6
|
+
</div>
|
|
7
|
+
</div>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script setup>
|
|
11
|
+
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<style>
|
|
15
|
+
|
|
16
|
+
</style>
|