From 0b5dcf6ece649788a93a1305aaf7e8f64a5a6cf6 Mon Sep 17 00:00:00 2001 From: Michael Genson <71845777+michael-genson@users.noreply.github.com> Date: Mon, 19 Feb 2024 19:45:44 +0000 Subject: [PATCH 01/20] extract user registration form into a composable --- .../Domain/User/UserRegistrationForm.vue | 153 ++++++++++++++++++ .../use-users/user-registration-form.ts | 84 ++++++++++ frontend/lib/api/user/user-registration.ts | 2 - frontend/pages/register/index.vue | 142 ++++------------ 4 files changed, 269 insertions(+), 112 deletions(-) create mode 100644 frontend/components/Domain/User/UserRegistrationForm.vue create mode 100644 frontend/composables/use-users/user-registration-form.ts diff --git a/frontend/components/Domain/User/UserRegistrationForm.vue b/frontend/components/Domain/User/UserRegistrationForm.vue new file mode 100644 index 00000000000..b0ecfd9a75b --- /dev/null +++ b/frontend/components/Domain/User/UserRegistrationForm.vue @@ -0,0 +1,153 @@ + + + + + diff --git a/frontend/composables/use-users/user-registration-form.ts b/frontend/composables/use-users/user-registration-form.ts new file mode 100644 index 00000000000..dfa644b821f --- /dev/null +++ b/frontend/composables/use-users/user-registration-form.ts @@ -0,0 +1,84 @@ +import { ref, Ref, useContext } from "@nuxtjs/composition-api"; +import { useAsyncValidator } from "~/composables/use-validators"; +import { VForm } from "~/types/vuetify"; +import { usePublicApi } from "~/composables/api/api-client"; + +const domAccountForm = ref(null); +const username = ref(""); +const email = ref(""); +const password1 = ref(""); +const password2 = ref(""); +const advancedOptions = ref(false); + +export const useUserRegistrationForm = () => { + const { i18n } = useContext(); + function safeValidate(form: Ref) { + if (form.value && form.value.validate) { + return form.value.validate(); + } + return false; + } + // ================================================================ + // Provide Group Details + const publicApi = usePublicApi(); + // ================================================================ + // Provide Account Details + + const usernameErrorMessages = ref([]); + const { validate: validateUsername, valid: validUsername } = useAsyncValidator( + username, + (v: string) => publicApi.validators.username(v), + i18n.tc("validation.username-is-taken"), + usernameErrorMessages + ); + const emailErrorMessages = ref([]); + const { validate: validateEmail, valid: validEmail } = useAsyncValidator( + email, + (v: string) => publicApi.validators.email(v), + i18n.tc("validation.email-is-taken"), + emailErrorMessages + ); + const accountDetails = { + username, + email, + advancedOptions, + validate: async () => { + if (!(validUsername.value && validEmail.value)) { + await Promise.all([validateUsername(), validateEmail()]); + } + + return (safeValidate(domAccountForm as Ref) && validUsername.value && validEmail.value); + }, + reset: () => { + accountDetails.username.value = ""; + accountDetails.email.value = ""; + accountDetails.advancedOptions.value = false; + }, + }; + // ================================================================ + // Provide Credentials + const passwordMatch = () => password1.value === password2.value || i18n.tc("user.password-must-match"); + const credentials = { + password1, + password2, + passwordMatch, + reset: () => { + credentials.password1.value = ""; + credentials.password2.value = ""; + } + }; + + return { + accountDetails, + credentials, + emailErrorMessages, + usernameErrorMessages, + // Fields + advancedOptions, + // Validators + validateUsername, + validateEmail, + // Dom Refs + domAccountForm, + }; +}; diff --git a/frontend/lib/api/user/user-registration.ts b/frontend/lib/api/user/user-registration.ts index ea722c5fbc5..e1921d4cf3e 100644 --- a/frontend/lib/api/user/user-registration.ts +++ b/frontend/lib/api/user/user-registration.ts @@ -8,8 +8,6 @@ const routes = { }; export class RegisterAPI extends BaseAPI { - /** Returns a list of available .zip files for import into Mealie. - */ async register(payload: CreateUserRegistration) { return await this.requests.post(routes.register, payload); } diff --git a/frontend/pages/register/index.vue b/frontend/pages/register/index.vue index 1cd1f74d88a..fd3ce361669 100644 --- a/frontend/pages/register/index.vue +++ b/frontend/pages/register/index.vue @@ -4,7 +4,7 @@ fluid class="d-flex justify-center align-center" :class="{ - 'bg-off-white': !$vuetify.theme.dark && !isDark.value, + 'bg-off-white': !$vuetify.theme.dark && !isDark, }" > @@ -136,73 +136,14 @@ diff --git a/frontend/pages/login.vue b/frontend/pages/login.vue index 2e6bfcec6b2..1d9a17f6169 100644 --- a/frontend/pages/login.vue +++ b/frontend/pages/login.vue @@ -132,14 +132,7 @@ export default defineComponent({ const { $auth, i18n, $axios } = useContext(); const { loggedIn } = useLoggedInState(); const groupSlug = computed(() => $auth.user?.groupSlug); - - whenever( - () => loggedIn.value && groupSlug.value, - () => { - router.push(`/g/${groupSlug.value || ""}`); - }, - { immediate: true }, - ); + const isFirstLogin = ref(false); const form = reactive({ email: "", @@ -147,12 +140,22 @@ export default defineComponent({ remember: false, }); - const isFirstLogin = ref(false) - useAsync(async () => { const data = await $axios.get("/api/app/about/startup-info"); isFirstLogin.value = data.data.isFirstLogin; - }, useAsyncKey()); + }, useAsyncKey()); + + whenever( + () => loggedIn.value && groupSlug.value, + () => { + if (isFirstLogin.value && $auth.user?.admin) { + router.push("/admin/setup"); + } else { + router.push(`/g/${groupSlug.value || ""}`); + } + }, + { immediate: true }, + ); const loggingIn = ref(false); From cf23b79710811765a5c89a2c5459bae67f69ee28 Mon Sep 17 00:00:00 2001 From: Michael Genson <71845777+michael-genson@users.noreply.github.com> Date: Mon, 19 Feb 2024 20:28:03 +0000 Subject: [PATCH 04/20] removed unused attrs --- frontend/pages/admin/setup.vue | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/frontend/pages/admin/setup.vue b/frontend/pages/admin/setup.vue index 9eebbdd4083..7686aa7fe15 100644 --- a/frontend/pages/admin/setup.vue +++ b/frontend/pages/admin/setup.vue @@ -61,7 +61,7 @@ export default defineComponent({ components: { UserRegistrationForm }, layout: "blank", setup() { - const { $auth, $globals, $vuetify, i18n } = useContext(); + const { $auth, $globals, i18n } = useContext(); const { accountDetails } = useUserRegistrationForm(); const groupSlug = computed(() => $auth.user?.groupSlug); @@ -72,14 +72,6 @@ export default defineComponent({ router.push("/login"); } - const attrs = computed(() => { - return $vuetify.breakpoint.smAndDown ? { - maxWidth: undefined, - } : { - maxWidth: "800", - } - }) - type Config = { nextButtonText: string | undefined; nextButtonIcon: string | undefined; @@ -159,7 +151,6 @@ export default defineComponent({ } return { - attrs, Pages, currentPage, totalPages, From 8ea0a97d2f66091337b556d2a3da18b431bbb9fa Mon Sep 17 00:00:00 2001 From: Michael Genson <71845777+michael-genson@users.noreply.github.com> Date: Mon, 19 Feb 2024 20:31:32 +0000 Subject: [PATCH 05/20] added setup bypass --- frontend/pages/admin/setup.vue | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/pages/admin/setup.vue b/frontend/pages/admin/setup.vue index 7686aa7fe15..6af8831ba0a 100644 --- a/frontend/pages/admin/setup.vue +++ b/frontend/pages/admin/setup.vue @@ -27,11 +27,13 @@ {{ $i18n.tc('admin.welcome-to-mealie-get-started') }} {{ $i18n.tc('admin.already-set-up-bring-to-homepage') }} @@ -70,6 +72,8 @@ export default defineComponent({ if (!$auth.loggedIn) { router.push("/login"); + } else if (!$auth.user?.admin) { + router.push(groupSlug.value ? `/g/${groupSlug.value}` : "/login"); } type Config = { @@ -144,7 +148,7 @@ export default defineComponent({ currentPage.value += 1; break; case Pages.END: - router.push(groupSlug.value ? `/g/${groupSlug.value || ""}` : "/login"); + router.push(groupSlug.value ? `/g/${groupSlug.value}` : "/login"); break; } isSubmitting.value = false; @@ -157,6 +161,7 @@ export default defineComponent({ activeConfig, isSubmitting, handleSubmit, + groupSlug, } }, From da100d1e59ec08a9099145fbab4971ff75fe471c Mon Sep 17 00:00:00 2001 From: Michael Genson <71845777+michael-genson@users.noreply.github.com> Date: Mon, 19 Feb 2024 20:35:15 +0000 Subject: [PATCH 06/20] made setup page more readable --- frontend/pages/admin/setup.vue | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frontend/pages/admin/setup.vue b/frontend/pages/admin/setup.vue index 6af8831ba0a..9cafdb4265f 100644 --- a/frontend/pages/admin/setup.vue +++ b/frontend/pages/admin/setup.vue @@ -63,6 +63,8 @@ export default defineComponent({ components: { UserRegistrationForm }, layout: "blank", setup() { + // ================================================================ + // Setup const { $auth, $globals, i18n } = useContext(); const { accountDetails } = useUserRegistrationForm(); @@ -94,6 +96,8 @@ export default defineComponent({ END = 4, } + // ================================================================ + // Page Navigation const currentPage = ref(0); const activeConfig = computed(() => { const config: Config = { @@ -131,6 +135,8 @@ export default defineComponent({ return config; }) + // ================================================================ + // Page Submission async function handleSubmit(page: number) { if (isSubmitting.value) { return; @@ -165,7 +171,7 @@ export default defineComponent({ } }, - header() { + head() { return { title: this.$i18n.tc("admin.first-time-setup"), }; From 3698b8342d37cd5ce66a8b10a4a5a2b402ad4576 Mon Sep 17 00:00:00 2001 From: Michael Genson <71845777+michael-genson@users.noreply.github.com> Date: Mon, 19 Feb 2024 23:06:27 +0000 Subject: [PATCH 07/20] add checkbox hints to autoform --- frontend/components/global/AutoForm.vue | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/frontend/components/global/AutoForm.vue b/frontend/components/global/AutoForm.vue index f92acf2e975..2f143f52ea6 100644 --- a/frontend/components/global/AutoForm.vue +++ b/frontend/components/global/AutoForm.vue @@ -15,12 +15,22 @@ v-if="inputField.type === fieldTypes.BOOLEAN" v-model="value[inputField.varName]" class="my-0 py-0" - :label="inputField.label" :name="inputField.varName" - :hint="inputField.hint || ''" :disabled="(inputField.disableUpdate && updateMode) || (!updateMode && inputField.disableCreate) || (disabledFields && disabledFields.includes(inputField.varName))" @change="emitBlur" - /> + > + + + Date: Mon, 19 Feb 2024 23:07:03 +0000 Subject: [PATCH 08/20] added common settings pages and initial submit logic --- .../use-setup/common-settings-form.ts | 30 +++ frontend/composables/use-setup/index.ts | 1 + frontend/composables/use-users/index.ts | 1 + frontend/lang/messages/en-US.json | 6 +- frontend/pages/admin/setup.vue | 193 +++++++++++++++++- frontend/pages/register/index.vue | 2 +- 6 files changed, 226 insertions(+), 7 deletions(-) create mode 100644 frontend/composables/use-setup/common-settings-form.ts create mode 100644 frontend/composables/use-setup/index.ts diff --git a/frontend/composables/use-setup/common-settings-form.ts b/frontend/composables/use-setup/common-settings-form.ts new file mode 100644 index 00000000000..12b3a7496e5 --- /dev/null +++ b/frontend/composables/use-setup/common-settings-form.ts @@ -0,0 +1,30 @@ +import { useContext } from "@nuxtjs/composition-api"; +import { fieldTypes } from "../forms"; +import { AutoFormItems } from "~/types/auto-forms"; + +export const useCommonSettingsForm = () => { + const { i18n } = useContext(); + + const commonSettingsForm: AutoFormItems = [ + { + section: i18n.tc("profile.group-settings"), + label: i18n.tc("group.enable-public-access"), + hint: i18n.tc("group.enable-public-access-description"), + varName: "makeGroupRecipesPublic", + type: fieldTypes.BOOLEAN, + rules: ["required"], + }, + { + section: i18n.tc("data-pages.data-management"), + label: i18n.tc("user-registration.use-seed-data"), + hint: i18n.tc("user-registration.use-seed-data-description"), + varName: "useSeedData", + type: fieldTypes.BOOLEAN, + rules: ["required"], + }, + ]; + + return { + commonSettingsForm, + } +} diff --git a/frontend/composables/use-setup/index.ts b/frontend/composables/use-setup/index.ts new file mode 100644 index 00000000000..13297ef4b8a --- /dev/null +++ b/frontend/composables/use-setup/index.ts @@ -0,0 +1 @@ +export { useCommonSettingsForm } from './common-settings-form'; diff --git a/frontend/composables/use-users/index.ts b/frontend/composables/use-users/index.ts index adacee24eb8..f1b1a6a8571 100644 --- a/frontend/composables/use-users/index.ts +++ b/frontend/composables/use-users/index.ts @@ -1 +1,2 @@ export { useUserForm } from "./user-form"; +export { useUserRegistrationForm } from "./user-registration-form"; diff --git a/frontend/lang/messages/en-US.json b/frontend/lang/messages/en-US.json index da10d5e3efc..6e6e0f1b7e7 100644 --- a/frontend/lang/messages/en-US.json +++ b/frontend/lang/messages/en-US.json @@ -84,6 +84,7 @@ "clear": "Clear", "close": "Close", "confirm": "Confirm", + "confirm-how-does-everything-look": "How does everything look?", "confirm-delete-generic": "Are you sure you want to delete this?", "copied_message": "Copied!", "create": "Create", @@ -237,6 +238,8 @@ "group-preferences": "Group Preferences", "private-group": "Private Group", "private-group-description": "Setting your group to private will default all public view options to default. This overrides an individual recipes public view settings.", + "enable-public-access": "Enable Public Access", + "enable-public-access-description": "Make group recipes public by default, and allow visitors to view recipes without logging-in", "allow-users-outside-of-your-group-to-see-your-recipes": "Allow users outside of your group to see your recipes", "allow-users-outside-of-your-group-to-see-your-recipes-description": "When enabled you can use a public share link to share specific recipes without authorizing the user. When disabled, you can only share recipes with users who are in your group or with a pre-generated private link", "show-nutrition-information": "Show nutrition information", @@ -1133,7 +1136,8 @@ "tasks": "Tasks", "first-time-setup": "First Time Setup", "welcome-to-mealie-get-started": "Welcome to Mealie! Let's get started", - "already-set-up-bring-to-homepage": "I'm already set up, just bring me to the homepage" + "already-set-up-bring-to-homepage": "I'm already set up, just bring me to the homepage", + "common-settings-for-new-sites": "Here are some common settings for new sites" }, "profile": { "welcome-user": "👋 Welcome, {0}", diff --git a/frontend/pages/admin/setup.vue b/frontend/pages/admin/setup.vue index 9cafdb4265f..9bc62595722 100644 --- a/frontend/pages/admin/setup.vue +++ b/frontend/pages/admin/setup.vue @@ -42,10 +42,26 @@ - Page 2 + + {{ $i18n.tc('admin.common-settings-for-new-sites') }} + + - Confirm Page + + {{ $t("general.confirm-how-does-everything-look") }} + + + + End Page @@ -56,7 +72,11 @@