Added account update form and e-mail verification.
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
<script setup>
|
||||
import { inject, onMounted, ref } from 'vue';
|
||||
|
||||
import Password from '#/components/form/password.vue';
|
||||
|
||||
import { post } from '#/src/api.js';
|
||||
import navigate from '#/src/navigate.js';
|
||||
|
||||
@@ -14,7 +16,6 @@ const password = ref('');
|
||||
const submitted = ref(false);
|
||||
const errorMsg = ref(null);
|
||||
const userInput = ref(null);
|
||||
const showPassword = ref(false);
|
||||
|
||||
async function login() {
|
||||
submitted.value = true;
|
||||
@@ -88,31 +89,10 @@ onMounted(() => {
|
||||
class="input"
|
||||
>
|
||||
|
||||
<div class="password-container">
|
||||
<input
|
||||
v-model="password"
|
||||
:type="showPassword ? 'input' : 'password'"
|
||||
placeholder="Password"
|
||||
class="password input"
|
||||
>
|
||||
|
||||
<div
|
||||
class="password-show"
|
||||
@click="showPassword = !showPassword"
|
||||
>
|
||||
<Icon
|
||||
v-show="!showPassword"
|
||||
icon="eye"
|
||||
class="password-show"
|
||||
/>
|
||||
|
||||
<Icon
|
||||
v-show="showPassword"
|
||||
icon="eye-blocked"
|
||||
class="password-show"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Password
|
||||
:password="password"
|
||||
@input="(newPassword) => password = newPassword"
|
||||
/>
|
||||
|
||||
<button
|
||||
class="button button-submit"
|
||||
@@ -129,6 +109,10 @@ onMounted(() => {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
import VueHCaptcha from '@hcaptcha/vue3-hcaptcha';
|
||||
import { inject, onMounted, ref } from 'vue';
|
||||
|
||||
import Password from '#/components/form/password.vue';
|
||||
|
||||
import { post } from '#/src/api.js';
|
||||
import navigate from '#/src/navigate.js';
|
||||
|
||||
@@ -16,7 +18,6 @@ const passwordConfirm = ref('');
|
||||
const errorMsg = ref(null);
|
||||
const submitted = ref(false);
|
||||
const userInput = ref(null);
|
||||
const showPassword = ref(false);
|
||||
const captcha = ref(null);
|
||||
|
||||
async function signup() {
|
||||
@@ -113,63 +114,18 @@ onMounted(() => {
|
||||
required
|
||||
>
|
||||
|
||||
<div class="password-container">
|
||||
<input
|
||||
v-model="password"
|
||||
:type="showPassword ? 'input' : 'password'"
|
||||
autocomplete="new-password"
|
||||
minlength="3"
|
||||
placeholder="Password"
|
||||
class="password input"
|
||||
required
|
||||
>
|
||||
<Password
|
||||
:password="password"
|
||||
autocomplete="new-password"
|
||||
@input="(newPassword) => password = newPassword"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="password-show"
|
||||
@click="showPassword = !showPassword"
|
||||
>
|
||||
<Icon
|
||||
v-show="!showPassword"
|
||||
icon="eye"
|
||||
class="password-show"
|
||||
/>
|
||||
|
||||
<Icon
|
||||
v-show="showPassword"
|
||||
icon="eye-blocked"
|
||||
class="password-show"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="password-container">
|
||||
<input
|
||||
v-model="passwordConfirm"
|
||||
:type="showPassword ? 'input' : 'password'"
|
||||
autocomplete="new-password"
|
||||
minlength="3"
|
||||
placeholder="Confirm password"
|
||||
class="password input"
|
||||
required
|
||||
>
|
||||
|
||||
<div
|
||||
class="password-show"
|
||||
@click="showPassword = !showPassword"
|
||||
>
|
||||
<Icon
|
||||
v-show="!showPassword"
|
||||
icon="eye"
|
||||
class="password-show"
|
||||
/>
|
||||
|
||||
<Icon
|
||||
v-show="showPassword"
|
||||
icon="eye-blocked"
|
||||
class="password-show"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Password
|
||||
:password="passwordConfirm"
|
||||
autocomplete="new-password"
|
||||
placeholder="Confirm password"
|
||||
@input="(newPassword) => passwordConfirm = newPassword"
|
||||
/>
|
||||
|
||||
<VueHCaptcha
|
||||
v-if="env.captcha.enabled"
|
||||
@@ -193,6 +149,10 @@ onMounted(() => {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
|
||||
@@ -9,6 +9,7 @@ import Summaries from '#/components/scenes/summaries.vue';
|
||||
// filters are implemented as generic settings panel in case we want to extend it in the future
|
||||
import Settings from '#/components/settings/settings.vue';
|
||||
import Stashes from '#/components/stashes/stashes.vue';
|
||||
import Account from '#/components/user/account.vue';
|
||||
import ApiKeys from '#/components/user/api-keys.vue';
|
||||
import Preferences from '#/components/user/preferences.vue';
|
||||
|
||||
@@ -19,6 +20,7 @@ const domain = pageContext.routeParams.domain;
|
||||
|
||||
const user = pageContext.user;
|
||||
const profile = ref(pageContext.pageProps.profile);
|
||||
const primaryStash = pageContext.assets.primaryStash;
|
||||
|
||||
const showFilters = ref(false);
|
||||
|
||||
@@ -70,6 +72,12 @@ function scrollHorizontal(event) {
|
||||
class="domains nobar"
|
||||
@wheel.prevent="scrollHorizontal"
|
||||
>
|
||||
<a
|
||||
v-if="primaryStash"
|
||||
:href="`/stash/${user.username}/${primaryStash.slug}`"
|
||||
class="domain nolink"
|
||||
><Icon :icon="primaryStash.slug === 'favorites' ? 'heart7' : 'folder-heart'" />{{ primaryStash.name }}</a>
|
||||
|
||||
<a
|
||||
:href="`/user/${profile.username}/stashes`"
|
||||
class="domain nolink"
|
||||
@@ -88,13 +96,6 @@ function scrollHorizontal(event) {
|
||||
:class="{ active: section === 'templates' }"
|
||||
><Icon icon="paste3" />Templates</a>
|
||||
|
||||
<a
|
||||
v-if="profile.isIdentityVerified"
|
||||
:href="`/user/${profile.username}/api`"
|
||||
class="domain nolink"
|
||||
:class="{ active: section === 'api' }"
|
||||
><Icon icon="key" />API Keys</a>
|
||||
|
||||
<a
|
||||
:href="`/user/${profile.username}/revisions/scenes`"
|
||||
class="domain nolink"
|
||||
@@ -114,11 +115,24 @@ function scrollHorizontal(event) {
|
||||
@click="showFilters = true"
|
||||
><Icon icon="filter" />Filters</span>
|
||||
|
||||
<a
|
||||
v-if="profile.isIdentityVerified"
|
||||
:href="`/user/${profile.username}/api`"
|
||||
class="domain nolink"
|
||||
:class="{ active: section === 'api' }"
|
||||
><Icon icon="key" />API Keys</a>
|
||||
|
||||
<a
|
||||
:href="`/user/${profile.username}/preferences`"
|
||||
class="domain nolink"
|
||||
:class="{ active: section === 'preferences' }"
|
||||
><Icon icon="equalizer" />Preferences</a>
|
||||
|
||||
<a
|
||||
:href="`/user/${profile.username}/account`"
|
||||
class="domain nolink"
|
||||
:class="{ active: section === 'account' }"
|
||||
><Icon icon="user-tie" />Account</a>
|
||||
</nav>
|
||||
|
||||
<Stashes v-if="section === 'stashes'" />
|
||||
@@ -129,13 +143,9 @@ function scrollHorizontal(event) {
|
||||
:release="mockupRelease"
|
||||
/>
|
||||
|
||||
<ApiKeys
|
||||
v-if="section === 'api'"
|
||||
/>
|
||||
|
||||
<Preferences
|
||||
v-if="section === 'preferences'"
|
||||
/>
|
||||
<ApiKeys v-if="section === 'api'" />
|
||||
<Preferences v-if="section === 'preferences'" />
|
||||
<Account v-if="section === 'account'" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
|
||||
85
pages/verify/email/+Page.vue
Normal file
85
pages/verify/email/+Page.vue
Normal file
@@ -0,0 +1,85 @@
|
||||
<script setup>
|
||||
import { inject, ref } from 'vue';
|
||||
|
||||
import Ellipsis from '#/components/loading/ellipsis.vue';
|
||||
|
||||
import { post } from '#/src/api.js';
|
||||
|
||||
const { urlParsed } = inject('pageContext');
|
||||
const { userId, token } = urlParsed.search;
|
||||
|
||||
const errorMsg = ref(null);
|
||||
const submitting = ref(false);
|
||||
const success = ref(false);
|
||||
|
||||
async function verify() {
|
||||
errorMsg.value = null;
|
||||
submitting.value = true;
|
||||
|
||||
try {
|
||||
await post('/emails/verify', {
|
||||
userId,
|
||||
token,
|
||||
}, {
|
||||
appendErrorMessage: true,
|
||||
});
|
||||
|
||||
success.value = true;
|
||||
}
|
||||
catch (error) {
|
||||
errorMsg.value = error.message;
|
||||
}
|
||||
finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="content">
|
||||
<Ellipsis v-if="submitting" />
|
||||
|
||||
<strong
|
||||
v-else-if="errorMsg"
|
||||
class="feedback error"
|
||||
>{{ errorMsg }}</strong>
|
||||
|
||||
<strong
|
||||
v-else-if="success"
|
||||
class="feedback success"
|
||||
>Verification successful</strong>
|
||||
|
||||
<button
|
||||
v-else
|
||||
class="button button-primary"
|
||||
@click="verify"
|
||||
>Click here to verify your e-mail address</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-grow: 1;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.button {
|
||||
padding: 1rem 1.5rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.feedback {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--error);
|
||||
}
|
||||
|
||||
.success {
|
||||
color: var(--success);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user