1
0
Fork 0
mirror of https://github.com/mealie-recipes/mealie.git synced 2025-08-05 21:45:25 +02:00

feat: Remove Explore URLs and make the normal URLs public (#2632)

* add groupSlug to most routes

* fixed more routing issues

* fixed jank and incorrect routes

* remove public explore links

* remove unused groupSlug and explore routes

* nuked explore pages

* fixed public toolstore bug

* fixed various routes missing group slug

* restored public app header menu

* fix janky login redirect

* 404 recipe API call returns to login

* removed unused explore layout

* force redirect when using the wrong group slug

* fixed dead admin links

* removed unused middleware from earlier attempt

* 🧹

* improve cookbooks sidebar
fixed sidebar link not working
fixed sidebar link target
hide cookbooks header when there are none

* added group slug to user

* fix $auth typehints

* vastly simplified groupSlug logic

* allow logged-in users to view other groups

* fixed some edgecases that bypassed isOwnGroup

* fixed static home ref

* 🧹

* fixed redirect logic

* lint warning

* removed group slug from group and user pages
refactored all components to use route groupSlug or user group slug
moved some group pages to recipe pages

* fixed some bad types

* 🧹

* moved groupSlug routes under /g/groupSlug

* move /recipe/ to /r/

* fix backend url generation and metadata injection

* moved shopping lists to root/other route fixes

* changed shared from /recipes/ to /r/

* fixed 404 redirect not awaiting

* removed unused import

* fix doc links

* fix public recipe setting not affecting public API

* fixed backend tests

* fix nuxt-generate command

---------

Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
This commit is contained in:
Michael Genson 2023-11-05 19:07:02 -06:00 committed by GitHub
parent 94cf690e8f
commit 80968b02bb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
87 changed files with 555 additions and 501 deletions

View file

@ -105,13 +105,13 @@
</section>
</section>
<v-container class="mt-4 d-flex justify-center text-center">
<nuxt-link to="/group/migrations"> {{ $t('recipe.looking-for-migrations') }} </nuxt-link>
<nuxt-link :to="`/group/migrations`"> {{ $t('recipe.looking-for-migrations') }} </nuxt-link>
</v-container>
</v-container>
</template>
<script lang="ts">
import { defineComponent, reactive, ref, toRefs, useContext, onMounted } from "@nuxtjs/composition-api";
import { computed, defineComponent, reactive, ref, toRefs, useContext, onMounted, useRoute } from "@nuxtjs/composition-api";
import { useAdminApi } from "~/composables/api";
import { AllBackups } from "~/lib/api/types/admin";
@ -119,6 +119,8 @@ export default defineComponent({
layout: "admin",
setup() {
const { i18n, $auth } = useContext();
const route = useRoute();
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
const adminApi = useAdminApi();
const selected = ref("");
@ -192,6 +194,7 @@ export default defineComponent({
onMounted(refreshBackups);
return {
groupSlug,
restoreBackup,
selected,
...toRefs(state),

View file

@ -77,7 +77,7 @@ export default defineComponent({
const refUserDialog = ref();
const { $auth } = useContext();
const user = computed(() => $auth.user as UserOut | null);
const user = computed(() => $auth.user);
const { $globals, i18n } = useContext();

View file

@ -1,23 +0,0 @@
<template>
<client-only>
<CookbookPage :group-slug="groupSlug" />
</client-only>
</template>
<script lang="ts">
import { defineComponent, useRoute } from "@nuxtjs/composition-api";
import CookbookPage from "@/components/Domain/Cookbook/CookbookPage.vue";
export default defineComponent({
components: { CookbookPage },
layout: "explore",
setup() {
const route = useRoute();
const groupSlug = route.value.params.groupSlug;
return {
groupSlug,
}
},
})
</script>

View file

@ -1,47 +0,0 @@
<template>
<div v-if="recipe">
<client-only>
<RecipePage :recipe="recipe" />
</client-only>
</div>
</template>
<script lang="ts">
import { defineComponent, useAsync, useMeta, useRoute, useRouter } from "@nuxtjs/composition-api";
import RecipePage from "~/components/Domain/Recipe/RecipePage/RecipePage.vue";
import { usePublicExploreApi } from "~/composables/api/api-client";
export default defineComponent({
components: { RecipePage },
layout: "explore",
setup() {
const route = useRoute();
const router = useRouter();
const groupSlug = route.value.params.groupSlug;
const recipeSlug = route.value.params.recipeSlug;
const api = usePublicExploreApi(groupSlug);
const { title } = useMeta();
const recipe = useAsync(async () => {
const { data, error } = await api.explore.recipes.getOne(recipeSlug);
if (error) {
console.error("error loading recipe -> ", error);
router.push("/");
}
if (data) {
title.value = data?.name || "";
}
return data;
});
return {
recipe,
};
},
head: {},
});
</script>

View file

@ -1,42 +0,0 @@
<template>
<div v-if="groupSlug">
<client-only>
<RecipeExplorerPage :group-slug="groupSlug" />
</client-only>
</div>
</template>
<script lang="ts">
import { defineComponent, useRoute, useRouter } from "@nuxtjs/composition-api";
import { invoke } from "@vueuse/core";
import RecipeExplorerPage from "~/components/Domain/Recipe/RecipeExplorerPage.vue";
import { usePublicExploreApi } from "~/composables/api/api-client";
export default defineComponent({
components: { RecipeExplorerPage },
layout: "explore",
setup() {
const route = useRoute();
const router = useRouter();
const groupSlug = route.value.params.groupSlug;
const api = usePublicExploreApi(groupSlug);
invoke(async () => {
if (!groupSlug) {
return;
}
// try to fetch one tag to make sure the group slug is valid
const { data } = await api.explore.tags.getAll(1, 1);
if (!data) {
// the group slug is invalid, so leave the page (this results in a 404)
router.push("/explore/recipes");
}
});
return {
groupSlug,
};
},
});
</script>

View file

@ -90,14 +90,22 @@
</template>
<script lang="ts">
import { defineComponent } from "@nuxtjs/composition-api";
import { defineComponent, useRouter } from "@nuxtjs/composition-api";
import draggable from "vuedraggable";
import { useCookbooks } from "@/composables/use-group-cookbooks";
import { useLoggedInState } from "~/composables/use-logged-in-state";
import RecipeOrganizerSelector from "~/components/Domain/Recipe/RecipeOrganizerSelector.vue";
export default defineComponent({
components: { draggable, RecipeOrganizerSelector },
setup() {
const { isOwnGroup, loggedIn } = useLoggedInState();
const router = useRouter();
if (!(loggedIn.value && isOwnGroup.value)) {
router.back();
}
const { cookbooks, actions } = useCookbooks();
return {

View file

@ -0,0 +1,14 @@
<template>
<div>
<RecipeExplorerPage />
</div>
</template>
<script lang="ts">
import { defineComponent } from "@nuxtjs/composition-api";
import RecipeExplorerPage from "~/components/Domain/Recipe/RecipeExplorerPage.vue";
export default defineComponent({
components: { RecipeExplorerPage },
});
</script>

View file

@ -0,0 +1,59 @@
<template>
<div>
<RecipePage v-if="recipe" :recipe="recipe" />
</div>
</template>
<script lang="ts">
import { computed, defineComponent, ref, useAsync, useContext, useMeta, useRoute, useRouter } from "@nuxtjs/composition-api";
import { useLoggedInState } from "~/composables/use-logged-in-state";
import RecipePage from "~/components/Domain/Recipe/RecipePage/RecipePage.vue";
import { usePublicExploreApi } from "~/composables/api/api-client";
import { useRecipe } from "~/composables/recipes";
import { Recipe } from "~/lib/api/types/recipe";
export default defineComponent({
components: { RecipePage },
setup() {
const { $auth } = useContext();
const { isOwnGroup } = useLoggedInState();
const route = useRoute();
const router = useRouter();
const slug = route.value.params.slug;
const { title } = useMeta();
let recipe = ref<Recipe | null>(null);
if (isOwnGroup.value) {
const { recipe: data } = useRecipe(slug);
recipe = data;
} else {
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "")
const api = usePublicExploreApi(groupSlug.value);
recipe = useAsync(async () => {
const { data, error } = await api.explore.recipes.getOne(slug);
if (error) {
console.error("error loading recipe -> ", error);
router.push(`/g/${groupSlug.value}`);
}
return data;
})
}
title.value = recipe.value?.name || "";
return {
recipe,
};
},
head() {
if (this.recipe) {
return {
title: this.recipe.name
}
}
}
});
</script>

View file

@ -94,7 +94,7 @@
</template>
<script lang="ts">
import { defineComponent, ref, useRoute, useRouter } from "@nuxtjs/composition-api";
import { computed, defineComponent, ref, useContext, useRoute, useRouter } from "@nuxtjs/composition-api";
import { invoke, until } from "@vueuse/core";
import {
CreateIngredientFood,
@ -124,9 +124,12 @@ export default defineComponent({
RecipeIngredientEditor,
},
setup() {
const { $auth } = useContext();
const panels = ref<number[]>([]);
const route = useRoute();
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
const router = useRouter();
const slug = route.value.params.slug;
const api = useUserApi();
@ -324,7 +327,7 @@ export default defineComponent({
const { response } = await api.recipes.updateOne(recipe.value.slug, recipe.value);
if (response?.status === 200) {
router.push("/recipe/" + recipe.value.slug);
router.push(`/g/${groupSlug.value}/r/${recipe.value.slug}`);
}
}

View file

@ -20,7 +20,7 @@
<AdvancedOnly>
<v-container class="d-flex justify-center align-center my-4">
<a to="/group/migrations"> {{ $t('recipe.looking-for-migrations') }}</a>
<a :to="`/group/migrations`"> {{ $t('recipe.looking-for-migrations') }}</a>
</v-container>
</AdvancedOnly>
</div>
@ -34,7 +34,7 @@ import AdvancedOnly from "~/components/global/AdvancedOnly.vue";
export default defineComponent({
components: { AdvancedOnly },
setup() {
const { $globals, i18n } = useContext();
const { $auth, $globals, i18n } = useContext();
const subpages: MenuItem[] = [
{
@ -71,10 +71,11 @@ export default defineComponent({
const route = useRoute();
const router = useRouter();
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
const subpage = computed({
set(subpage: string) {
router.push({ path: `/recipe/create/${subpage}`, query: route.value.query });
router.push({ path: `/g/${groupSlug.value}/r/create/${subpage}`, query: route.value.query });
},
get() {
return route.value.path.split("/").pop() ?? "url";
@ -82,6 +83,7 @@ export default defineComponent({
});
return {
groupSlug,
subpages,
subpage,
};

View file

@ -10,7 +10,7 @@ export default defineComponent({
const router = useRouter();
onMounted(() => {
// Force redirect to first valid page
router.replace("/recipe/create/url");
router.replace("/r/create/url");
});
return {};
},

View file

@ -35,7 +35,7 @@
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs, ref, useRouter } from "@nuxtjs/composition-api";
import { defineComponent, reactive, toRefs, ref, useContext, useRouter, computed, useRoute } from "@nuxtjs/composition-api";
import { AxiosResponse } from "axios";
import { useUserApi } from "~/composables/api";
import { validators } from "~/composables/use-validators";
@ -47,6 +47,10 @@ export default defineComponent({
error: false,
loading: false,
});
const { $auth } = useContext();
const route = useRoute();
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
const api = useUserApi();
const router = useRouter();
@ -56,7 +60,7 @@ export default defineComponent({
state.loading = false;
return;
}
router.push(`/recipe/${response.data}?edit=${edit.toString()}`);
router.push(`/g/${groupSlug.value}/r/${response.data}?edit=${edit.toString()}`);
}
const newRecipeName = ref("");

View file

@ -32,7 +32,7 @@
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs, ref, useRouter } from "@nuxtjs/composition-api";
import { defineComponent, reactive, toRefs, ref, useRouter, computed, useContext, useRoute } from "@nuxtjs/composition-api";
import { AxiosResponse } from "axios";
import { useUserApi } from "~/composables/api";
import { validators } from "~/composables/use-validators";
@ -45,6 +45,10 @@ export default defineComponent({
loading: false,
makeFileRecipeImage: false,
});
const { $auth } = useContext();
const route = useRoute();
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
const api = useUserApi();
const router = useRouter();
@ -56,7 +60,7 @@ export default defineComponent({
state.loading = false;
return;
}
router.push(`/recipe/${response.data}/ocr-editor`);
router.push(`/g/${groupSlug.value}/r/${response.data}/ocr-editor`);
}
const domCreateByOcr = ref<VForm | null>(null);

View file

@ -69,6 +69,7 @@ import {
ref,
useRouter,
computed,
useContext,
useRoute,
onMounted,
} from "@nuxtjs/composition-api";
@ -85,8 +86,11 @@ export default defineComponent({
loading: false,
});
const { $auth } = useContext();
const api = useUserApi();
const route = useRoute();
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
const router = useRouter();
const tags = useTagStore();
@ -99,7 +103,7 @@ export default defineComponent({
if (refreshTags) {
tags.actions.refresh();
}
router.push(`/recipe/${response.data}?edit=${edit.toString()}`);
router.push(`/g/${groupSlug.value}/r/${response.data}?edit=${edit.toString()}`);
}
const recipeUrl = computed({

View file

@ -30,7 +30,7 @@
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs, ref, useRouter } from "@nuxtjs/composition-api";
import { computed, defineComponent, reactive, toRefs, ref, useContext, useRoute, useRouter } from "@nuxtjs/composition-api";
import { AxiosResponse } from "axios";
import { useUserApi } from "~/composables/api";
import { validators } from "~/composables/use-validators";
@ -41,6 +41,10 @@ export default defineComponent({
error: false,
loading: false,
});
const { $auth } = useContext();
const route = useRoute();
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
const api = useUserApi();
const router = useRouter();
@ -50,7 +54,7 @@ export default defineComponent({
state.loading = false;
return;
}
router.push(`/recipe/${response.data}?edit=${edit.toString()}`);
router.push(`/g/${groupSlug.value}/r/${response.data}?edit=${edit.toString()}`);
}
const newRecipeZip = ref<File | null>(null);

View file

@ -7,7 +7,7 @@
</template>
<script lang="ts">
import { defineComponent, useAsync, useMeta, useRoute, useRouter } from "@nuxtjs/composition-api";
import { computed, defineComponent, useAsync, useContext, useMeta, useRoute, useRouter } from "@nuxtjs/composition-api";
import RecipePage from "~/components/Domain/Recipe/RecipePage/RecipePage.vue";
import { usePublicApi } from "~/composables/api/api-client";
@ -15,7 +15,10 @@ export default defineComponent({
components: { RecipePage },
layout: "basic",
setup() {
const { $auth } = useContext();
const route = useRoute();
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
const router = useRouter();
const recipeId = route.value.params.id;
const api = usePublicApi();
@ -27,7 +30,7 @@ export default defineComponent({
if (error) {
console.error("error loading recipe -> ", error);
router.push("/");
router.push(`/g/${groupSlug.value}`);
}
if (data) {

View file

@ -46,7 +46,9 @@ export default defineComponent({
labels: i18n.tc("data-pages.labels.labels"),
};
const DATA_TYPE_OPTIONS = [
const route = useRoute();
const DATA_TYPE_OPTIONS = computed(() => [
{
text: i18n.t("general.recipes"),
value: "new",
@ -67,9 +69,7 @@ export default defineComponent({
value: "new",
to: "/group/data/labels",
},
];
const route = useRoute();
]);
const buttonText = computed(() => {
const last = route.value.path.split("/").pop();

View file

@ -26,10 +26,10 @@
<div class="d-flex align-center justify-space-between mb-2">
<v-tabs>
<v-tab to="/group/mealplan/planner/view">{{ $t('meal-plan.meal-planner') }}</v-tab>
<v-tab to="/group/mealplan/planner/edit">{{ $t('general.edit') }}</v-tab>
<v-tab :to="`/group/mealplan/planner/view`">{{ $t('meal-plan.meal-planner') }}</v-tab>
<v-tab :to="`/group/mealplan/planner/edit`">{{ $t('general.edit') }}</v-tab>
</v-tabs>
<ButtonLink :icon="$globals.icons.calendar" to="/group/mealplan/settings" :text="$tc('general.settings')" />
<ButtonLink :icon="$globals.icons.calendar" :to="`/group/mealplan/settings`" :text="$tc('general.settings')" />
</div>
<div>

View file

@ -14,7 +14,7 @@
</template>
</i18n>
<v-container class="mt-1 px-0">
<nuxt-link class="text-center" to="/user/profile/edit"> {{ $t('group.looking-to-update-your-profile') }} </nuxt-link>
<nuxt-link class="text-center" :to="`/user/profile/edit`"> {{ $t('group.looking-to-update-your-profile') }} </nuxt-link>
</v-container>
</BasePageTitle>
<v-data-table

View file

@ -1,29 +1,21 @@
<template>
<div v-if="groupSlug">
<RecipeExplorerPage :group-slug="groupSlug" />
</div>
<div></div>
</template>
<script lang="ts">
import { defineComponent, ref } from "@nuxtjs/composition-api";
import { invoke } from "@vueuse/core";
import { useUserApi } from "~/composables/api/api-client";
import RecipeExplorerPage from "~/components/Domain/Recipe/RecipeExplorerPage.vue";
import { computed, defineComponent, useContext, useRouter } from "@nuxtjs/composition-api";
export default defineComponent({
components: { RecipeExplorerPage },
layout: "blank",
setup() {
const api = useUserApi();
const groupSlug = ref<string>();
const { $auth } = useContext();
const router = useRouter();
const groupSlug = computed(() => $auth.user?.groupSlug);
invoke(async () => {
const { data } = await api.users.getSelfGroup();
groupSlug.value = data?.slug;
});
return {
groupSlug,
};
},
if (groupSlug.value) {
router.push(`/g/${groupSlug.value}`);
} else {
router.push("/login");
}
}
});
</script>

View file

@ -103,6 +103,7 @@
<script lang="ts">
import { defineComponent, ref, useContext, computed, reactive, useRouter } from "@nuxtjs/composition-api";
import { useDark, whenever } from "@vueuse/core";
import { useLoggedInState } from "~/composables/use-logged-in-state";
import { useAppInfo } from "~/composables/api";
import { usePasswordField } from "~/composables/use-passwords";
import { alert } from "~/composables/use-toast";
@ -115,11 +116,13 @@ export default defineComponent({
const router = useRouter();
const { $auth, i18n } = useContext();
const { loggedIn } = useLoggedInState();
const groupSlug = computed(() => $auth.user?.groupSlug);
whenever(
() => $auth.loggedIn,
() => loggedIn.value && groupSlug.value,
() => {
router.push("/");
router.push(`/g/${groupSlug.value || ""}`);
},
{ immediate: true },
);

View file

@ -1,33 +0,0 @@
<template>
<div>
<RecipePage v-if="recipe" :recipe="recipe" />
</div>
</template>
<script lang="ts">
import { defineComponent, useRoute } from "@nuxtjs/composition-api";
import RecipePage from "~/components/Domain/Recipe/RecipePage/RecipePage.vue";
import { useRecipe } from "~/composables/recipes";
export default defineComponent({
components: { RecipePage },
setup() {
const route = useRoute();
const slug = route.value.params.slug;
const { recipe, loading } = useRecipe(slug);
return {
recipe,
loading,
};
},
head() {
if (this.recipe) {
return {
title: this.recipe.name
}
}
}
});
</script>

View file

@ -199,7 +199,7 @@
<v-lazy>
<div class="d-flex justify-end mt-10">
<ButtonLink to="/group/data/labels" :text="$tc('shopping-list.manage-labels')" :icon="$globals.icons.tags" />
<ButtonLink :to="`/group/data/labels`" :text="$tc('shopping-list.manage-labels')" :icon="$globals.icons.tags" />
</div>
</v-lazy>
</v-container>
@ -236,6 +236,7 @@ export default defineComponent({
ShoppingListItemEditor,
},
setup() {
const { $auth, i18n } = useContext();
const preferences = useShoppingListPreferences();
const { idle } = useIdle(5 * 60 * 1000) // 5 minutes
@ -247,10 +248,9 @@ export default defineComponent({
const reorderLabelsDialog = ref(false);
const route = useRoute();
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
const id = route.value.params.id;
const { i18n } = useContext();
// ===============================================================
// Shopping List Actions
@ -755,6 +755,7 @@ export default defineComponent({
deleteListItem,
edit,
getLabelColor,
groupSlug,
itemsByLabel,
listItems,
loadingCounter,

View file

@ -33,19 +33,22 @@
</v-card>
</section>
<div class="d-flex justify-end mt-10">
<ButtonLink to="/group/data/labels" :text="$tc('shopping-list.manage-labels')" :icon="$globals.icons.tags" />
<ButtonLink :to="`/group/data/labels`" :text="$tc('shopping-list.manage-labels')" :icon="$globals.icons.tags" />
</div>
</v-container>
</template>
<script lang="ts">
import { defineComponent, useAsync, reactive, toRefs } from "@nuxtjs/composition-api";
import { computed, defineComponent, useAsync, useContext, reactive, toRefs, useRoute } from "@nuxtjs/composition-api";
import { useUserApi } from "~/composables/api";
import { useAsyncKey } from "~/composables/use-utils";
export default defineComponent({
setup() {
const { $auth } = useContext();
const userApi = useUserApi();
const route = useRoute();
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
const state = reactive({
createName: "",
@ -95,6 +98,7 @@ export default defineComponent({
return {
...toRefs(state),
groupSlug,
shoppingLists,
createOne,
deleteOne,

View file

@ -1,6 +1,6 @@
<template>
<v-container>
<RecipeCardSection v-if="user" :icon="$globals.icons.heart" :title="$tc('user.user-favorites')" :recipes="user.favoriteRecipes">
<RecipeCardSection v-if="user && isOwnGroup" :icon="$globals.icons.heart" :title="$tc('user.user-favorites')" :recipes="user.favoriteRecipes">
</RecipeCardSection>
</v-container>
</template>
@ -8,6 +8,7 @@
<script lang="ts">
import { defineComponent, useAsync, useRoute } from "@nuxtjs/composition-api";
import RecipeCardSection from "~/components/Domain/Recipe/RecipeCardSection.vue";
import { useLoggedInState } from "~/composables/use-logged-in-state";
import { useUserApi } from "~/composables/api";
import { useAsyncKey } from "~/composables/use-utils";
@ -16,6 +17,7 @@ export default defineComponent({
setup() {
const api = useUserApi();
const route = useRoute();
const { isOwnGroup } = useLoggedInState();
const userId = route.value.params.id;
@ -26,6 +28,7 @@ export default defineComponent({
return {
user,
isOwnGroup,
};
},
head() {

View file

@ -1,7 +1,7 @@
<template>
<div></div>
</template>
<script lang="ts">
import { defineComponent } from "@nuxtjs/composition-api";
@ -16,6 +16,6 @@ export default defineComponent({
},
});
</script>
<style scoped>
</style>
</style>

View file

@ -108,9 +108,9 @@
:label="$t('profile.show-advanced-description')"
@change="updateUser"
></v-checkbox>
<nuxt-link class="mt-5 d-flex flex-column justify-center text-center" to="/group"> {{ $t('profile.looking-for-privacy-settings') }} </nuxt-link>
<nuxt-link class="mt-5 d-flex flex-column justify-center text-center" :to="`/group`"> {{ $t('profile.looking-for-privacy-settings') }} </nuxt-link>
<div class="d-flex flex-wrap justify-center mt-5">
<v-btn outlined class="rounded-xl my-1 mx-1" to="/user/profile" nuxt exact>
<v-btn outlined class="rounded-xl my-1 mx-1" :to="`/user/profile`" nuxt exact>
<v-icon left>
{{ $globals.icons.backArrow }}
</v-icon>

View file

@ -16,17 +16,6 @@
</v-icon>
{{ $t('profile.get-invite-link') }}
</v-btn>
<v-btn
v-if="group && group.preferences && !group.preferences.privateGroup"
outlined
rounded
@click="getPublicLink()"
>
<v-icon left>
{{ $globals.icons.shareVariant }}
</v-icon>
{{ $t('profile.get-public-link') }}
</v-btn>
</v-card-actions>
<div v-show="generatedSignupLink !== ''">
<v-card-text>
@ -114,7 +103,7 @@
<v-row tag="section">
<v-col cols="12" sm="12" md="6">
<UserProfileLinkCard
:link="{ text: $tc('profile.manage-user-profile'), to: '/user/profile/edit' }"
:link="{ text: $tc('profile.manage-user-profile'), to: `/user/profile/edit` }"
:image="require('~/static/svgs/manage-profile.svg')"
>
<template #title> {{ $t('profile.user-settings') }} </template>
@ -124,7 +113,7 @@
<AdvancedOnly>
<v-col cols="12" sm="12" md="6">
<UserProfileLinkCard
:link="{ text: $tc('profile.manage-your-api-tokens'), to: '/user/profile/api-tokens' }"
:link="{ text: $tc('profile.manage-your-api-tokens'), to: `/user/profile/api-tokens` }"
:image="require('~/static/svgs/manage-api-tokens.svg')"
>
<template #title> {{ $t('settings.token.api-tokens') }} </template>
@ -143,7 +132,7 @@
<v-row tag="section">
<v-col cols="12" sm="12" md="6">
<UserProfileLinkCard
:link="{ text: $tc('profile.group-settings'), to: '/group' }"
:link="{ text: $tc('profile.group-settings'), to: `/group` }"
:image="require('~/static/svgs/manage-group-settings.svg')"
>
<template #title> {{ $t('profile.group-settings') }} </template>
@ -152,7 +141,7 @@
</v-col>
<v-col cols="12" sm="12" md="6">
<UserProfileLinkCard
:link="{ text: $tc('profile.manage-cookbooks'), to: '/group/cookbooks' }"
:link="{ text: $tc('profile.manage-cookbooks'), to: `/g/${groupSlug}/cookbooks` }"
:image="require('~/static/svgs/manage-cookbooks.svg')"
>
<template #title> {{ $t('sidebar.cookbooks') }} </template>
@ -161,7 +150,7 @@
</v-col>
<v-col v-if="user.canManage" cols="12" sm="12" md="6">
<UserProfileLinkCard
:link="{ text: $tc('profile.manage-members'), to: '/group/members' }"
:link="{ text: $tc('profile.manage-members'), to: `/group/members` }"
:image="require('~/static/svgs/manage-members.svg')"
>
<template #title> {{ $t('profile.members') }} </template>
@ -171,7 +160,7 @@
<AdvancedOnly>
<v-col v-if="user.advanced" cols="12" sm="12" md="6">
<UserProfileLinkCard
:link="{ text: $tc('profile.manage-webhooks'), to: '/group/webhooks' }"
:link="{ text: $tc('profile.manage-webhooks'), to: `/group/webhooks` }"
:image="require('~/static/svgs/manage-webhooks.svg')"
>
<template #title> {{ $t('settings.webhooks.webhooks') }} </template>
@ -182,7 +171,7 @@
<AdvancedOnly>
<v-col cols="12" sm="12" md="6">
<UserProfileLinkCard
:link="{ text: $tc('profile.manage-notifiers'), to: '/group/notifiers' }"
:link="{ text: $tc('profile.manage-notifiers'), to: `/group/notifiers` }"
:image="require('~/static/svgs/manage-notifiers.svg')"
>
<template #title> {{ $t('profile.notifiers') }} </template>
@ -193,7 +182,7 @@
<AdvancedOnly>
<v-col cols="12" sm="12" md="6">
<UserProfileLinkCard
:link="{ text: $tc('profile.manage-data'), to: '/group/data/foods' }"
:link="{ text: $tc('profile.manage-data'), to: `/group/data/foods` }"
:image="require('~/static/svgs/manage-recipes.svg')"
>
<template #title> {{ $t('profile.manage-data') }} </template>
@ -204,7 +193,7 @@
<AdvancedOnly>
<v-col cols="12" sm="12" md="6">
<UserProfileLinkCard
:link="{ text: $tc('profile.manage-data-migrations'), to: '/group/migrations' }"
:link="{ text: $tc('profile.manage-data-migrations'), to: `/group/migrations` }"
:image="require('~/static/svgs/manage-data-migrations.svg')"
>
<template #title>{{ $t('profile.data-migrations') }} </template>
@ -218,7 +207,7 @@
</template>
<script lang="ts">
import { computed, defineComponent, useContext, ref, toRefs, reactive, useAsync } from "@nuxtjs/composition-api";
import { computed, defineComponent, useContext, ref, toRefs, reactive, useAsync, useRoute } from "@nuxtjs/composition-api";
import { invoke, until } from "@vueuse/core";
import UserProfileLinkCard from "@/components/Domain/User/UserProfileLinkCard.vue";
import { useUserApi } from "~/composables/api";
@ -239,6 +228,8 @@ export default defineComponent({
scrollToTop: true,
setup() {
const { $auth, i18n } = useContext();
const route = useRoute();
const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || "");
// @ts-ignore $auth.user is typed as unknown, but it's a user
const user = computed<UserOut | null>(() => $auth.user);
@ -261,14 +252,6 @@ export default defineComponent({
group.value = data;
});
function getPublicLink() {
if (group.value) {
publicLink.value = `${window.location.origin}/explore/recipes/${group.value.slug}`
showPublicLink.value = true;
generatedSignupLink.value = "";
}
}
async function getSignupLink() {
const { data } = await api.groups.createInvitation({ uses: 1 });
if (data) {
@ -350,16 +333,16 @@ export default defineComponent({
return iconText[key] ?? $globals.icons.primary;
}
const statsTo: { [key: string]: string } = {
totalRecipes: "/",
const statsTo = computed<{ [key: string]: string }>(() => { return {
totalRecipes: `/g/${groupSlug.value}/`,
totalUsers: "/group/members",
totalCategories: "/recipes/categories",
totalTags: "/recipes/tags",
totalTools: "/recipes/tools",
};
totalCategories: `/g/${groupSlug.value}/recipes/categories`,
totalTags: `/g/${groupSlug.value}/recipes/tags`,
totalTools: `/g/${groupSlug.value}/recipes/tools`,
}});
function getStatsTo(key: string) {
return statsTo[key] ?? "unknown";
return statsTo.value[key] ?? "unknown";
}
const storage = useAsync(async () => {
@ -386,6 +369,7 @@ export default defineComponent({
});
return {
groupSlug,
storageText,
storageUsedPercentage,
getStatsTitle,
@ -399,7 +383,6 @@ export default defineComponent({
showPublicLink,
publicLink,
getSignupLink,
getPublicLink,
sendInvite,
validators,
validEmail,