mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-07-24 07:39:41 +02:00
feat: Migrate to Nuxt 3 framework (#5184)
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com> Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
This commit is contained in:
parent
89ab7fac25
commit
c24d532608
403 changed files with 23959 additions and 19557 deletions
|
@ -1,11 +1,18 @@
|
|||
<template>
|
||||
<div v-if="dialog">
|
||||
<BaseDialog v-if="shoppingListDialog && ready" v-model="dialog" :title="$t('recipe.add-to-list')" :icon="$globals.icons.cartCheck">
|
||||
<v-container v-if="!shoppingListChoices.length">
|
||||
<BasePageTitle>
|
||||
<template #title>{{ $t('shopping-list.no-shopping-lists-found') }}</template>
|
||||
</BasePageTitle>
|
||||
</v-container>
|
||||
<BaseDialog
|
||||
v-if="shoppingListDialog && ready"
|
||||
v-model="dialog"
|
||||
:title="$t('recipe.add-to-list')"
|
||||
:icon="$globals.icons.cartCheck"
|
||||
>
|
||||
<v-container v-if="!shoppingListChoices.length">
|
||||
<BasePageTitle>
|
||||
<template #title>
|
||||
{{ $t('shopping-list.no-shopping-lists-found') }}
|
||||
</template>
|
||||
</BasePageTitle>
|
||||
</v-container>
|
||||
<v-card-text>
|
||||
<v-card
|
||||
v-for="list in shoppingListChoices"
|
||||
|
@ -21,14 +28,23 @@
|
|||
</v-card-text>
|
||||
<template #card-actions>
|
||||
<v-btn
|
||||
text
|
||||
variant="text"
|
||||
color="grey"
|
||||
@click="dialog = false"
|
||||
>
|
||||
{{ $t("general.cancel") }}
|
||||
</v-btn>
|
||||
<div class="d-flex justify-end" style="width: 100%;">
|
||||
<v-checkbox v-model="preferences.viewAllLists" hide-details :label="$tc('general.show-all')" class="my-auto mr-4" @click="setShowAllToggled()" />
|
||||
<div
|
||||
class="d-flex justify-end"
|
||||
style="width: 100%;"
|
||||
>
|
||||
<v-checkbox
|
||||
v-model="preferences.viewAllLists"
|
||||
hide-details
|
||||
:label="$t('general.show-all')"
|
||||
class="my-auto mr-4"
|
||||
@click="setShowAllToggled()"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</BaseDialog>
|
||||
|
@ -38,32 +54,52 @@
|
|||
:title="selectedShoppingList ? selectedShoppingList.name : $t('recipe.add-to-list')"
|
||||
:icon="$globals.icons.cartCheck"
|
||||
width="70%"
|
||||
:submit-text="$tc('recipe.add-to-list')"
|
||||
:submit-text="$t('recipe.add-to-list')"
|
||||
can-submit
|
||||
@submit="addRecipesToList()"
|
||||
>
|
||||
<div style="max-height: 70vh; overflow-y: auto">
|
||||
<v-card
|
||||
v-for="(recipeSection, recipeSectionIndex) in recipeIngredientSections" :key="recipeSection.recipeId + recipeSectionIndex"
|
||||
v-for="(recipeSection, recipeSectionIndex) in recipeIngredientSections"
|
||||
:key="recipeSection.recipeId + recipeSectionIndex"
|
||||
elevation="0"
|
||||
height="fit-content"
|
||||
width="100%"
|
||||
>
|
||||
<v-divider v-if="recipeSectionIndex > 0" class="mt-3" />
|
||||
<v-divider
|
||||
v-if="recipeSectionIndex > 0"
|
||||
class="mt-3"
|
||||
/>
|
||||
<v-card-title
|
||||
v-if="recipeIngredientSections.length > 1"
|
||||
class="justify-center text-h5"
|
||||
width="100%"
|
||||
>
|
||||
<v-container style="width: 100%;">
|
||||
<v-row no-gutters class="ma-0 pa-0">
|
||||
<v-col cols="12" align-self="center" class="text-center">
|
||||
<v-row
|
||||
no-gutters
|
||||
class="ma-0 pa-0"
|
||||
>
|
||||
<v-col
|
||||
cols="12"
|
||||
align-self="center"
|
||||
class="text-center"
|
||||
>
|
||||
{{ recipeSection.recipeName }}
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="recipeSection.recipeScale > 1" no-gutters class="ma-0 pa-0">
|
||||
<v-row
|
||||
v-if="recipeSection.recipeScale > 1"
|
||||
no-gutters
|
||||
class="ma-0 pa-0"
|
||||
>
|
||||
<!-- TODO: make this editable in the dialog and visible on single-recipe lists -->
|
||||
<v-col cols="12" align-self="center" class="text-center">
|
||||
({{ $tc("recipe.quantity") }}: {{ recipeSection.recipeScale }})
|
||||
<v-col
|
||||
cols="12"
|
||||
align-self="center"
|
||||
class="text-center"
|
||||
>
|
||||
({{ $t("recipe.quantity") }}: {{ recipeSection.recipeScale }})
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
|
@ -73,36 +109,41 @@
|
|||
v-for="(ingredientSection, ingredientSectionIndex) in recipeSection.ingredientSections"
|
||||
:key="recipeSection.recipeId + recipeSectionIndex + ingredientSectionIndex"
|
||||
>
|
||||
<v-card-title v-if="ingredientSection.sectionName" class="ingredient-title mt-2 pb-0 text-h6">
|
||||
<v-card-title
|
||||
v-if="ingredientSection.sectionName"
|
||||
class="ingredient-title mt-2 pb-0 text-h6"
|
||||
>
|
||||
{{ ingredientSection.sectionName }}
|
||||
</v-card-title>
|
||||
<div
|
||||
:class="$vuetify.breakpoint.smAndDown ? '' : 'ingredient-grid'"
|
||||
:style="$vuetify.breakpoint.smAndDown ? '' : { gridTemplateRows: `repeat(${Math.ceil(ingredientSection.ingredients.length / 2)}, min-content)` }"
|
||||
:class="$vuetify.display.smAndDown ? '' : 'ingredient-grid'"
|
||||
:style="$vuetify.display.smAndDown ? '' : { gridTemplateRows: `repeat(${Math.ceil(ingredientSection.ingredients.length / 2)}, min-content)` }"
|
||||
>
|
||||
<v-list-item
|
||||
v-for="(ingredientData, i) in ingredientSection.ingredients"
|
||||
:key="recipeSection.recipeId + recipeSectionIndex + ingredientSectionIndex + i"
|
||||
dense
|
||||
density="compact"
|
||||
@click="recipeIngredientSections[recipeSectionIndex]
|
||||
.ingredientSections[ingredientSectionIndex]
|
||||
.ingredients[i].checked = !recipeIngredientSections[recipeSectionIndex]
|
||||
.ingredientSections[ingredientSectionIndex]
|
||||
.ingredients[i]
|
||||
.checked"
|
||||
.ingredientSections[ingredientSectionIndex]
|
||||
.ingredients[i]
|
||||
.checked"
|
||||
>
|
||||
<v-checkbox
|
||||
hide-details
|
||||
:input-value="ingredientData.checked"
|
||||
:model-value="ingredientData.checked"
|
||||
class="pt-0 my-auto py-auto"
|
||||
color="secondary"
|
||||
density="compact"
|
||||
/>
|
||||
<v-list-item-content :key="ingredientData.ingredient.quantity">
|
||||
<div :key="ingredientData.ingredient.quantity">
|
||||
<RecipeIngredientListItem
|
||||
:ingredient="ingredientData.ingredient"
|
||||
:disable-amount="ingredientData.disableAmount"
|
||||
:scale="recipeSection.recipeScale" />
|
||||
</v-list-item-content>
|
||||
:scale="recipeSection.recipeScale"
|
||||
/>
|
||||
</div>
|
||||
</v-list-item>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -114,12 +155,12 @@
|
|||
:buttons="[
|
||||
{
|
||||
icon: $globals.icons.checkboxBlankOutline,
|
||||
text: $tc('shopping-list.uncheck-all-items'),
|
||||
text: $t('shopping-list.uncheck-all-items'),
|
||||
event: 'uncheck',
|
||||
},
|
||||
{
|
||||
icon: $globals.icons.checkboxOutline,
|
||||
text: $tc('shopping-list.check-all-items'),
|
||||
text: $t('shopping-list.check-all-items'),
|
||||
event: 'check',
|
||||
},
|
||||
]"
|
||||
|
@ -132,14 +173,13 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, reactive, ref, useContext, watchEffect } from "@nuxtjs/composition-api";
|
||||
import { toRefs } from "@vueuse/core";
|
||||
import RecipeIngredientListItem from "./RecipeIngredientListItem.vue";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { alert } from "~/composables/use-toast";
|
||||
import { useShoppingListPreferences } from "~/composables/use-users/preferences";
|
||||
import { RecipeIngredient, ShoppingListAddRecipeParamsBulk, ShoppingListSummary } from "~/lib/api/types/household";
|
||||
import { Recipe } from "~/lib/api/types/recipe";
|
||||
import type { RecipeIngredient, ShoppingListAddRecipeParamsBulk, ShoppingListSummary } from "~/lib/api/types/household";
|
||||
import type { Recipe } from "~/lib/api/types/recipe";
|
||||
|
||||
export interface RecipeWithScale extends Recipe {
|
||||
scale: number;
|
||||
|
@ -163,12 +203,12 @@ export interface ShoppingListRecipeIngredientSection {
|
|||
ingredientSections: ShoppingListIngredientSection[];
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
export default defineNuxtComponent({
|
||||
components: {
|
||||
RecipeIngredientListItem,
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
@ -181,8 +221,10 @@ export default defineComponent({
|
|||
default: () => [],
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
setup(props, context) {
|
||||
const { $auth, i18n } = useContext();
|
||||
const i18n = useI18n();
|
||||
const $auth = useMealieAuth();
|
||||
const api = useUserApi();
|
||||
const preferences = useShoppingListPreferences();
|
||||
const ready = ref(false);
|
||||
|
@ -190,10 +232,10 @@ export default defineComponent({
|
|||
// v-model support
|
||||
const dialog = computed({
|
||||
get: () => {
|
||||
return props.value;
|
||||
return props.modelValue;
|
||||
},
|
||||
set: (val) => {
|
||||
context.emit("input", val);
|
||||
context.emit("update:modelValue", val);
|
||||
initState();
|
||||
},
|
||||
});
|
||||
|
@ -205,11 +247,11 @@ export default defineComponent({
|
|||
});
|
||||
|
||||
const userHousehold = computed(() => {
|
||||
return $auth.user?.householdSlug || "";
|
||||
return $auth.user.value?.householdSlug || "";
|
||||
});
|
||||
|
||||
const shoppingListChoices = computed(() => {
|
||||
return props.shoppingLists.filter((list) => preferences.value.viewAllLists || list.userId === $auth.user?.id);
|
||||
return props.shoppingLists.filter(list => preferences.value.viewAllLists || list.userId === $auth.user.value?.id);
|
||||
});
|
||||
|
||||
const recipeIngredientSections = ref<ShoppingListRecipeIngredientSection[]>([]);
|
||||
|
@ -220,7 +262,8 @@ export default defineComponent({
|
|||
if (shoppingListChoices.value.length === 1 && !state.shoppingListShowAllToggled) {
|
||||
selectedShoppingList.value = shoppingListChoices.value[0];
|
||||
openShoppingListIngredientDialog(selectedShoppingList.value);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
ready.value = true;
|
||||
}
|
||||
},
|
||||
|
@ -234,7 +277,6 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
if (recipeSectionMap.has(recipe.slug)) {
|
||||
// @ts-ignore not undefined, see above
|
||||
recipeSectionMap.get(recipe.slug).recipeScale += recipe.scale;
|
||||
continue;
|
||||
}
|
||||
|
@ -247,7 +289,8 @@ export default defineComponent({
|
|||
recipe.id = data.id || "";
|
||||
recipe.name = data.name || "";
|
||||
recipe.recipeIngredient = data.recipeIngredient;
|
||||
} else if (!recipe.recipeIngredient.length) {
|
||||
}
|
||||
else if (!recipe.recipeIngredient.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -257,7 +300,7 @@ export default defineComponent({
|
|||
checked: !householdsWithFood.includes(userHousehold.value),
|
||||
ingredient: ing,
|
||||
disableAmount: recipe.settings?.disableAmount || false,
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
let currentTitle = "";
|
||||
|
@ -300,7 +343,7 @@ export default defineComponent({
|
|||
recipeName: recipe.name,
|
||||
recipeScale: recipe.scale,
|
||||
ingredientSections: shoppingListIngredientSections,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
recipeIngredientSections.value = Array.from(recipeSectionMap.values());
|
||||
|
@ -366,13 +409,13 @@ export default defineComponent({
|
|||
recipeId: section.recipeId,
|
||||
recipeIncrementQuantity: section.recipeScale,
|
||||
recipeIngredients: ingredients,
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
const { error } = await api.shopping.lists.addRecipes(selectedShoppingList.value.id, recipeData);
|
||||
error ? alert.error(i18n.tc("recipe.failed-to-add-recipes-to-list"))
|
||||
: alert.success(i18n.tc("recipe.successfully-added-to-list"));
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
error ? alert.error(i18n.t("recipe.failed-to-add-recipes-to-list")) : alert.success(i18n.t("recipe.successfully-added-to-list"));
|
||||
|
||||
state.shoppingListDialog = false;
|
||||
state.shoppingListIngredientDialog = false;
|
||||
|
@ -391,9 +434,9 @@ export default defineComponent({
|
|||
setShowAllToggled,
|
||||
recipeIngredientSections,
|
||||
selectedShoppingList,
|
||||
}
|
||||
};
|
||||
},
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="css">
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue