mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-08-05 05:25:26 +02:00
feat(backend): ✨ migrate site-settings to groups (#673)
* feat(frontend): ✨ add user registration page (WIP) * feat(backend): ✨ add user registration (WIP) * test(backend): ✅ add validator testing for registration schema * feat(backend): ✨ continued work on user sign-up * feat(backend): ✨ add signup flow and user/group settings * test(backend): ✅ user-creation tests and small refactor of existing tests * fix(backend): ✅ fix failing group tests * style: 🎨 fix lint issues Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
parent
e179dcdb10
commit
3c504e7048
63 changed files with 1665 additions and 841 deletions
|
@ -8,6 +8,8 @@ const routes = {
|
|||
groupsSelf: `${prefix}/groups/self`,
|
||||
categories: `${prefix}/groups/categories`,
|
||||
|
||||
preferences: `${prefix}/groups/preferences`,
|
||||
|
||||
groupsId: (id: string | number) => `${prefix}/groups/${id}`,
|
||||
};
|
||||
|
||||
|
@ -21,13 +23,34 @@ export interface CreateGroup {
|
|||
name: string;
|
||||
}
|
||||
|
||||
export interface UpdatePreferences {
|
||||
privateGroup: boolean;
|
||||
firstDayOfWeek: number;
|
||||
recipePublic: boolean;
|
||||
recipeShowNutrition: boolean;
|
||||
recipeShowAssets: boolean;
|
||||
recipeLandscapeView: boolean;
|
||||
recipeDisableComments: boolean;
|
||||
recipeDisableAmount: boolean;
|
||||
}
|
||||
|
||||
export interface Preferences extends UpdatePreferences {
|
||||
id: number;
|
||||
group_id: number;
|
||||
}
|
||||
|
||||
export interface Group extends CreateGroup {
|
||||
id: number;
|
||||
preferences: Preferences;
|
||||
}
|
||||
|
||||
export class GroupAPI extends BaseCRUDAPI<GroupInDB, CreateGroup> {
|
||||
baseRoute = routes.groups;
|
||||
itemRoute = routes.groupsId;
|
||||
/** Returns the Group Data for the Current User
|
||||
*/
|
||||
async getCurrentUserGroup() {
|
||||
return await this.requests.get(routes.groupsSelf);
|
||||
return await this.requests.get<Group>(routes.groupsSelf);
|
||||
}
|
||||
|
||||
async getCategories() {
|
||||
|
@ -37,4 +60,12 @@ export class GroupAPI extends BaseCRUDAPI<GroupInDB, CreateGroup> {
|
|||
async setCategories(payload: Category[]) {
|
||||
return await this.requests.put<Category[]>(routes.categories, payload);
|
||||
}
|
||||
|
||||
async getPreferences() {
|
||||
return await this.requests.get<Preferences>(routes.preferences);
|
||||
}
|
||||
|
||||
async setPreferences(payload: UpdatePreferences) {
|
||||
return await this.requests.put<Preferences>(routes.preferences, payload);
|
||||
}
|
||||
}
|
||||
|
|
25
frontend/api/class-interfaces/user-registration.ts
Normal file
25
frontend/api/class-interfaces/user-registration.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { BaseAPI } from "./_base";
|
||||
|
||||
export interface RegisterPayload {
|
||||
group: string;
|
||||
groupToken: string;
|
||||
email: string;
|
||||
password: string;
|
||||
passwordConfirm: string;
|
||||
advanced: boolean;
|
||||
private: boolean;
|
||||
}
|
||||
|
||||
const prefix = "/api";
|
||||
|
||||
const routes = {
|
||||
register: `${prefix}/users/register`,
|
||||
};
|
||||
|
||||
export class RegisterAPI extends BaseAPI {
|
||||
/** Returns a list of avaiable .zip files for import into Mealie.
|
||||
*/
|
||||
async register(payload: RegisterPayload) {
|
||||
return await this.requests.post<any>(routes.register, payload);
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ import { UnitAPI } from "./class-interfaces/recipe-units";
|
|||
import { CookbookAPI } from "./class-interfaces/cookbooks";
|
||||
import { WebhooksAPI } from "./class-interfaces/group-webhooks";
|
||||
import { AdminAboutAPI } from "./class-interfaces/admin-about";
|
||||
import { RegisterAPI } from "./class-interfaces/user-registration";
|
||||
import { ApiRequestInstance } from "~/types/api";
|
||||
|
||||
class AdminAPI {
|
||||
|
@ -46,6 +47,7 @@ class Api {
|
|||
public units: UnitAPI;
|
||||
public cookbooks: CookbookAPI;
|
||||
public groupWebhooks: WebhooksAPI;
|
||||
public register: RegisterAPI;
|
||||
|
||||
// Utils
|
||||
public upload: UploadFile;
|
||||
|
@ -67,6 +69,7 @@ class Api {
|
|||
this.groups = new GroupAPI(requests);
|
||||
this.cookbooks = new CookbookAPI(requests);
|
||||
this.groupWebhooks = new WebhooksAPI(requests);
|
||||
this.register = new RegisterAPI(requests);
|
||||
|
||||
// Admin
|
||||
this.events = new EventsAPI(requests);
|
||||
|
|
|
@ -61,13 +61,7 @@
|
|||
</v-fade-transition>
|
||||
</v-card-title>
|
||||
<v-card-text v-if="edit">
|
||||
<v-textarea
|
||||
:key="generateKey('instructions', index)"
|
||||
v-model="value[index]['text']"
|
||||
auto-grow
|
||||
dense
|
||||
rows="4"
|
||||
>
|
||||
<v-textarea :key="'instructions' + index" v-model="value[index]['text']" auto-grow dense rows="4">
|
||||
</v-textarea>
|
||||
</v-card-text>
|
||||
<v-expand-transition>
|
||||
|
|
|
@ -134,9 +134,9 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { ref } from "@nuxtjs/composition-api";
|
||||
import { validators } from "@/composables/use-validators";
|
||||
import { fieldTypes } from "@/composables/forms";
|
||||
import { ref } from "@nuxtjs/composition-api";
|
||||
|
||||
const BLUR_EVENT = "blur";
|
||||
|
||||
|
|
|
@ -138,9 +138,9 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { ref } from "@nuxtjs/composition-api";
|
||||
import { validators } from "@/composables/use-validators";
|
||||
import { fieldTypes } from "@/composables/forms";
|
||||
import { ref } from "@nuxtjs/composition-api";
|
||||
|
||||
const BLUR_EVENT = "blur";
|
||||
|
||||
|
|
|
@ -3,7 +3,38 @@ import { useAsyncKey } from "./use-utils";
|
|||
import { useApiSingleton } from "~/composables/use-api";
|
||||
import { CreateGroup } from "~/api/class-interfaces/groups";
|
||||
|
||||
export const useGroup = function () {
|
||||
export const useGroupSelf = function () {
|
||||
const api = useApiSingleton();
|
||||
|
||||
const actions = {
|
||||
get() {
|
||||
const group = useAsync(async () => {
|
||||
const { data } = await api.groups.getCurrentUserGroup();
|
||||
|
||||
return data;
|
||||
}, useAsyncKey());
|
||||
|
||||
return group;
|
||||
},
|
||||
async updatePreferences() {
|
||||
if (!group.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { data } = await api.groups.setPreferences(group.value.preferences);
|
||||
|
||||
if (data) {
|
||||
group.value.preferences = data;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const group = actions.get();
|
||||
|
||||
return { actions, group };
|
||||
};
|
||||
|
||||
export const useGroupCategories = function () {
|
||||
const api = useApiSingleton();
|
||||
|
||||
const actions = {
|
||||
|
@ -61,7 +92,6 @@ export const useGroups = function () {
|
|||
}
|
||||
|
||||
async function createGroup(payload: CreateGroup) {
|
||||
console.log(payload);
|
||||
loading.value = true;
|
||||
const { data } = await api.groups.createOne(payload);
|
||||
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
<template>
|
||||
<v-app dark>
|
||||
<!-- <TheSnackbar /> -->
|
||||
|
||||
<AppSidebar
|
||||
v-model="sidebar"
|
||||
absolute
|
||||
:top-link="topLinks"
|
||||
:secondary-links="$auth.user.admin ? adminLinks : null"
|
||||
:bottom-links="$auth.user.admin ? bottomLinks : null"
|
||||
:bottom-links="bottomLinks"
|
||||
:user="{ data: true }"
|
||||
:secondary-header="$t('user.admin')"
|
||||
@input="sidebar = !sidebar"
|
||||
|
@ -30,7 +27,7 @@
|
|||
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api";
|
||||
import { defineComponent, ref, useContext } from "@nuxtjs/composition-api";
|
||||
import AppHeader from "@/components/Layout/AppHeader.vue";
|
||||
import AppSidebar from "@/components/Layout/AppSidebar.vue";
|
||||
import TheSnackbar from "~/components/Layout/TheSnackbar.vue";
|
||||
|
@ -40,103 +37,110 @@ export default defineComponent({
|
|||
middleware: "auth",
|
||||
auth: true,
|
||||
setup() {
|
||||
return {};
|
||||
},
|
||||
data() {
|
||||
// @ts-ignore - $globals not found in type definition
|
||||
const { $globals, i18n } = useContext();
|
||||
|
||||
const sidebar = ref(null);
|
||||
|
||||
const topLinks = [
|
||||
{
|
||||
icon: $globals.icons.viewDashboard,
|
||||
to: "/admin/dashboard",
|
||||
title: i18n.t("sidebar.dashboard"),
|
||||
},
|
||||
{
|
||||
icon: $globals.icons.cog,
|
||||
to: "/admin/site-settings",
|
||||
title: i18n.t("sidebar.site-settings"),
|
||||
},
|
||||
{
|
||||
icon: $globals.icons.tools,
|
||||
to: "/admin/toolbox",
|
||||
title: i18n.t("sidebar.toolbox"),
|
||||
children: [
|
||||
{
|
||||
icon: $globals.icons.bellAlert,
|
||||
to: "/admin/toolbox/notifications",
|
||||
title: i18n.t("events.notification"),
|
||||
},
|
||||
{
|
||||
icon: $globals.icons.foods,
|
||||
to: "/admin/toolbox/foods",
|
||||
title: "Manage Foods",
|
||||
},
|
||||
{
|
||||
icon: $globals.icons.units,
|
||||
to: "/admin/toolbox/units",
|
||||
title: "Manage Units",
|
||||
},
|
||||
{
|
||||
icon: $globals.icons.tags,
|
||||
to: "/admin/toolbox/categories",
|
||||
title: i18n.t("sidebar.tags"),
|
||||
},
|
||||
{
|
||||
icon: $globals.icons.tags,
|
||||
to: "/admin/toolbox/tags",
|
||||
title: i18n.t("sidebar.categories"),
|
||||
},
|
||||
{
|
||||
icon: $globals.icons.broom,
|
||||
to: "/admin/toolbox/organize",
|
||||
title: i18n.t("settings.organize"),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: $globals.icons.group,
|
||||
to: "/admin/manage-users",
|
||||
title: i18n.t("sidebar.manage-users"),
|
||||
children: [
|
||||
{
|
||||
icon: $globals.icons.user,
|
||||
to: "/admin/manage-users/all-users",
|
||||
title: i18n.t("user.users"),
|
||||
},
|
||||
{
|
||||
icon: $globals.icons.group,
|
||||
to: "/admin/manage-users/all-groups",
|
||||
title: i18n.t("group.groups"),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: $globals.icons.import,
|
||||
to: "/admin/migrations",
|
||||
title: i18n.t("sidebar.migrations"),
|
||||
},
|
||||
{
|
||||
icon: $globals.icons.database,
|
||||
to: "/admin/backups",
|
||||
title: i18n.t("sidebar.backups"),
|
||||
},
|
||||
];
|
||||
|
||||
const bottomLinks = [
|
||||
{
|
||||
icon: $globals.icons.heart,
|
||||
title: i18n.t("about.support"),
|
||||
href: "https://github.com/sponsors/hay-kot",
|
||||
},
|
||||
{
|
||||
icon: $globals.icons.information,
|
||||
title: i18n.t("about.about"),
|
||||
to: "/admin/about",
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
sidebar: null,
|
||||
topLinks: [
|
||||
{
|
||||
icon: this.$globals.icons.viewDashboard,
|
||||
to: "/admin/dashboard",
|
||||
title: this.$t("sidebar.dashboard"),
|
||||
},
|
||||
{
|
||||
icon: this.$globals.icons.cog,
|
||||
to: "/admin/site-settings",
|
||||
title: this.$t("sidebar.site-settings"),
|
||||
},
|
||||
{
|
||||
icon: this.$globals.icons.tools,
|
||||
to: "/admin/toolbox",
|
||||
title: this.$t("sidebar.toolbox"),
|
||||
children: [
|
||||
{
|
||||
icon: this.$globals.icons.bellAlert,
|
||||
to: "/admin/toolbox/notifications",
|
||||
title: this.$t("events.notification"),
|
||||
},
|
||||
{
|
||||
icon: this.$globals.icons.foods,
|
||||
to: "/admin/toolbox/foods",
|
||||
title: "Manage Foods",
|
||||
},
|
||||
{
|
||||
icon: this.$globals.icons.units,
|
||||
to: "/admin/toolbox/units",
|
||||
title: "Manage Units",
|
||||
},
|
||||
{
|
||||
icon: this.$globals.icons.tags,
|
||||
to: "/admin/toolbox/categories",
|
||||
title: this.$t("sidebar.tags"),
|
||||
},
|
||||
{
|
||||
icon: this.$globals.icons.tags,
|
||||
to: "/admin/toolbox/tags",
|
||||
title: this.$t("sidebar.categories"),
|
||||
},
|
||||
{
|
||||
icon: this.$globals.icons.broom,
|
||||
to: "/admin/toolbox/organize",
|
||||
title: this.$t("settings.organize"),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: this.$globals.icons.group,
|
||||
to: "/admin/manage-users",
|
||||
title: this.$t("sidebar.manage-users"),
|
||||
children: [
|
||||
{
|
||||
icon: this.$globals.icons.user,
|
||||
to: "/admin/manage-users/all-users",
|
||||
title: this.$t("user.users"),
|
||||
},
|
||||
{
|
||||
icon: this.$globals.icons.group,
|
||||
to: "/admin/manage-users/all-groups",
|
||||
title: this.$t("group.groups"),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
icon: this.$globals.icons.import,
|
||||
to: "/admin/migrations",
|
||||
title: this.$t("sidebar.migrations"),
|
||||
},
|
||||
{
|
||||
icon: this.$globals.icons.database,
|
||||
to: "/admin/backups",
|
||||
title: this.$t("sidebar.backups"),
|
||||
},
|
||||
],
|
||||
bottomLinks: [
|
||||
{
|
||||
icon: this.$globals.icons.heart,
|
||||
title: this.$t("about.support"),
|
||||
href: "https://github.com/sponsors/hay-kot",
|
||||
},
|
||||
{
|
||||
icon: this.$globals.icons.information,
|
||||
title: this.$t("about.about"),
|
||||
to: "/admin/about",
|
||||
},
|
||||
],
|
||||
sidebar,
|
||||
topLinks,
|
||||
bottomLinks,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>+
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<v-app dark>
|
||||
<!-- <TheSnackbar /> -->
|
||||
<TheSnackbar />
|
||||
|
||||
<AppHeader :menu="false"> </AppHeader>
|
||||
<v-main>
|
||||
|
@ -17,9 +17,10 @@
|
|||
import { defineComponent } from "@nuxtjs/composition-api";
|
||||
import AppFooter from "@/components/Layout/AppFooter.vue";
|
||||
import AppHeader from "@/components/Layout/AppHeader.vue";
|
||||
import TheSnackbar from "~/components/Layout/TheSnackbar.vue";
|
||||
|
||||
export default defineComponent({
|
||||
components: { AppHeader, AppFooter },
|
||||
components: { AppHeader, AppFooter, TheSnackbar },
|
||||
setup() {
|
||||
return {};
|
||||
},
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
:top-link="topLinks"
|
||||
secondary-header="Cookbooks"
|
||||
:secondary-links="cookbookLinks || []"
|
||||
:bottom-links="$auth.user.admin ? bottomLink : []"
|
||||
:bottom-links="isAdmin ? bottomLink : []"
|
||||
@input="sidebar = !sidebar"
|
||||
/>
|
||||
|
||||
|
@ -37,11 +37,13 @@ import { useCookbooks } from "~/composables/use-group-cookbooks";
|
|||
export default defineComponent({
|
||||
components: { AppHeader, AppSidebar, AppFloatingButton },
|
||||
// @ts-ignore
|
||||
// middleware: process.env.GLOBAL_MIDDLEWARE,
|
||||
middleware: "auth",
|
||||
setup() {
|
||||
const { cookbooks } = useCookbooks();
|
||||
// @ts-ignore
|
||||
const { $globals } = useContext();
|
||||
const { $globals, $auth } = useContext();
|
||||
|
||||
const isAdmin = computed(() => $auth.user?.admin);
|
||||
|
||||
const cookbookLinks = computed(() => {
|
||||
if (!cookbooks.value) return [];
|
||||
|
@ -53,7 +55,7 @@ export default defineComponent({
|
|||
};
|
||||
});
|
||||
});
|
||||
return { cookbookLinks };
|
||||
return { cookbookLinks, isAdmin };
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
|
@ -56,7 +56,7 @@ export default {
|
|||
// https://go.nuxtjs.dev/pwa
|
||||
"@nuxtjs/pwa",
|
||||
// https://i18n.nuxtjs.org/setup
|
||||
"nuxt-i18n",
|
||||
"@nuxtjs/i18n",
|
||||
// https://auth.nuxtjs.org/guide/setup
|
||||
"@nuxtjs/auth-next",
|
||||
// https://github.com/nuxt-community/proxy-module
|
||||
|
@ -81,8 +81,8 @@ export default {
|
|||
|
||||
auth: {
|
||||
redirect: {
|
||||
login: "/user/login",
|
||||
logout: "/",
|
||||
login: "/login",
|
||||
logout: "/login",
|
||||
callback: "/login",
|
||||
home: "/",
|
||||
},
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
"@mdi/js": "^5.9.55",
|
||||
"@nuxtjs/auth-next": "5.0.0-1624817847.21691f1",
|
||||
"@nuxtjs/axios": "^5.13.6",
|
||||
"@nuxtjs/i18n": "^7.0.3",
|
||||
"@nuxtjs/proxy": "^2.1.0",
|
||||
"@nuxtjs/pwa": "^3.3.5",
|
||||
"@vue/composition-api": "^1.0.5",
|
||||
|
@ -25,7 +26,6 @@
|
|||
"core-js": "^3.15.1",
|
||||
"fuse.js": "^6.4.6",
|
||||
"nuxt": "^2.15.7",
|
||||
"nuxt-i18n": "^6.28.0",
|
||||
"vuedraggable": "^2.24.3",
|
||||
"vuetify": "^2.5.5"
|
||||
},
|
||||
|
@ -33,7 +33,7 @@
|
|||
"@babel/eslint-parser": "^7.14.7",
|
||||
"@nuxt/types": "^2.15.7",
|
||||
"@nuxt/typescript-build": "^2.1.0",
|
||||
"@nuxtjs/composition-api": "^0.26.0",
|
||||
"@nuxtjs/composition-api": "^0.28.0",
|
||||
"@nuxtjs/eslint-config-typescript": "^6.0.1",
|
||||
"@nuxtjs/eslint-module": "^3.0.2",
|
||||
"@nuxtjs/vuetify": "^1.12.1",
|
||||
|
@ -50,4 +50,4 @@
|
|||
"resolutions": {
|
||||
"vite": "2.3.8"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -95,8 +95,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import AdminBackupImportOptions from "@/components/Domain/Admin/AdminBackupImportOptions.vue";
|
||||
import { defineComponent, reactive, toRefs, useContext, ref } from "@nuxtjs/composition-api";
|
||||
import AdminBackupImportOptions from "@/components/Domain/Admin/AdminBackupImportOptions.vue";
|
||||
import { useBackups } from "~/composables/use-backups";
|
||||
|
||||
export default defineComponent({
|
||||
|
|
|
@ -23,8 +23,8 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import RecipeCardSection from "@/components/Domain/Recipe/RecipeCardSection.vue";
|
||||
import { defineComponent, useRoute, ref } from "@nuxtjs/composition-api";
|
||||
import RecipeCardSection from "@/components/Domain/Recipe/RecipeCardSection.vue";
|
||||
import { useCookbook } from "~/composables/use-group-cookbooks";
|
||||
export default defineComponent({
|
||||
components: { RecipeCardSection },
|
||||
|
|
|
@ -178,10 +178,9 @@
|
|||
</v-btn>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-btn v-if="$config.ALLOW_SIGNUP" class="mx-auto" text to="/user/sign-up"> Sign Up </v-btn>
|
||||
<v-btn v-if="$config.ALLOW_SIGNUP" class="mx-auto" text to="/register"> Register </v-btn>
|
||||
<v-btn v-else class="mx-auto" text disabled> Invite Only </v-btn>
|
||||
</v-card>
|
||||
<!-- <v-col class="fill-height"> </v-col> -->
|
||||
</v-container>
|
||||
</template>
|
||||
|
170
frontend/pages/register.vue
Normal file
170
frontend/pages/register.vue
Normal file
|
@ -0,0 +1,170 @@
|
|||
<template>
|
||||
<v-container fill-height fluid class="d-flex justify-center align-start narrow-container">
|
||||
<v-card color="background d-flex flex-column align-center" flat width="700px">
|
||||
<v-card-title class="headline"> User Registration </v-card-title>
|
||||
<v-card-text>
|
||||
<v-form ref="domRegisterForm" @submit.prevent="register()">
|
||||
<ToggleState>
|
||||
<template #activator="{ toggle }">
|
||||
<div class="d-flex justify-center my-2">
|
||||
<v-btn-toggle tile mandatory group color="primary">
|
||||
<v-btn small @click="toggle(false)"> Create a Group </v-btn>
|
||||
<v-btn small @click="toggle(true)"> Join a Group </v-btn>
|
||||
</v-btn-toggle>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="{ state }">
|
||||
<v-text-field
|
||||
v-if="!state"
|
||||
v-model="form.group"
|
||||
filled
|
||||
rounded
|
||||
autofocus
|
||||
validate-on-blur
|
||||
class="rounded-lg"
|
||||
:prepend-icon="$globals.icons.group"
|
||||
:rules="[tokenOrGroup]"
|
||||
label="New Group Name"
|
||||
/>
|
||||
<v-text-field
|
||||
v-else
|
||||
v-model="form.groupToken"
|
||||
filled
|
||||
rounded
|
||||
validate-on-blur
|
||||
:rules="[tokenOrGroup]"
|
||||
class="rounded-lg"
|
||||
:prepend-icon="$globals.icons.group"
|
||||
label="Group Token"
|
||||
/>
|
||||
</template>
|
||||
</ToggleState>
|
||||
<v-text-field
|
||||
v-model="form.email"
|
||||
filled
|
||||
rounded
|
||||
class="rounded-lg"
|
||||
validate-on-blur
|
||||
:prepend-icon="$globals.icons.email"
|
||||
label="Email"
|
||||
:rules="[validators.required, validators.email]"
|
||||
/>
|
||||
<v-text-field
|
||||
v-model="form.username"
|
||||
filled
|
||||
rounded
|
||||
class="rounded-lg"
|
||||
:prepend-icon="$globals.icons.user"
|
||||
label="Username"
|
||||
:rules="[validators.required]"
|
||||
/>
|
||||
<v-text-field
|
||||
v-model="form.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="form.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]"
|
||||
/>
|
||||
<div class="mt-n4 px-8">
|
||||
<v-checkbox v-model="form.private" label="Keep My Recipes Private"></v-checkbox>
|
||||
<p class="text-caption mt-n4">
|
||||
Sets your group and all recipes defaults to private. You can always change this later.
|
||||
</p>
|
||||
<v-checkbox v-model="form.advanced" label="Enable Advanced Content"></v-checkbox>
|
||||
<p class="text-caption mt-n4">
|
||||
Enables advanced features like Recipe Scaling, API keys, Webhooks, and Data Management. Don't worry, you
|
||||
can always change this later
|
||||
</p>
|
||||
</div>
|
||||
<div class="d-flex flex-column justify-center">
|
||||
<v-btn :loading="loggingIn" color="primary" type="submit" large rounded class="rounded-xl" block>
|
||||
Register
|
||||
</v-btn>
|
||||
<v-btn class="mx-auto my-2" text to="/login"> Login </v-btn>
|
||||
</div>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, reactive, toRefs, ref, useRouter } from "@nuxtjs/composition-api";
|
||||
import { validators } from "@/composables/use-validators";
|
||||
import { useApiSingleton } from "~/composables/use-api";
|
||||
import { alert } from "~/composables/use-toast";
|
||||
|
||||
export default defineComponent({
|
||||
layout: "basic",
|
||||
setup() {
|
||||
const api = useApiSingleton();
|
||||
const state = reactive({
|
||||
loggingIn: false,
|
||||
success: false,
|
||||
});
|
||||
const allowSignup = computed(() => process.env.AllOW_SIGNUP);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
// @ts-ignore
|
||||
const domRegisterForm = ref<VForm>(null);
|
||||
|
||||
const form = reactive({
|
||||
group: "",
|
||||
groupToken: "",
|
||||
email: "",
|
||||
username: "",
|
||||
password: "",
|
||||
passwordConfirm: "",
|
||||
advanced: false,
|
||||
private: false,
|
||||
});
|
||||
|
||||
const passwordMatch = () => form.password === form.passwordConfirm || "Passwords do not match";
|
||||
const tokenOrGroup = () => form.group !== "" || form.groupToken !== "" || "Group name or token must be given";
|
||||
|
||||
async function register() {
|
||||
if (!domRegisterForm.value?.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { data, response } = await api.register.register(form);
|
||||
|
||||
if (response?.status === 201) {
|
||||
state.success = true;
|
||||
alert.success("Registration Success");
|
||||
router.push("/user/login");
|
||||
}
|
||||
|
||||
console.log(data, response);
|
||||
}
|
||||
|
||||
return {
|
||||
domRegisterForm,
|
||||
validators,
|
||||
allowSignup,
|
||||
form,
|
||||
...toRefs(state),
|
||||
passwordMatch,
|
||||
tokenOrGroup,
|
||||
register,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -50,8 +50,8 @@
|
|||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api";
|
||||
import { useCookbooks } from "@/composables/use-group-cookbooks";
|
||||
import draggable from "vuedraggable";
|
||||
import { useCookbooks } from "@/composables/use-group-cookbooks";
|
||||
|
||||
export default defineComponent({
|
||||
components: { draggable },
|
||||
|
|
|
@ -1,39 +1,139 @@
|
|||
<template>
|
||||
<v-container>
|
||||
<BasePageTitle divider>
|
||||
<v-container class="narrow-container">
|
||||
<BasePageTitle class="mb-5">
|
||||
<template #header>
|
||||
<v-img max-height="100" max-width="100" :src="require('~/static/svgs/manage-group-settings.svg')"></v-img>
|
||||
</template>
|
||||
<template #title> Group Settings </template>
|
||||
These items are shared within your group. Editing one of them will change it for the whole group!
|
||||
</BasePageTitle>
|
||||
<v-card tag="section" outlined>
|
||||
<v-card-text>
|
||||
<BaseCardSectionTitle title="Mealplan Categories">
|
||||
Set the categories below for the ones that you want to be included in your mealplan random generation.
|
||||
<div class="mt-2">
|
||||
<BaseButton save @click="actions.updateAll()" />
|
||||
</div>
|
||||
</BaseCardSectionTitle>
|
||||
<DomainRecipeCategoryTagSelector v-if="categories" v-model="categories" />
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
<section>
|
||||
<BaseCardSectionTitle title="Mealplan Categories">
|
||||
Set the categories below for the ones that you want to be included in your mealplan random generation.
|
||||
</BaseCardSectionTitle>
|
||||
<DomainRecipeCategoryTagSelector v-if="categories" v-model="categories" />
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<BaseButton save @click="actions.updateAll()" />
|
||||
</v-card-actions>
|
||||
</section>
|
||||
|
||||
<section v-if="group">
|
||||
<BaseCardSectionTitle class="mt-10" title="Group Preferences"></BaseCardSectionTitle>
|
||||
<v-checkbox
|
||||
v-model="group.preferences.privateGroup"
|
||||
class="mt-n4"
|
||||
label="Private Group"
|
||||
@change="groupActions.updatePreferences()"
|
||||
></v-checkbox>
|
||||
<v-select
|
||||
v-model="group.preferences.firstDayOfWeek"
|
||||
:prepend-icon="$globals.icons.calendarWeekBegin"
|
||||
:items="allDays"
|
||||
item-text="name"
|
||||
item-value="value"
|
||||
:label="$t('settings.first-day-of-week')"
|
||||
@change="groupActions.updatePreferences()"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section v-if="group">
|
||||
<BaseCardSectionTitle class="mt-10" title="Default Recipe Preferences">
|
||||
These are the default settings when a new recipe is created in your group. These can be changed for indivdual
|
||||
recipes in the recipe settings menu.
|
||||
</BaseCardSectionTitle>
|
||||
|
||||
<v-checkbox
|
||||
v-model="group.preferences.recipePublic"
|
||||
class="mt-n4"
|
||||
label="Allow users outside of your group to see your recipes"
|
||||
@change="groupActions.updatePreferences()"
|
||||
></v-checkbox>
|
||||
<v-checkbox
|
||||
v-model="group.preferences.recipeShowNutrition"
|
||||
class="mt-n4"
|
||||
label="Show nutrition information"
|
||||
@change="groupActions.updatePreferences()"
|
||||
></v-checkbox>
|
||||
<v-checkbox
|
||||
v-model="group.preferences.recipeShowAssets"
|
||||
class="mt-n4"
|
||||
label="Show recipe assets"
|
||||
@change="groupActions.updatePreferences()"
|
||||
></v-checkbox>
|
||||
<v-checkbox
|
||||
v-model="group.preferences.recipeLandscapeView"
|
||||
class="mt-n4"
|
||||
label="Default to landscape view"
|
||||
@change="groupActions.updatePreferences()"
|
||||
></v-checkbox>
|
||||
<v-checkbox
|
||||
v-model="group.preferences.recipeDisableComments"
|
||||
class="mt-n4"
|
||||
label="Allow recipe comments from users in your group"
|
||||
@change="groupActions.updatePreferences()"
|
||||
></v-checkbox>
|
||||
<v-checkbox
|
||||
v-model="group.preferences.recipeDisableAmount"
|
||||
class="mt-n4"
|
||||
label="Enable organizing recipe ingredients by units and food"
|
||||
@change="groupActions.updatePreferences()"
|
||||
></v-checkbox>
|
||||
</section>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api";
|
||||
import { useGroup } from "~/composables/use-groups";
|
||||
import { defineComponent, useContext } from "@nuxtjs/composition-api";
|
||||
import { useGroupCategories, useGroupSelf } from "~/composables/use-groups";
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const { categories, actions } = useGroup();
|
||||
const { categories, actions } = useGroupCategories();
|
||||
const { group, actions: groupActions } = useGroupSelf();
|
||||
|
||||
const { i18n } = useContext();
|
||||
|
||||
const allDays = [
|
||||
{
|
||||
name: i18n.t("general.sunday"),
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
name: i18n.t("general.monday"),
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
name: i18n.t("general.tuesday"),
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
name: i18n.t("general.wednesday"),
|
||||
value: 3,
|
||||
},
|
||||
{
|
||||
name: i18n.t("general.thursday"),
|
||||
value: 4,
|
||||
},
|
||||
{
|
||||
name: i18n.t("general.friday"),
|
||||
value: 5,
|
||||
},
|
||||
{
|
||||
name: i18n.t("general.saturday"),
|
||||
value: 6,
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
categories,
|
||||
actions,
|
||||
group,
|
||||
groupActions,
|
||||
allDays,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
|
|
@ -82,6 +82,18 @@
|
|||
</template>
|
||||
</ToggleState>
|
||||
</section>
|
||||
<section>
|
||||
<BaseCardSectionTitle class="mt-10" title="Preferences"> </BaseCardSectionTitle>
|
||||
<v-checkbox
|
||||
v-model="userCopy.advanced"
|
||||
class="mt-n4"
|
||||
label="Show advanced features (API Keys, Webhooks, and Data Management)"
|
||||
@change="updateUser"
|
||||
></v-checkbox>
|
||||
<div class="d-flex justify-center mt-5">
|
||||
<v-btn outlined class="rounded-xl" to="/user/group"> Looking for Privacy Settings? </v-btn>
|
||||
</div>
|
||||
</section>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
</v-col>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<UserProfileLinkCard
|
||||
v-if="user.advanced"
|
||||
:link="{ text: 'Manage Your API Tokens', to: '/user/profile/api-tokens' }"
|
||||
:image="require('~/static/svgs/manage-api-tokens.svg')"
|
||||
>
|
||||
|
@ -63,6 +64,7 @@
|
|||
</v-col>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<UserProfileLinkCard
|
||||
v-if="user.advanced"
|
||||
:link="{ text: 'Manage Webhooks', to: '/user/group/webhooks' }"
|
||||
:image="require('~/static/svgs/manage-webhooks.svg')"
|
||||
>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
"~/*": ["./*"],
|
||||
"@/*": ["./*"]
|
||||
},
|
||||
"types": ["@nuxt/types", "@nuxtjs/axios", "@types/node", "nuxt-i18n", "@nuxtjs/auth-next"]
|
||||
"types": ["@nuxt/types", "@nuxtjs/axios", "@types/node", "@nuxtjs/i18n", "@nuxtjs/auth-next"]
|
||||
},
|
||||
"exclude": ["node_modules", ".nuxt", "dist"]
|
||||
}
|
||||
|
|
17
frontend/types/vue.d.ts
vendored
17
frontend/types/vue.d.ts
vendored
|
@ -1,14 +1,17 @@
|
|||
import Vue from "vue";
|
||||
import "@nuxt/types";
|
||||
|
||||
declare module "vue/types/vue" {
|
||||
|
||||
interface Vue {
|
||||
$globals: any;
|
||||
}
|
||||
interface Vue {
|
||||
$globals: any;
|
||||
}
|
||||
}
|
||||
|
||||
declare module "vue/types/options" {
|
||||
interface ComponentOptions<V extends Vue> {
|
||||
$globals?: any;
|
||||
}
|
||||
interface ComponentOptions<V extends Vue> {
|
||||
$globals?: any;
|
||||
}
|
||||
interface ComponentOptions<V extends UseContextReturn> {
|
||||
$globals?: any;
|
||||
}
|
||||
}
|
||||
|
|
1267
frontend/yarn.lock
1267
frontend/yarn.lock
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue