| 
									
										
										
										
											2022-01-08 22:24:34 -09:00
										 |  |  | <template> | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |   <v-container | 
					
						
							|  |  |  |     v-if="shoppingListChoices && ready" | 
					
						
							|  |  |  |     class="narrow-container" | 
					
						
							|  |  |  |   > | 
					
						
							|  |  |  |     <BaseDialog | 
					
						
							|  |  |  |       v-model="createDialog" | 
					
						
							|  |  |  |       :title="$t('shopping-list.create-shopping-list')" | 
					
						
							|  |  |  |       can-submit | 
					
						
							|  |  |  |       @submit="createOne" | 
					
						
							|  |  |  |     > | 
					
						
							| 
									
										
										
										
											2022-01-08 22:24:34 -09:00
										 |  |  |       <v-card-text> | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         <v-text-field | 
					
						
							|  |  |  |           v-model="createName" | 
					
						
							|  |  |  |           autofocus | 
					
						
							|  |  |  |           :label="$t('shopping-list.new-list')" | 
					
						
							|  |  |  |         /> | 
					
						
							| 
									
										
										
										
											2022-01-08 22:24:34 -09:00
										 |  |  |       </v-card-text> | 
					
						
							|  |  |  |     </BaseDialog> | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-26 08:04:40 -06:00
										 |  |  |     <!-- Settings --> | 
					
						
							|  |  |  |     <BaseDialog | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       v-model="ownerDialog" | 
					
						
							|  |  |  |       :icon="$globals.icons.admin" | 
					
						
							|  |  |  |       :title="$t('user.edit-user')" | 
					
						
							|  |  |  |       can-confirm | 
					
						
							|  |  |  |       @confirm="updateOwner" | 
					
						
							|  |  |  |     > | 
					
						
							|  |  |  |       <v-container> | 
					
						
							|  |  |  |         <v-form> | 
					
						
							|  |  |  |           <v-select | 
					
						
							|  |  |  |             v-model="updateUserId" | 
					
						
							|  |  |  |             :items="allUsers" | 
					
						
							|  |  |  |             item-title="fullName" | 
					
						
							|  |  |  |             item-value="id" | 
					
						
							|  |  |  |             :label="$t('general.owner')" | 
					
						
							|  |  |  |             :prepend-icon="$globals.icons.user" | 
					
						
							|  |  |  |           /> | 
					
						
							|  |  |  |         </v-form> | 
					
						
							|  |  |  |       </v-container> | 
					
						
							|  |  |  |     </BaseDialog> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     <BaseDialog | 
					
						
							|  |  |  |       v-model="deleteDialog" | 
					
						
							|  |  |  |       :title="$t('general.confirm')" | 
					
						
							|  |  |  |       color="error" | 
					
						
							|  |  |  |       can-confirm | 
					
						
							|  |  |  |       @confirm="deleteOne" | 
					
						
							|  |  |  |     > | 
					
						
							| 
									
										
										
										
											2023-01-29 02:39:51 +01:00
										 |  |  |       <v-card-text>{{ $t('shopping-list.are-you-sure-you-want-to-delete-this-item') }}</v-card-text> | 
					
						
							| 
									
										
										
										
											2022-01-08 22:24:34 -09:00
										 |  |  |     </BaseDialog> | 
					
						
							|  |  |  |     <BasePageTitle divider> | 
					
						
							|  |  |  |       <template #header> | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         <v-img | 
					
						
							|  |  |  |           width="100%" | 
					
						
							|  |  |  |           max-height="100" | 
					
						
							|  |  |  |           max-width="100" | 
					
						
							|  |  |  |           :src="require('~/static/svgs/shopping-cart.svg')" | 
					
						
							|  |  |  |         /> | 
					
						
							|  |  |  |       </template> | 
					
						
							|  |  |  |       <template #title> | 
					
						
							|  |  |  |         {{ $t('shopping-list.shopping-lists') }} | 
					
						
							| 
									
										
										
										
											2022-01-08 22:24:34 -09:00
										 |  |  |       </template> | 
					
						
							|  |  |  |     </BasePageTitle> | 
					
						
							| 
									
										
										
										
											2024-02-23 17:07:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     <v-container class="d-flex align-center justify-end px-0 pt-0 pb-4"> | 
					
						
							|  |  |  |       <v-checkbox | 
					
						
							|  |  |  |         v-model="preferences.viewAllLists" | 
					
						
							|  |  |  |         hide-details | 
					
						
							|  |  |  |         :label="$t('general.show-all')" | 
					
						
							|  |  |  |         class="my-0 mr-4" | 
					
						
							|  |  |  |       /> | 
					
						
							|  |  |  |       <BaseButton | 
					
						
							|  |  |  |         create | 
					
						
							|  |  |  |         class="my-0" | 
					
						
							|  |  |  |         @click="createDialog = true" | 
					
						
							|  |  |  |       /> | 
					
						
							| 
									
										
										
										
											2024-02-23 17:07:43 +00:00
										 |  |  |     </v-container> | 
					
						
							| 
									
										
										
										
											2022-01-08 22:24:34 -09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-04 21:59:17 -07:00
										 |  |  |     <v-container v-if="!shoppingListChoices.length"> | 
					
						
							|  |  |  |       <BasePageTitle> | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         <template #title> | 
					
						
							|  |  |  |           {{ $t('shopping-list.no-shopping-lists-found') }} | 
					
						
							|  |  |  |         </template> | 
					
						
							| 
									
										
										
										
											2024-12-04 21:59:17 -07:00
										 |  |  |       </BasePageTitle> | 
					
						
							|  |  |  |     </v-container> | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-08 22:24:34 -09:00
										 |  |  |     <section> | 
					
						
							| 
									
										
										
										
											2024-02-23 17:07:43 +00:00
										 |  |  |       <v-card | 
					
						
							| 
									
										
										
										
											2024-02-23 19:22:39 +00:00
										 |  |  |         v-for="list in shoppingListChoices" | 
					
						
							| 
									
										
										
										
											2024-02-23 17:07:43 +00:00
										 |  |  |         :key="list.id" | 
					
						
							|  |  |  |         class="my-2 left-border" | 
					
						
							| 
									
										
										
										
											2024-02-23 18:58:47 +00:00
										 |  |  |         :to="`/shopping-lists/${list.id}`" | 
					
						
							|  |  |  |       > | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       <v-card-title class="d-flex align-center"> | 
					
						
							|  |  |  |         <v-icon class="mr-2"> | 
					
						
							|  |  |  |           {{ $globals.icons.cartCheck }} | 
					
						
							|  |  |  |         </v-icon> | 
					
						
							|  |  |  |         <span class="flex-grow-1"> | 
					
						
							|  |  |  |           {{ list.name }} | 
					
						
							|  |  |  |         </span> | 
					
						
							|  |  |  |         <v-btn | 
					
						
							|  |  |  |           icon | 
					
						
							|  |  |  |           variant="plain" | 
					
						
							|  |  |  |           @click.prevent="toggleOwnerDialog(list)" | 
					
						
							|  |  |  |         > | 
					
						
							|  |  |  |           <v-icon> | 
					
						
							|  |  |  |             {{ $globals.icons.user }} | 
					
						
							| 
									
										
										
										
											2022-01-08 22:24:34 -09:00
										 |  |  |           </v-icon> | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         </v-btn> | 
					
						
							|  |  |  |         <v-btn | 
					
						
							|  |  |  |           icon | 
					
						
							|  |  |  |           variant="plain" | 
					
						
							|  |  |  |           @click.prevent="openDelete(list.id)" | 
					
						
							|  |  |  |         > | 
					
						
							|  |  |  |           <v-icon> | 
					
						
							|  |  |  |             {{ $globals.icons.delete }} | 
					
						
							|  |  |  |           </v-icon> | 
					
						
							|  |  |  |         </v-btn> | 
					
						
							|  |  |  |       </v-card-title> | 
					
						
							| 
									
										
										
										
											2022-01-08 22:24:34 -09:00
										 |  |  |       </v-card> | 
					
						
							|  |  |  |     </section> | 
					
						
							|  |  |  |   </v-container> | 
					
						
							|  |  |  | </template> | 
					
						
							| 
									
										
										
										
											2022-03-19 11:31:17 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-08 22:24:34 -09:00
										 |  |  | <script lang="ts"> | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  | import type { ShoppingListOut } from "~/lib/api/types/household"; | 
					
						
							| 
									
										
										
										
											2022-01-08 22:24:34 -09:00
										 |  |  | import { useUserApi } from "~/composables/api"; | 
					
						
							|  |  |  | import { useAsyncKey } from "~/composables/use-utils"; | 
					
						
							| 
									
										
										
										
											2024-03-06 15:11:43 +00:00
										 |  |  | import { useShoppingListPreferences } from "~/composables/use-users/preferences"; | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  | import type { UserOut } from "~/lib/api/types/user"; | 
					
						
							| 
									
										
										
										
											2022-01-08 22:24:34 -09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  | export default defineNuxtComponent({ | 
					
						
							|  |  |  |   middleware: "sidebase-auth", | 
					
						
							| 
									
										
										
										
											2022-01-08 22:24:34 -09:00
										 |  |  |   setup() { | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     const $auth = useMealieAuth(); | 
					
						
							|  |  |  |     const i18n = useI18n(); | 
					
						
							| 
									
										
										
										
											2024-06-25 08:41:41 -05:00
										 |  |  |     const ready = ref(false); | 
					
						
							| 
									
										
										
										
											2022-01-08 22:24:34 -09:00
										 |  |  |     const userApi = useUserApi(); | 
					
						
							| 
									
										
										
										
											2023-11-05 19:07:02 -06:00
										 |  |  |     const route = useRoute(); | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     useSeoMeta({ | 
					
						
							|  |  |  |       title: i18n.t("shopping-list.shopping-list"), | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const groupSlug = computed(() => route.params.groupSlug || $auth.user.value?.groupSlug || ""); | 
					
						
							| 
									
										
										
										
											2024-06-25 08:41:41 -05:00
										 |  |  |     const overrideDisableRedirect = ref(false); | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     const disableRedirect = computed(() => route.query.disableRedirect === "true" || overrideDisableRedirect.value); | 
					
						
							| 
									
										
										
										
											2024-03-06 15:11:43 +00:00
										 |  |  |     const preferences = useShoppingListPreferences(); | 
					
						
							| 
									
										
										
										
											2022-01-08 22:24:34 -09:00
										 |  |  | 
 | 
					
						
							|  |  |  |     const state = reactive({ | 
					
						
							|  |  |  |       createName: "", | 
					
						
							|  |  |  |       createDialog: false, | 
					
						
							|  |  |  |       deleteDialog: false, | 
					
						
							|  |  |  |       deleteTarget: "", | 
					
						
							| 
									
										
										
										
											2025-01-26 08:04:40 -06:00
										 |  |  |       ownerDialog: false, | 
					
						
							|  |  |  |       ownerTarget: ref<ShoppingListOut | null>(null), | 
					
						
							| 
									
										
										
										
											2022-01-08 22:24:34 -09:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     const { data: shoppingLists } = useAsyncData(useAsyncKey(), async () => { | 
					
						
							| 
									
										
										
										
											2022-01-08 22:24:34 -09:00
										 |  |  |       return await fetchShoppingLists(); | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2022-01-08 22:24:34 -09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-23 19:22:39 +00:00
										 |  |  |     const shoppingListChoices = computed(() => { | 
					
						
							|  |  |  |       if (!shoppingLists.value) { | 
					
						
							|  |  |  |         return []; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       return shoppingLists.value.filter(list => preferences.value.viewAllLists || list.userId === $auth.user.value?.id); | 
					
						
							| 
									
										
										
										
											2024-02-23 19:22:39 +00:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-25 08:41:41 -05:00
										 |  |  |     // This has to appear before the shoppingListChoices watcher, otherwise that runs first and the redirect is not disabled
 | 
					
						
							|  |  |  |     watch( | 
					
						
							|  |  |  |       () => preferences.value.viewAllLists, | 
					
						
							|  |  |  |       () => { | 
					
						
							|  |  |  |         overrideDisableRedirect.value = true; | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     watch( | 
					
						
							|  |  |  |       () => shoppingListChoices, | 
					
						
							|  |  |  |       () => { | 
					
						
							|  |  |  |         if (!disableRedirect.value && shoppingListChoices.value.length === 1) { | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |           navigateTo(`/shopping-lists/${shoppingListChoices.value[0].id}`); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							| 
									
										
										
										
											2024-06-25 08:41:41 -05:00
										 |  |  |           ready.value = true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         deep: true, | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-08 22:24:34 -09:00
										 |  |  |     async function fetchShoppingLists() { | 
					
						
							| 
									
										
										
										
											2024-02-23 16:03:56 +00:00
										 |  |  |       const { data } = await userApi.shopping.lists.getAll(1, -1, { orderBy: "name", orderDirection: "asc" }); | 
					
						
							| 
									
										
										
										
											2022-06-25 14:39:38 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |       if (!data) { | 
					
						
							|  |  |  |         return []; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return data.items; | 
					
						
							| 
									
										
										
										
											2022-01-08 22:24:34 -09:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     async function refresh() { | 
					
						
							|  |  |  |       shoppingLists.value = await fetchShoppingLists(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     async function createOne() { | 
					
						
							|  |  |  |       const { data } = await userApi.shopping.lists.createOne({ name: state.createName }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (data) { | 
					
						
							|  |  |  |         refresh(); | 
					
						
							|  |  |  |         state.createName = ""; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-26 08:04:40 -06:00
										 |  |  |     async function toggleOwnerDialog(list: ShoppingListOut) { | 
					
						
							|  |  |  |       if (!state.ownerDialog) { | 
					
						
							|  |  |  |         state.ownerTarget = list; | 
					
						
							|  |  |  |         await fetchAllUsers(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       state.ownerDialog = !state.ownerDialog; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // ===============================================================
 | 
					
						
							|  |  |  |     // Shopping List Edit User/Owner
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const allUsers = ref<UserOut[]>([]); | 
					
						
							|  |  |  |     const updateUserId = ref<string | undefined>(); | 
					
						
							|  |  |  |     async function fetchAllUsers() { | 
					
						
							|  |  |  |       const { data } = await userApi.households.fetchMembers(); | 
					
						
							|  |  |  |       if (!data) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // update current user
 | 
					
						
							|  |  |  |       allUsers.value = data.items.sort((a, b) => ((a.fullName || "") < (b.fullName || "") ? -1 : 1)); | 
					
						
							|  |  |  |       updateUserId.value = state.ownerTarget?.userId; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     async function updateOwner() { | 
					
						
							|  |  |  |       if (!state.ownerTarget || !updateUserId.value) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       // user has not changed, so we should not update
 | 
					
						
							|  |  |  |       if (state.ownerTarget.userId === updateUserId.value) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       // get full list, so the move does not delete shopping list items
 | 
					
						
							|  |  |  |       const { data: fullList } = await userApi.shopping.lists.getOne(state.ownerTarget.id); | 
					
						
							|  |  |  |       if (!fullList) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       const { data } = await userApi.shopping.lists.updateOne( | 
					
						
							|  |  |  |         state.ownerTarget.id, | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         { ...fullList, userId: updateUserId.value }, | 
					
						
							| 
									
										
										
										
											2025-01-26 08:04:40 -06:00
										 |  |  |       ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (data) { | 
					
						
							|  |  |  |         refresh(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-08 22:24:34 -09:00
										 |  |  |     function openDelete(id: string) { | 
					
						
							|  |  |  |       state.deleteDialog = true; | 
					
						
							|  |  |  |       state.deleteTarget = id; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     async function deleteOne() { | 
					
						
							|  |  |  |       const { data } = await userApi.shopping.lists.deleteOne(state.deleteTarget); | 
					
						
							|  |  |  |       if (data) { | 
					
						
							|  |  |  |         refresh(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |       ...toRefs(state), | 
					
						
							| 
									
										
										
										
											2024-06-25 08:41:41 -05:00
										 |  |  |       ready, | 
					
						
							| 
									
										
										
										
											2023-11-05 19:07:02 -06:00
										 |  |  |       groupSlug, | 
					
						
							| 
									
										
										
										
											2024-03-06 15:11:43 +00:00
										 |  |  |       preferences, | 
					
						
							| 
									
										
										
										
											2024-02-23 19:22:39 +00:00
										 |  |  |       shoppingListChoices, | 
					
						
							| 
									
										
										
										
											2022-01-08 22:24:34 -09:00
										 |  |  |       createOne, | 
					
						
							| 
									
										
										
										
											2025-01-26 08:04:40 -06:00
										 |  |  |       toggleOwnerDialog, | 
					
						
							|  |  |  |       allUsers, | 
					
						
							|  |  |  |       updateUserId, | 
					
						
							|  |  |  |       updateOwner, | 
					
						
							| 
									
										
										
										
											2022-01-08 22:24:34 -09:00
										 |  |  |       deleteOne, | 
					
						
							|  |  |  |       openDelete, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | </script> |