mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-08-05 13:35:23 +02:00
feat(frontend): ✨ Fix scheduler, forgot password flow, and minor bug fixes (#725)
* feat(frontend): 💄 add recipe title * fix(frontend): 🐛 fixes #722 side-bar issue * feat(frontend): ✨ Add page titles to all pages * minor cleanup * refactor(backend): ♻️ rewrite scheduler to be more modulare and work * feat(frontend): ✨ start password reset functionality * refactor(backend): ♻️ refactor application settings to facilitate dependency injection * refactor(backend): 🔥 remove RECIPE_SETTINGS env variables in favor of group settings * formatting * refactor(backend): ♻️ align naming convention * feat(backend): ✨ password reset * test(backend): ✅ password reset * feat(frontend): ✨ self-service password reset * purge password schedule * update user creation for tests Co-authored-by: Hayden <hay-kot@pm.me>
This commit is contained in:
parent
d1f0441252
commit
2e9026f9ea
121 changed files with 1461 additions and 679 deletions
|
@ -99,6 +99,11 @@ export default defineComponent({
|
|||
appInfo,
|
||||
};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("about.about") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -146,6 +146,11 @@ export default defineComponent({
|
|||
backupsFileNameDownload,
|
||||
};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("sidebar.backups") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -146,6 +146,11 @@ export default defineComponent({
|
|||
|
||||
return { statistics, events, deleteEvents, deleteEvent };
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("sidebar.dashboard") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -111,5 +111,10 @@ export default defineComponent({
|
|||
|
||||
return { ...toRefs(state), groups, refreshAllGroups, deleteGroup, createGroup };
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("group.manage-groups") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -153,6 +153,11 @@ export default defineComponent({
|
|||
},
|
||||
};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("sidebar.manage-users") as string,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
updateUser(userData: any) {
|
||||
this.updateMode = true;
|
||||
|
|
|
@ -17,6 +17,11 @@ export default defineComponent({
|
|||
setup() {
|
||||
return {};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("settings.migrations") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -156,6 +156,11 @@ export default defineComponent({
|
|||
testEmail,
|
||||
};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("settings.site-settings") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -12,6 +12,11 @@ export default defineComponent({
|
|||
setup() {
|
||||
return {};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("sidebar.categories") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -113,6 +113,11 @@ export default defineComponent({
|
|||
workingFoodData,
|
||||
};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: "Foods",
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -215,6 +215,11 @@ export default defineComponent({
|
|||
notificationTypes,
|
||||
};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("events.notification") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -12,6 +12,11 @@ export default defineComponent({
|
|||
setup() {
|
||||
return {};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("settings.organize") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -12,6 +12,11 @@ export default defineComponent({
|
|||
setup() {
|
||||
return {};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("sidebar.tags") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -115,6 +115,11 @@ export default defineComponent({
|
|||
workingUnitData,
|
||||
};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: "Units",
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, useRoute, ref } from "@nuxtjs/composition-api";
|
||||
import { defineComponent, useRoute, ref, useMeta } from "@nuxtjs/composition-api";
|
||||
import RecipeCardSection from "@/components/Domain/Recipe/RecipeCardSection.vue";
|
||||
import { useCookbook } from "~/composables/use-group-cookbooks";
|
||||
export default defineComponent({
|
||||
|
@ -37,11 +37,18 @@ export default defineComponent({
|
|||
|
||||
const book = getOne(slug);
|
||||
|
||||
useMeta(() => {
|
||||
return {
|
||||
title: book?.value?.name || "Cookbook",
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
book,
|
||||
tab,
|
||||
};
|
||||
},
|
||||
head: {}, // Must include for useMeta
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
86
frontend/pages/forgot-password.vue
Normal file
86
frontend/pages/forgot-password.vue
Normal file
|
@ -0,0 +1,86 @@
|
|||
<template>
|
||||
<v-container fill-height fluid class="d-flex justify-center align-center">
|
||||
<v-card color="background d-flex flex-column align-center" flat width="600px">
|
||||
<v-card-title class="headline justify-center"> Forgot Password </v-card-title>
|
||||
<BaseDivider />
|
||||
<v-card-text>
|
||||
<v-form @submit.prevent="requestLink()">
|
||||
<v-text-field
|
||||
v-model="email"
|
||||
filled
|
||||
rounded
|
||||
autofocus
|
||||
class="rounded-lg"
|
||||
name="login"
|
||||
:label="$t('user.email')"
|
||||
type="text"
|
||||
/>
|
||||
<p class="text-center">Please enter your email address and we will send you a link to reset your password.</p>
|
||||
<v-card-actions class="justify-center">
|
||||
<div class="max-button">
|
||||
<v-btn :loading="loading" color="primary" type="submit" large rounded class="rounded-xl" block>
|
||||
<v-icon left>
|
||||
{{ $globals.icons.email }}
|
||||
</v-icon>
|
||||
{{ $t("user.reset-password") }}
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-card-actions>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-btn class="mx-auto" text nuxt to="/login"> {{ $t("user.login") }} </v-btn>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, toRefs, reactive } from "@nuxtjs/composition-api";
|
||||
import { useApiSingleton } from "~/composables/use-api";
|
||||
import { alert } from "~/composables/use-toast";
|
||||
export default defineComponent({
|
||||
layout: "basic",
|
||||
|
||||
setup() {
|
||||
const state = reactive({
|
||||
email: "",
|
||||
loading: false,
|
||||
error: false,
|
||||
});
|
||||
|
||||
const api = useApiSingleton();
|
||||
|
||||
async function requestLink() {
|
||||
state.loading = true;
|
||||
// TODO: Fix Response to send meaningful error
|
||||
const { response } = await api.email.sendForgotPassword({ email: state.email });
|
||||
|
||||
if (response?.status === 200) {
|
||||
state.loading = false;
|
||||
state.error = false;
|
||||
alert.success("Link successfully sent");
|
||||
} else {
|
||||
state.loading = false;
|
||||
state.error = true;
|
||||
alert.error("Email failure");
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
requestLink,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("user.login") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="css">
|
||||
.max-button {
|
||||
width: 300px;
|
||||
}
|
||||
</style>
|
|
@ -18,9 +18,7 @@ export default defineComponent({
|
|||
components: { RecipeCardSection },
|
||||
setup() {
|
||||
const { assignSorted } = useRecipes(false);
|
||||
|
||||
useStaticRoutes();
|
||||
|
||||
return { recentRecipes, assignSorted };
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<v-container fill-height fluid class="d-flex justify-center align-center">
|
||||
<v-card color="background d-flex flex-column align-center" flat width="600px">
|
||||
<v-card tag="section" color="background d-flex flex-column align-center" flat width="600px">
|
||||
<svg
|
||||
id="bbc88faa-5a3b-49cf-bdbb-6c9ab11be594"
|
||||
data-name="Layer 1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 728 754.88525"
|
||||
style="max-height: 200px"
|
||||
style="max-height: 100px"
|
||||
class="mt-2"
|
||||
>
|
||||
<rect
|
||||
|
@ -182,8 +182,11 @@
|
|||
</v-card-actions>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-btn v-if="allowSignup" rounded class="mx-auto" text to="/register"> {{ $t("user.register") }} </v-btn>
|
||||
<v-btn v-else class="mx-auto" text disabled> {{ $t("user.invite-only") }} </v-btn>
|
||||
<v-card-actions>
|
||||
<v-btn v-if="allowSignup" text to="/register"> {{ $t("user.register") }} </v-btn>
|
||||
<v-btn v-else text disabled> {{ $t("user.invite-only") }} </v-btn>
|
||||
<v-btn class="mr-auto" text to="/forgot-password"> {{ $t("user.reset-password") }} </v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</template>
|
||||
|
@ -223,10 +226,15 @@ export default defineComponent({
|
|||
authenticate,
|
||||
};
|
||||
},
|
||||
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("user.login") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="css">
|
||||
.max-button {
|
||||
width: 300px;
|
||||
|
|
|
@ -309,6 +309,11 @@ export default defineComponent({
|
|||
days,
|
||||
};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("meal-plan.dinner-this-week") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -9,6 +9,11 @@ export default defineComponent({
|
|||
setup() {
|
||||
return {};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("meal-plan.dinner-this-week") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -397,6 +397,34 @@ export default defineComponent({
|
|||
scale: 1,
|
||||
});
|
||||
|
||||
// ===============================================================
|
||||
// Metadata
|
||||
|
||||
const structuredData = computed(() => {
|
||||
return {
|
||||
"@context": "http://schema.org",
|
||||
"@type": "Recipe",
|
||||
...recipe.value,
|
||||
};
|
||||
});
|
||||
|
||||
useMeta(() => {
|
||||
return {
|
||||
title: recipe?.value?.name || "Recipe",
|
||||
// @ts-ignore
|
||||
mainImage: recipeImage(recipe?.value?.image),
|
||||
meta: [
|
||||
{
|
||||
hid: "description",
|
||||
name: "description",
|
||||
content: recipe?.value?.description || "",
|
||||
},
|
||||
],
|
||||
__dangerouslyDisableSanitizers: ["script"],
|
||||
script: [{ innerHTML: JSON.stringify(structuredData), type: "application/ld+json" }],
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
scaledYield,
|
||||
...toRefs(state),
|
||||
|
|
|
@ -214,6 +214,11 @@ export default defineComponent({
|
|||
validators,
|
||||
};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("general.create") as string,
|
||||
};
|
||||
},
|
||||
// Computed State is used because of the limitation of vue-composition-api in v2.0
|
||||
computed: {
|
||||
tab: {
|
||||
|
|
|
@ -47,6 +47,11 @@ export default defineComponent({
|
|||
|
||||
return { recipes, infiniteScroll, loading };
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("page.all-recipes") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
@ -29,6 +29,11 @@ export default defineComponent({
|
|||
}, slug);
|
||||
return { category };
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("sidebar.categories") as string,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
assignSorted(val: Array<Recipe>) {
|
||||
if (this.category) {
|
||||
|
|
|
@ -59,6 +59,11 @@ export default defineComponent({
|
|||
|
||||
return { categories, api, categoriesByLetter };
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("sidebar.categories") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -29,6 +29,11 @@ export default defineComponent({
|
|||
}, slug);
|
||||
return { tag };
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("sidebar.tags") as string,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
assignSorted(val: Array<Recipe>) {
|
||||
if (this.tag) {
|
||||
|
|
|
@ -59,6 +59,11 @@ export default defineComponent({
|
|||
|
||||
return { tags, api, tagsByLetter };
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("sidebar.tags") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -174,5 +174,10 @@ export default defineComponent({
|
|||
register,
|
||||
};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("user.register") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
140
frontend/pages/reset-password.vue
Normal file
140
frontend/pages/reset-password.vue
Normal file
|
@ -0,0 +1,140 @@
|
|||
<template>
|
||||
<v-container fill-height fluid class="d-flex justify-center align-center">
|
||||
<v-card color="background d-flex flex-column align-center" flat width="600px">
|
||||
<v-card-title class="headline justify-center"> Reset Password </v-card-title>
|
||||
<BaseDivider />
|
||||
<v-card-text>
|
||||
<v-form @submit.prevent="requestLink()">
|
||||
<v-text-field
|
||||
v-model="email"
|
||||
:prepend-icon="$globals.icons.email"
|
||||
filled
|
||||
rounded
|
||||
autofocus
|
||||
class="rounded-lg"
|
||||
name="login"
|
||||
:label="$t('user.email')"
|
||||
type="text"
|
||||
/>
|
||||
<v-text-field
|
||||
v-model="password"
|
||||
filled
|
||||
rounded
|
||||
class="rounded-lg"
|
||||
:prepend-icon="$globals.icons.lock"
|
||||
name="password"
|
||||
label="Password"
|
||||
type="password"
|
||||
:rules="[validators.required]"
|
||||
/>
|
||||
<v-text-field
|
||||
v-model="passwordConfirm"
|
||||
filled
|
||||
rounded
|
||||
validate-on-blur
|
||||
class="rounded-lg"
|
||||
:prepend-icon="$globals.icons.lock"
|
||||
name="password"
|
||||
label="Confirm Password"
|
||||
type="password"
|
||||
:rules="[validators.required, passwordMatch]"
|
||||
/>
|
||||
<p class="text-center">Please enter your new password.</p>
|
||||
<v-card-actions class="justify-center">
|
||||
<div class="max-button">
|
||||
<v-btn
|
||||
:loading="loading"
|
||||
color="primary"
|
||||
:disabled="token === ''"
|
||||
type="submit"
|
||||
large
|
||||
rounded
|
||||
class="rounded-xl"
|
||||
block
|
||||
>
|
||||
<v-icon left>
|
||||
{{ $globals.icons.lock }}
|
||||
</v-icon>
|
||||
{{ token === "" ? "Token Required" : $t("user.reset-password") }}
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-card-actions>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-btn class="mx-auto" text nuxt to="/login"> {{ $t("user.login") }} </v-btn>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, toRefs, reactive } from "@nuxtjs/composition-api";
|
||||
import { useApiSingleton } from "~/composables/use-api";
|
||||
import { alert } from "~/composables/use-toast";
|
||||
import { validators } from "@/composables/use-validators";
|
||||
import { useRouteQuery } from "~/composables/use-router";
|
||||
export default defineComponent({
|
||||
layout: "basic",
|
||||
|
||||
setup() {
|
||||
const state = reactive({
|
||||
email: "",
|
||||
password: "",
|
||||
passwordConfirm: "",
|
||||
loading: false,
|
||||
error: false,
|
||||
});
|
||||
|
||||
const passwordMatch = () => state.password === state.passwordConfirm || "Passwords do not match";
|
||||
|
||||
// ===================
|
||||
// Token Getter
|
||||
const token = useRouteQuery("token", "");
|
||||
|
||||
// ===================
|
||||
// API
|
||||
const api = useApiSingleton();
|
||||
async function requestLink() {
|
||||
state.loading = true;
|
||||
// TODO: Fix Response to send meaningful error
|
||||
const { response } = await api.users.resetPassword({
|
||||
token: token.value,
|
||||
email: state.email,
|
||||
password: state.password,
|
||||
passwordConfirm: state.passwordConfirm,
|
||||
});
|
||||
|
||||
state.loading = false;
|
||||
|
||||
if (response?.status === 200) {
|
||||
state.loading = false;
|
||||
state.error = false;
|
||||
alert.success("Password Reset Successful");
|
||||
} else {
|
||||
state.loading = false;
|
||||
state.error = true;
|
||||
alert.error("Something Went Wrong");
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
passwordMatch,
|
||||
token,
|
||||
requestLink,
|
||||
validators,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("user.login") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="css">
|
||||
.max-button {
|
||||
width: 300px;
|
||||
}
|
||||
</style>
|
|
@ -110,6 +110,11 @@ export default defineComponent({
|
|||
},
|
||||
};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("search.search"),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
searchString: {
|
||||
set(q) {
|
||||
|
|
|
@ -9,6 +9,11 @@ export default defineComponent({
|
|||
setup() {
|
||||
return {};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("shopping-list.shopping-list") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api";
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
return {};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("shopping-list.shopping-list") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
return {}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
<style scoped>
|
||||
</style>
|
|
@ -9,6 +9,11 @@ export default defineComponent({
|
|||
setup() {
|
||||
return {};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("general.favorites") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -3,13 +3,18 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import { defineComponent } from "@nuxtjs/composition-api";
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
return {}
|
||||
}
|
||||
})
|
||||
return {};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("settings.profile") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -63,6 +63,11 @@ export default defineComponent({
|
|||
actions,
|
||||
};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("settings.pages") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -133,6 +133,11 @@ export default defineComponent({
|
|||
allDays,
|
||||
};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("group.group") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -109,5 +109,10 @@ export default defineComponent({
|
|||
|
||||
return { members, headers, setPermissions };
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: "Members",
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -64,5 +64,10 @@ export default defineComponent({
|
|||
actions,
|
||||
};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("settings.webhooks.webhooks") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -126,6 +126,11 @@ export default defineComponent({
|
|||
|
||||
return { createToken, deleteToken, copyToken, createdToken, loading, name, user, resetCreate };
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("settings.token.api-tokens") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
<template #title> Your Profile Settings </template>
|
||||
</BasePageTitle>
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<ToggleState tag="article">
|
||||
<template #activator="{ toggle, state }">
|
||||
|
@ -161,6 +159,11 @@ export default defineComponent({
|
|||
loading: false,
|
||||
};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("settings.profile") as string,
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
async changePassword() {
|
||||
|
|
|
@ -193,6 +193,11 @@ export default defineComponent({
|
|||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("settings.profile") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api";
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
return {};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
|
@ -1,113 +0,0 @@
|
|||
<template>
|
||||
<v-container fill-height fluid class="d-flex justify-center align-start">
|
||||
<v-card color="background d-flex flex-column align-center " flat width="600px">
|
||||
<svg
|
||||
id="b76bd6b3-ad77-41ff-b778-1d1d054fe577"
|
||||
data-name="Layer 1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
style="max-height: 300px"
|
||||
viewBox="0 0 570 511.67482"
|
||||
>
|
||||
<path
|
||||
d="M879.99927,389.83741a.99678.99678,0,0,1-.5708-.1792L602.86963,197.05469a5.01548,5.01548,0,0,0-5.72852.00977L322.57434,389.65626a1.00019,1.00019,0,0,1-1.14868-1.6377l274.567-192.5918a7.02216,7.02216,0,0,1,8.02-.01318l276.55883,192.603a1.00019,1.00019,0,0,1-.57226,1.8208Z"
|
||||
transform="translate(-315 -194.16259)"
|
||||
fill="#3f3d56"
|
||||
/>
|
||||
<polygon
|
||||
points="23.264 202.502 285.276 8.319 549.276 216.319 298.776 364.819 162.776 333.819 23.264 202.502"
|
||||
fill="#e6e6e6"
|
||||
/>
|
||||
<path
|
||||
d="M489.25553,650.70367H359.81522a6.04737,6.04737,0,1,1,0-12.09473H489.25553a6.04737,6.04737,0,1,1,0,12.09473Z"
|
||||
transform="translate(-315 -194.16259)"
|
||||
fill="#e58325"
|
||||
/>
|
||||
<path
|
||||
d="M406.25553,624.70367H359.81522a6.04737,6.04737,0,1,1,0-12.09473h46.44031a6.04737,6.04737,0,1,1,0,12.09473Z"
|
||||
transform="translate(-315 -194.16259)"
|
||||
fill="#e58325"
|
||||
/>
|
||||
<path
|
||||
d="M603.96016,504.82207a7.56366,7.56366,0,0,1-2.86914-.562L439.5002,437.21123v-209.874a7.00817,7.00817,0,0,1,7-7h310a7.00818,7.00818,0,0,1,7,7v210.0205l-.30371.12989L606.91622,504.22734A7.61624,7.61624,0,0,1,603.96016,504.82207Z"
|
||||
transform="translate(-315 -194.16259)"
|
||||
fill="#fff"
|
||||
/>
|
||||
<path
|
||||
d="M603.96016,505.32158a8.07177,8.07177,0,0,1-3.05957-.59863L439.0002,437.54521v-210.208a7.50851,7.50851,0,0,1,7.5-7.5h310a7.50851,7.50851,0,0,1,7.5,7.5V437.68779l-156.8877,66.999A8.10957,8.10957,0,0,1,603.96016,505.32158Zm-162.96-69.1123,160.66309,66.66455a6.1182,6.1182,0,0,0,4.668-.02784l155.669-66.47851V227.33721a5.50653,5.50653,0,0,0-5.5-5.5h-310a5.50653,5.50653,0,0,0-5.5,5.5Z"
|
||||
transform="translate(-315 -194.16259)"
|
||||
fill="#3f3d56"
|
||||
/>
|
||||
<path
|
||||
d="M878,387.83741h-.2002L763,436.85743l-157.06982,67.07a5.06614,5.06614,0,0,1-3.88038.02L440,436.71741l-117.62012-48.8-.17968-.08H322a7.00778,7.00778,0,0,0-7,7v304a7.00779,7.00779,0,0,0,7,7H878a7.00779,7.00779,0,0,0,7-7v-304A7.00778,7.00778,0,0,0,878,387.83741Zm5,311a5.002,5.002,0,0,1-5,5H322a5.002,5.002,0,0,1-5-5v-304a5.01106,5.01106,0,0,1,4.81006-5L440,438.87739l161.28027,66.92a7.12081,7.12081,0,0,0,5.43994-.03L763,439.02741l115.2002-49.19a5.01621,5.01621,0,0,1,4.7998,5Z"
|
||||
transform="translate(-315 -194.16259)"
|
||||
fill="#3f3d56"
|
||||
/>
|
||||
<path
|
||||
d="M602.345,445.30958a27.49862,27.49862,0,0,1-16.5459-5.4961l-.2959-.22217-62.311-47.70752a27.68337,27.68337,0,1,1,33.67407-43.94921l40.36035,30.94775,95.37793-124.38672a27.68235,27.68235,0,0,1,38.81323-5.12353l-.593.80517.6084-.79346a27.71447,27.71447,0,0,1,5.12353,38.81348L624.36938,434.50586A27.69447,27.69447,0,0,1,602.345,445.30958Z"
|
||||
transform="translate(-315 -194.16259)"
|
||||
fill="#e58325"
|
||||
/>
|
||||
</svg>
|
||||
<v-card-title class="headline justify-center"> Request Secure Link </v-card-title>
|
||||
<BaseDivider />
|
||||
<v-card-text>
|
||||
<v-form @submit.prevent="authenticate()">
|
||||
<v-text-field
|
||||
v-model="form.email"
|
||||
filled
|
||||
rounded
|
||||
autofocus
|
||||
class="rounded-lg"
|
||||
prepend-icon="mdi-account"
|
||||
name="login"
|
||||
label="Email"
|
||||
type="text"
|
||||
/>
|
||||
<v-btn :loading="loggingIn" color="primary" type="submit" large rounded class="rounded-xl" block>
|
||||
Submit
|
||||
</v-btn>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-btn v-if="allowSignup" class="mx-auto" text to="/login"> Login </v-btn>
|
||||
</v-card>
|
||||
<!-- <v-col class="fill-height"> </v-col> -->
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api";
|
||||
|
||||
export default defineComponent({
|
||||
layout: "basic",
|
||||
setup() {
|
||||
return {};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loggingIn: false,
|
||||
form: {
|
||||
email: "changeme@email.com",
|
||||
password: "MyPassword",
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
allowSignup(): boolean {
|
||||
// @ts-ignore
|
||||
return process.env.ALLOW_SIGNUP;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async authenticate() {
|
||||
this.loggingIn = true;
|
||||
const formData = new FormData();
|
||||
formData.append("username", this.form.email);
|
||||
formData.append("password", this.form.password);
|
||||
|
||||
await this.$auth.loginWith("local", { data: formData });
|
||||
this.loggingIn = false;
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
Loading…
Add table
Add a link
Reference in a new issue