Add to Shopping List Dialog

This commit is contained in:
Kuchenpirat 2025-07-30 10:45:44 +00:00
commit 0dbb837dff

View file

@ -51,7 +51,7 @@
<BaseDialog
v-if="shoppingListIngredientDialog"
v-model="dialog"
:title="selectedShoppingList ? selectedShoppingList.name : $t('recipe.add-to-list')"
:title="selectedShoppingList?.name || $t('recipe.add-to-list')"
:icon="$globals.icons.cartCheck"
width="70%"
:submit-text="$t('recipe.add-to-list')"
@ -137,7 +137,7 @@
color="secondary"
density="compact"
/>
<div :key="ingredientData.ingredient.quantity">
<div :key="`${ingredientData.ingredient.quantity || 'no-qty'}-${i}`">
<RecipeIngredientListItem
:ingredient="ingredientData.ingredient"
:disable-amount="ingredientData.disableAmount"
@ -172,7 +172,7 @@
</div>
</template>
<script lang="ts">
<script setup lang="ts">
import { toRefs } from "@vueuse/core";
import RecipeIngredientListItem from "./RecipeIngredientListItem.vue";
import { useUserApi } from "~/composables/api";
@ -203,61 +203,43 @@ export interface ShoppingListRecipeIngredientSection {
ingredientSections: ShoppingListIngredientSection[];
}
export default defineNuxtComponent({
components: {
RecipeIngredientListItem,
},
props: {
modelValue: {
type: Boolean,
default: false,
},
recipes: {
type: Array as () => RecipeWithScale[],
default: undefined,
},
shoppingLists: {
type: Array as () => ShoppingListSummary[],
default: () => [],
},
},
emits: ["update:modelValue"],
setup(props, context) {
const i18n = useI18n();
const $auth = useMealieAuth();
const api = useUserApi();
const preferences = useShoppingListPreferences();
const ready = ref(false);
interface Props {
recipes?: RecipeWithScale[];
shoppingLists?: ShoppingListSummary[];
}
const props = withDefaults(defineProps<Props>(), {
recipes: undefined,
shoppingLists: () => [],
});
// v-model support
const dialog = computed({
get: () => {
return props.modelValue;
},
set: (val) => {
context.emit("update:modelValue", val);
initState();
},
});
const dialog = defineModel<boolean>({ default: false });
const state = reactive({
const i18n = useI18n();
const $auth = useMealieAuth();
const api = useUserApi();
const preferences = useShoppingListPreferences();
const ready = ref(false);
const state = reactive({
shoppingListDialog: true,
shoppingListIngredientDialog: false,
shoppingListShowAllToggled: false,
});
});
const userHousehold = computed(() => {
const { shoppingListDialog, shoppingListIngredientDialog, shoppingListShowAllToggled: _shoppingListShowAllToggled } = toRefs(state);
const userHousehold = computed(() => {
return $auth.user.value?.householdSlug || "";
});
});
const shoppingListChoices = computed(() => {
const shoppingListChoices = computed(() => {
return props.shoppingLists.filter(list => preferences.value.viewAllLists || list.userId === $auth.user.value?.id);
});
});
const recipeIngredientSections = ref<ShoppingListRecipeIngredientSection[]>([]);
const selectedShoppingList = ref<ShoppingListSummary | null>(null);
const recipeIngredientSections = ref<ShoppingListRecipeIngredientSection[]>([]);
const selectedShoppingList = ref<ShoppingListSummary | null>(null);
watchEffect(
watchEffect(
() => {
if (shoppingListChoices.value.length === 1 && !state.shoppingListShowAllToggled) {
selectedShoppingList.value = shoppingListChoices.value[0];
@ -267,9 +249,15 @@ export default defineNuxtComponent({
ready.value = true;
}
},
);
);
async function consolidateRecipesIntoSections(recipes: RecipeWithScale[]) {
watch(dialog, (val) => {
if (!val) {
initState();
}
});
async function consolidateRecipesIntoSections(recipes: RecipeWithScale[]) {
const recipeSectionMap = new Map<string, ShoppingListRecipeIngredientSection>();
for (const recipe of recipes) {
if (!recipe.slug) {
@ -277,7 +265,10 @@ export default defineNuxtComponent({
}
if (recipeSectionMap.has(recipe.slug)) {
recipeSectionMap.get(recipe.slug).recipeScale += recipe.scale;
const existingSection = recipeSectionMap.get(recipe.slug);
if (existingSection) {
existingSection.recipeScale += recipe.scale;
}
continue;
}
@ -347,19 +338,19 @@ export default defineNuxtComponent({
}
recipeIngredientSections.value = Array.from(recipeSectionMap.values());
}
}
function initState() {
function initState() {
state.shoppingListDialog = true;
state.shoppingListIngredientDialog = false;
state.shoppingListShowAllToggled = false;
recipeIngredientSections.value = [];
selectedShoppingList.value = null;
}
}
initState();
initState();
async function openShoppingListIngredientDialog(list: ShoppingListSummary) {
async function openShoppingListIngredientDialog(list: ShoppingListSummary) {
if (!props.recipes?.length) {
return;
}
@ -368,13 +359,13 @@ export default defineNuxtComponent({
await consolidateRecipesIntoSections(props.recipes);
state.shoppingListDialog = false;
state.shoppingListIngredientDialog = true;
}
}
function setShowAllToggled() {
function setShowAllToggled() {
state.shoppingListShowAllToggled = true;
}
}
function bulkCheckIngredients(value = true) {
function bulkCheckIngredients(value = true) {
recipeIngredientSections.value.forEach((recipeSection) => {
recipeSection.ingredientSections.forEach((ingSection) => {
ingSection.ingredients.forEach((ing) => {
@ -382,9 +373,9 @@ export default defineNuxtComponent({
});
});
});
}
}
async function addRecipesToList() {
async function addRecipesToList() {
if (!selectedShoppingList.value) {
return;
}
@ -420,23 +411,7 @@ export default defineNuxtComponent({
state.shoppingListDialog = false;
state.shoppingListIngredientDialog = false;
dialog.value = false;
}
return {
dialog,
preferences,
ready,
shoppingListChoices,
...toRefs(state),
addRecipesToList,
bulkCheckIngredients,
openShoppingListIngredientDialog,
setShowAllToggled,
recipeIngredientSections,
selectedShoppingList,
};
},
});
}
</script>
<style scoped lang="css">