| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  | <template> | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |   <v-container | 
					
						
							|  |  |  |     fluid | 
					
						
							| 
									
										
										
										
											2025-06-28 15:59:58 +02:00
										 |  |  |     class="px-0" | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |   > | 
					
						
							|  |  |  |     <div class="search-container pb-8"> | 
					
						
							|  |  |  |       <form | 
					
						
							|  |  |  |         class="search-box pa-2" | 
					
						
							|  |  |  |         @submit.prevent="search" | 
					
						
							|  |  |  |       > | 
					
						
							|  |  |  |         <div class="d-flex justify-center mb-2"> | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |           <v-text-field | 
					
						
							|  |  |  |             ref="input" | 
					
						
							|  |  |  |             v-model="state.search" | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |             variant="outlined" | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |             hide-details | 
					
						
							|  |  |  |             clearable | 
					
						
							|  |  |  |             color="primary" | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |             :placeholder="$t('search.search-placeholder')" | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |             :prepend-inner-icon="$globals.icons.search" | 
					
						
							|  |  |  |             @keyup.enter="hideKeyboard" | 
					
						
							|  |  |  |           /> | 
					
						
							|  |  |  |         </div> | 
					
						
							|  |  |  |         <div class="search-row"> | 
					
						
							|  |  |  |           <!-- Category Filter --> | 
					
						
							|  |  |  |           <SearchFilter | 
					
						
							|  |  |  |             v-if="categories" | 
					
						
							|  |  |  |             v-model="selectedCategories" | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |             v-model:require-all="state.requireAllCategories" | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |             :items="categories" | 
					
						
							|  |  |  |           > | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |             <v-icon start> | 
					
						
							| 
									
										
										
										
											2023-10-25 17:08:58 +02:00
										 |  |  |               {{ $globals.icons.categories }} | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |             </v-icon> | 
					
						
							|  |  |  |             {{ $t("category.categories") }} | 
					
						
							|  |  |  |           </SearchFilter> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           <!-- Tag Filter --> | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |           <SearchFilter | 
					
						
							|  |  |  |             v-if="tags" | 
					
						
							|  |  |  |             v-model="selectedTags" | 
					
						
							|  |  |  |             v-model:require-all="state.requireAllTags" | 
					
						
							|  |  |  |             :items="tags" | 
					
						
							|  |  |  |           > | 
					
						
							|  |  |  |             <v-icon start> | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |               {{ $globals.icons.tags }} | 
					
						
							|  |  |  |             </v-icon> | 
					
						
							|  |  |  |             {{ $t("tag.tags") }} | 
					
						
							|  |  |  |           </SearchFilter> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           <!-- Tool Filter --> | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |           <SearchFilter | 
					
						
							|  |  |  |             v-if="tools" | 
					
						
							|  |  |  |             v-model="selectedTools" | 
					
						
							|  |  |  |             v-model:require-all="state.requireAllTools" | 
					
						
							|  |  |  |             :items="tools" | 
					
						
							|  |  |  |           > | 
					
						
							|  |  |  |             <v-icon start> | 
					
						
							| 
									
										
										
										
											2023-10-25 17:08:58 +02:00
										 |  |  |               {{ $globals.icons.potSteam }} | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |             </v-icon> | 
					
						
							|  |  |  |             {{ $t("tool.tools") }} | 
					
						
							|  |  |  |           </SearchFilter> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           <!-- Food Filter --> | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |           <SearchFilter | 
					
						
							|  |  |  |             v-if="foods" | 
					
						
							|  |  |  |             v-model="selectedFoods" | 
					
						
							|  |  |  |             v-model:require-all="state.requireAllFoods" | 
					
						
							|  |  |  |             :items="foods" | 
					
						
							|  |  |  |           > | 
					
						
							|  |  |  |             <v-icon start> | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |               {{ $globals.icons.foods }} | 
					
						
							|  |  |  |             </v-icon> | 
					
						
							|  |  |  |             {{ $t("general.foods") }} | 
					
						
							|  |  |  |           </SearchFilter> | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-22 09:59:20 -05:00
										 |  |  |           <!-- Household Filter --> | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |           <SearchFilter | 
					
						
							|  |  |  |             v-if="households.length > 1" | 
					
						
							|  |  |  |             v-model="selectedHouseholds" | 
					
						
							|  |  |  |             :items="households" | 
					
						
							|  |  |  |             radio | 
					
						
							|  |  |  |           > | 
					
						
							|  |  |  |             <v-icon start> | 
					
						
							| 
									
										
										
										
											2024-09-22 09:59:20 -05:00
										 |  |  |               {{ $globals.icons.household }} | 
					
						
							|  |  |  |             </v-icon> | 
					
						
							|  |  |  |             {{ $t("household.households") }} | 
					
						
							|  |  |  |           </SearchFilter> | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |           <!-- Sort Options --> | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |           <v-menu | 
					
						
							|  |  |  |             offset-y | 
					
						
							|  |  |  |             nudge-bottom="3" | 
					
						
							|  |  |  |           > | 
					
						
							|  |  |  |             <template #activator="{ props }"> | 
					
						
							|  |  |  |               <v-btn | 
					
						
							|  |  |  |                 class="ml-auto" | 
					
						
							|  |  |  |                 size="small" | 
					
						
							|  |  |  |                 color="accent" | 
					
						
							|  |  |  |                 v-bind="props" | 
					
						
							|  |  |  |               > | 
					
						
							|  |  |  |                 <v-icon :start="!$vuetify.display.xs"> | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |                   {{ state.orderDirection === "asc" ? $globals.icons.sortAscending : $globals.icons.sortDescending }} | 
					
						
							|  |  |  |                 </v-icon> | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |                 {{ $vuetify.display.xs ? null : sortText }} | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |               </v-btn> | 
					
						
							|  |  |  |             </template> | 
					
						
							|  |  |  |             <v-card> | 
					
						
							|  |  |  |               <v-list> | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |                 <v-list-item | 
					
						
							|  |  |  |                   slim | 
					
						
							|  |  |  |                   density="comfortable" | 
					
						
							|  |  |  |                   :prepend-icon="state.orderDirection === 'asc' ? $globals.icons.sortDescending : $globals.icons.sortAscending" | 
					
						
							|  |  |  |                   :title="state.orderDirection === 'asc' ? $t('general.sort-descending') : $t('general.sort-ascending')" | 
					
						
							|  |  |  |                   @click="toggleOrderDirection()" | 
					
						
							|  |  |  |                 /> | 
					
						
							| 
									
										
										
										
											2024-03-12 10:20:48 -05:00
										 |  |  |                 <v-divider /> | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |                 <v-list-item | 
					
						
							|  |  |  |                   v-for="v in sortable" | 
					
						
							|  |  |  |                   :key="v.name" | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |                   :active="state.orderBy === v.value" | 
					
						
							|  |  |  |                   slim | 
					
						
							|  |  |  |                   density="comfortable" | 
					
						
							|  |  |  |                   :prepend-icon="v.icon" | 
					
						
							|  |  |  |                   :title="v.name" | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |                   @click="state.orderBy = v.value" | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |                 /> | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |               </v-list> | 
					
						
							|  |  |  |             </v-card> | 
					
						
							|  |  |  |           </v-menu> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           <!-- Settings --> | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |           <v-menu | 
					
						
							|  |  |  |             offset-y | 
					
						
							|  |  |  |             bottom | 
					
						
							|  |  |  |             start | 
					
						
							|  |  |  |             nudge-bottom="3" | 
					
						
							|  |  |  |             :close-on-content-click="false" | 
					
						
							|  |  |  |           > | 
					
						
							|  |  |  |             <template #activator="{ props }"> | 
					
						
							|  |  |  |               <v-btn | 
					
						
							|  |  |  |                 size="small" | 
					
						
							|  |  |  |                 color="accent" | 
					
						
							|  |  |  |                 dark | 
					
						
							|  |  |  |                 v-bind="props" | 
					
						
							|  |  |  |               > | 
					
						
							|  |  |  |                 <v-icon size="small"> | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |                   {{ $globals.icons.cog }} | 
					
						
							|  |  |  |                 </v-icon> | 
					
						
							|  |  |  |               </v-btn> | 
					
						
							|  |  |  |             </template> | 
					
						
							|  |  |  |             <v-card> | 
					
						
							|  |  |  |               <v-card-text> | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |                 <v-switch | 
					
						
							|  |  |  |                   v-model="state.auto" | 
					
						
							|  |  |  |                   :label="$t('search.auto-search')" | 
					
						
							|  |  |  |                   single-line | 
					
						
							|  |  |  |                 /> | 
					
						
							|  |  |  |                 <v-btn | 
					
						
							|  |  |  |                   block | 
					
						
							|  |  |  |                   color="primary" | 
					
						
							|  |  |  |                   @click="reset" | 
					
						
							|  |  |  |                 > | 
					
						
							|  |  |  |                   {{ $t("general.reset") }} | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |                 </v-btn> | 
					
						
							|  |  |  |               </v-card-text> | 
					
						
							|  |  |  |             </v-card> | 
					
						
							|  |  |  |           </v-menu> | 
					
						
							|  |  |  |         </div> | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         <div | 
					
						
							|  |  |  |           v-if="!state.auto" | 
					
						
							|  |  |  |           class="search-button-container" | 
					
						
							|  |  |  |         > | 
					
						
							|  |  |  |           <v-btn | 
					
						
							|  |  |  |             size="x-large" | 
					
						
							|  |  |  |             color="primary" | 
					
						
							|  |  |  |             type="submit" | 
					
						
							|  |  |  |             block | 
					
						
							|  |  |  |           > | 
					
						
							|  |  |  |             <v-icon start> | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |               {{ $globals.icons.search }} | 
					
						
							|  |  |  |             </v-icon> | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |             {{ $t("search.search") }} | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |           </v-btn> | 
					
						
							|  |  |  |         </div> | 
					
						
							|  |  |  |       </form> | 
					
						
							|  |  |  |     </div> | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     <v-divider /> | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |     <v-container class="mt-6 px-md-6"> | 
					
						
							|  |  |  |       <RecipeCardSection | 
					
						
							| 
									
										
										
										
											2024-03-25 11:04:42 -05:00
										 |  |  |         v-if="state.ready" | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |         class="mt-n5" | 
					
						
							| 
									
										
										
										
											2024-07-28 00:08:34 -04:00
										 |  |  |         :icon="$globals.icons.silverwareForkKnife" | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         :title="$t('general.recipes')" | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |         :recipes="recipes" | 
					
						
							| 
									
										
										
										
											2024-03-25 11:04:42 -05:00
										 |  |  |         :query="passedQueryWithSeed" | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         disable-sort | 
					
						
							| 
									
										
										
										
											2025-01-14 09:18:16 -08:00
										 |  |  |         @item-selected="filterItems" | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         @replace-recipes="replaceRecipes" | 
					
						
							|  |  |  |         @append-recipes="appendRecipes" | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |       /> | 
					
						
							|  |  |  |     </v-container> | 
					
						
							|  |  |  |   </v-container> | 
					
						
							|  |  |  | </template> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <script lang="ts"> | 
					
						
							|  |  |  | import { watchDebounced } from "@vueuse/shared"; | 
					
						
							|  |  |  | import SearchFilter from "~/components/Domain/SearchFilter.vue"; | 
					
						
							| 
									
										
										
										
											2023-11-05 19:07:02 -06:00
										 |  |  | import { useLoggedInState } from "~/composables/use-logged-in-state"; | 
					
						
							| 
									
										
										
										
											2024-09-22 09:59:20 -05:00
										 |  |  | import { | 
					
						
							|  |  |  |   useCategoryStore, | 
					
						
							|  |  |  |   usePublicCategoryStore, | 
					
						
							|  |  |  |   useFoodStore, | 
					
						
							|  |  |  |   usePublicFoodStore, | 
					
						
							|  |  |  |   useHouseholdStore, | 
					
						
							|  |  |  |   usePublicHouseholdStore, | 
					
						
							|  |  |  |   useTagStore, | 
					
						
							|  |  |  |   usePublicTagStore, | 
					
						
							|  |  |  |   useToolStore, | 
					
						
							|  |  |  |   usePublicToolStore, | 
					
						
							|  |  |  | } from "~/composables/store"; | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  | import { useUserSearchQuerySession } from "~/composables/use-users/preferences"; | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  | import RecipeCardSection from "~/components/Domain/Recipe/RecipeCardSection.vue"; | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  | import type { IngredientFood, RecipeCategory, RecipeTag, RecipeTool } from "~/lib/api/types/recipe"; | 
					
						
							|  |  |  | import type { NoUndefinedField } from "~/lib/api/types/non-generated"; | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  | import { useLazyRecipes } from "~/composables/recipes"; | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  | import type { RecipeSearchQuery } from "~/lib/api/user/recipes/recipe"; | 
					
						
							|  |  |  | import type { HouseholdSummary } from "~/lib/api/types/household"; | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  | export default defineNuxtComponent({ | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |   components: { SearchFilter, RecipeCardSection }, | 
					
						
							| 
									
										
										
										
											2023-11-05 19:07:02 -06:00
										 |  |  |   setup() { | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |     const router = useRouter(); | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     const i18n = useI18n(); | 
					
						
							|  |  |  |     const $auth = useMealieAuth(); | 
					
						
							|  |  |  |     const { $globals } = useNuxtApp(); | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-05 19:07:02 -06:00
										 |  |  |     const { isOwnGroup } = useLoggedInState(); | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |     const state = ref({ | 
					
						
							|  |  |  |       auto: true, | 
					
						
							| 
									
										
										
										
											2024-03-25 11:04:42 -05:00
										 |  |  |       ready: false, | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |       search: "", | 
					
						
							|  |  |  |       orderBy: "created_at", | 
					
						
							|  |  |  |       orderDirection: "desc" as "asc" | "desc", | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // and/or
 | 
					
						
							|  |  |  |       requireAllCategories: false, | 
					
						
							|  |  |  |       requireAllTags: false, | 
					
						
							|  |  |  |       requireAllTools: false, | 
					
						
							|  |  |  |       requireAllFoods: false, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-05 19:07:02 -06:00
										 |  |  |     const route = useRoute(); | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     const groupSlug = computed(() => route.params.groupSlug as string || $auth.user.value?.groupSlug || ""); | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |     const searchQuerySession = useUserSearchQuerySession(); | 
					
						
							| 
									
										
										
										
											2023-11-05 19:07:02 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     const { recipes, appendRecipes, assignSorted, removeRecipe, replaceRecipes } = useLazyRecipes(isOwnGroup.value ? null : groupSlug.value); | 
					
						
							|  |  |  |     const categories = isOwnGroup.value ? useCategoryStore() : usePublicCategoryStore(groupSlug.value); | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |     const selectedCategories = ref<NoUndefinedField<RecipeCategory>[]>([]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-05 19:07:02 -06:00
										 |  |  |     const foods = isOwnGroup.value ? useFoodStore() : usePublicFoodStore(groupSlug.value); | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |     const selectedFoods = ref<IngredientFood[]>([]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-22 09:59:20 -05:00
										 |  |  |     const households = isOwnGroup.value ? useHouseholdStore() : usePublicHouseholdStore(groupSlug.value); | 
					
						
							|  |  |  |     const selectedHouseholds = ref([] as NoUndefinedField<HouseholdSummary>[]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-05 19:07:02 -06:00
										 |  |  |     const tags = isOwnGroup.value ? useTagStore() : usePublicTagStore(groupSlug.value); | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |     const selectedTags = ref<NoUndefinedField<RecipeTag>[]>([]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-05 19:07:02 -06:00
										 |  |  |     const tools = isOwnGroup.value ? useToolStore() : usePublicToolStore(groupSlug.value); | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |     const selectedTools = ref<NoUndefinedField<RecipeTool>[]>([]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-25 11:04:42 -05:00
										 |  |  |     function calcPassedQuery(): RecipeSearchQuery { | 
					
						
							|  |  |  |       return { | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |         // the search clear button sets search to null, which still renders the query param for a moment,
 | 
					
						
							|  |  |  |         // whereas an empty string is not rendered
 | 
					
						
							|  |  |  |         search: state.value.search ? state.value.search : "", | 
					
						
							| 
									
										
										
										
											2024-03-25 11:04:42 -05:00
										 |  |  |         categories: toIDArray(selectedCategories.value), | 
					
						
							|  |  |  |         foods: toIDArray(selectedFoods.value), | 
					
						
							| 
									
										
										
										
											2024-09-22 09:59:20 -05:00
										 |  |  |         households: toIDArray(selectedHouseholds.value), | 
					
						
							| 
									
										
										
										
											2024-03-25 11:04:42 -05:00
										 |  |  |         tags: toIDArray(selectedTags.value), | 
					
						
							|  |  |  |         tools: toIDArray(selectedTools.value), | 
					
						
							|  |  |  |         requireAllCategories: state.value.requireAllCategories, | 
					
						
							|  |  |  |         requireAllTags: state.value.requireAllTags, | 
					
						
							|  |  |  |         requireAllTools: state.value.requireAllTools, | 
					
						
							|  |  |  |         requireAllFoods: state.value.requireAllFoods, | 
					
						
							|  |  |  |         orderBy: state.value.orderBy, | 
					
						
							|  |  |  |         orderDirection: state.value.orderDirection, | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     const passedQuery = ref<RecipeSearchQuery>(calcPassedQuery()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // we calculate this separately because otherwise we can't check for query changes
 | 
					
						
							|  |  |  |     const passedQueryWithSeed = computed(() => { | 
					
						
							|  |  |  |       return { | 
					
						
							|  |  |  |         ...passedQuery.value, | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         _searchSeed: Date.now().toString(), | 
					
						
							| 
									
										
										
										
											2024-03-25 11:04:42 -05:00
										 |  |  |       }; | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |     const queryDefaults = { | 
					
						
							|  |  |  |       search: "", | 
					
						
							|  |  |  |       orderBy: "created_at", | 
					
						
							|  |  |  |       orderDirection: "desc" as "asc" | "desc", | 
					
						
							|  |  |  |       requireAllCategories: false, | 
					
						
							|  |  |  |       requireAllTags: false, | 
					
						
							|  |  |  |       requireAllTools: false, | 
					
						
							|  |  |  |       requireAllFoods: false, | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     }; | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |     function reset() { | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |       state.value.search = queryDefaults.search; | 
					
						
							|  |  |  |       state.value.orderBy = queryDefaults.orderBy; | 
					
						
							|  |  |  |       state.value.orderDirection = queryDefaults.orderDirection; | 
					
						
							|  |  |  |       state.value.requireAllCategories = queryDefaults.requireAllCategories; | 
					
						
							|  |  |  |       state.value.requireAllTags = queryDefaults.requireAllTags; | 
					
						
							|  |  |  |       state.value.requireAllTools = queryDefaults.requireAllTools; | 
					
						
							|  |  |  |       state.value.requireAllFoods = queryDefaults.requireAllFoods; | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |       selectedCategories.value = []; | 
					
						
							|  |  |  |       selectedFoods.value = []; | 
					
						
							| 
									
										
										
										
											2024-09-22 09:59:20 -05:00
										 |  |  |       selectedHouseholds.value = []; | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |       selectedTags.value = []; | 
					
						
							|  |  |  |       selectedTools.value = []; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function toggleOrderDirection() { | 
					
						
							|  |  |  |       state.value.orderDirection = state.value.orderDirection === "asc" ? "desc" : "asc"; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function toIDArray(array: { id: string }[]) { | 
					
						
							| 
									
										
										
										
											2024-03-25 11:04:42 -05:00
										 |  |  |       // we sort the array to make sure the query is always the same
 | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       return array.map(item => item.id).sort(); | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function hideKeyboard() { | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       input.value.blur(); | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const input: Ref<any> = ref(null); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     async function search() { | 
					
						
							| 
									
										
										
										
											2024-03-25 11:04:42 -05:00
										 |  |  |       const oldQueryValueString = JSON.stringify(passedQuery.value); | 
					
						
							|  |  |  |       const newQueryValue = calcPassedQuery(); | 
					
						
							|  |  |  |       const newQueryValueString = JSON.stringify(newQueryValue); | 
					
						
							|  |  |  |       if (oldQueryValueString === newQueryValueString) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-25 11:04:42 -05:00
										 |  |  |       passedQuery.value = newQueryValue; | 
					
						
							|  |  |  |       const query = { | 
					
						
							|  |  |  |         categories: passedQuery.value.categories, | 
					
						
							|  |  |  |         foods: passedQuery.value.foods, | 
					
						
							|  |  |  |         tags: passedQuery.value.tags, | 
					
						
							|  |  |  |         tools: passedQuery.value.tools, | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |         // Only add the query param if it's not the default value
 | 
					
						
							| 
									
										
										
										
											2024-03-25 11:04:42 -05:00
										 |  |  |         ...{ | 
					
						
							|  |  |  |           auto: state.value.auto ? undefined : "false", | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |           search: passedQuery.value.search === queryDefaults.search ? undefined : passedQuery.value.search, | 
					
						
							|  |  |  |           orderBy: passedQuery.value.orderBy === queryDefaults.orderBy ? undefined : passedQuery.value.orderBy, | 
					
						
							|  |  |  |           orderDirection: passedQuery.value.orderDirection === queryDefaults.orderDirection ? undefined : passedQuery.value.orderDirection, | 
					
						
							| 
									
										
										
										
											2024-09-22 09:59:20 -05:00
										 |  |  |           households: !passedQuery.value.households?.length || passedQuery.value.households?.length === households.store.value.length ? undefined : passedQuery.value.households, | 
					
						
							| 
									
										
										
										
											2024-03-25 11:04:42 -05:00
										 |  |  |           requireAllCategories: passedQuery.value.requireAllCategories ? "true" : undefined, | 
					
						
							|  |  |  |           requireAllTags: passedQuery.value.requireAllTags ? "true" : undefined, | 
					
						
							|  |  |  |           requireAllTools: passedQuery.value.requireAllTools ? "true" : undefined, | 
					
						
							|  |  |  |           requireAllFoods: passedQuery.value.requireAllFoods ? "true" : undefined, | 
					
						
							|  |  |  |         }, | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       }; | 
					
						
							| 
									
										
										
										
											2024-03-25 11:04:42 -05:00
										 |  |  |       await router.push({ query }); | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |       searchQuerySession.value.recipe = JSON.stringify(query); | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function waitUntilAndExecute( | 
					
						
							|  |  |  |       condition: () => boolean, | 
					
						
							|  |  |  |       callback: () => void, | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       opts = { timeout: 2000, interval: 500 }, | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |     ): Promise<void> { | 
					
						
							|  |  |  |       return new Promise((resolve, reject) => { | 
					
						
							|  |  |  |         const state = { | 
					
						
							|  |  |  |           timeout: undefined as number | undefined, | 
					
						
							|  |  |  |           interval: undefined as number | undefined, | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const check = () => { | 
					
						
							|  |  |  |           if (condition()) { | 
					
						
							|  |  |  |             clearInterval(state.interval); | 
					
						
							|  |  |  |             clearTimeout(state.timeout); | 
					
						
							|  |  |  |             callback(); | 
					
						
							|  |  |  |             resolve(); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // For some reason these were returning NodeJS.Timeout
 | 
					
						
							|  |  |  |         state.interval = setInterval(check, opts.interval) as unknown as number; | 
					
						
							|  |  |  |         state.timeout = setTimeout(() => { | 
					
						
							|  |  |  |           clearInterval(state.interval); | 
					
						
							|  |  |  |           reject(new Error("Timeout")); | 
					
						
							|  |  |  |         }, opts.timeout) as unknown as number; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const sortText = computed(() => { | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       const sort = sortable.find(s => s.value === state.value.orderBy); | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |       if (!sort) return ""; | 
					
						
							|  |  |  |       return `${sort.name}`; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const sortable = [ | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         icon: $globals.icons.orderAlphabeticalAscending, | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         name: i18n.t("general.sort-alphabetically"), | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |         value: "name", | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         icon: $globals.icons.newBox, | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         name: i18n.t("general.created"), | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |         value: "created_at", | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         icon: $globals.icons.chefHat, | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         name: i18n.t("general.last-made"), | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |         value: "last_made", | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         icon: $globals.icons.star, | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         name: i18n.t("general.rating"), | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |         value: "rating", | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         icon: $globals.icons.update, | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         name: i18n.t("general.updated"), | 
					
						
							| 
									
										
										
										
											2024-08-22 10:14:32 -05:00
										 |  |  |         value: "updated_at", | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         icon: $globals.icons.diceMultiple, | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         name: i18n.t("general.random"), | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |         value: "random", | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     ]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-25 11:04:42 -05:00
										 |  |  |     watch( | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       () => route.query, | 
					
						
							| 
									
										
										
										
											2024-03-25 11:04:42 -05:00
										 |  |  |       () => { | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         if (!Object.keys(route.query).length) { | 
					
						
							| 
									
										
										
										
											2024-09-22 09:59:20 -05:00
										 |  |  |           reset(); | 
					
						
							| 
									
										
										
										
											2024-03-25 11:04:42 -05:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       }, | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-14 09:18:16 -08:00
										 |  |  |     function filterItems(item: RecipeCategory | RecipeTag | RecipeTool, urlPrefix: string) { | 
					
						
							|  |  |  |       if (urlPrefix === "categories") { | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         const result = categories.store.value.filter(category => (category.id as string).includes(item.id as string)); | 
					
						
							| 
									
										
										
										
											2025-01-14 09:18:16 -08:00
										 |  |  |         selectedCategories.value = result as NoUndefinedField<RecipeTag>[]; | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       } | 
					
						
							|  |  |  |       else if (urlPrefix === "tags") { | 
					
						
							|  |  |  |         const result = tags.store.value.filter(tag => (tag.id as string).includes(item.id as string)); | 
					
						
							| 
									
										
										
										
											2025-01-14 09:18:16 -08:00
										 |  |  |         selectedTags.value = result as NoUndefinedField<RecipeTag>[]; | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       } | 
					
						
							|  |  |  |       else if (urlPrefix === "tools") { | 
					
						
							|  |  |  |         const result = tools.store.value.filter(tool => (tool.id).includes(item.id || "")); | 
					
						
							| 
									
										
										
										
											2025-01-14 09:18:16 -08:00
										 |  |  |         selectedTags.value = result as NoUndefinedField<RecipeTag>[]; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-25 11:04:42 -05:00
										 |  |  |     async function hydrateSearch() { | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       const query = router.currentRoute.value.query; | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |       if (query.auto?.length) { | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |         state.value.auto = query.auto === "true"; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |       if (query.search?.length) { | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |         state.value.search = query.search as string; | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       } | 
					
						
							|  |  |  |       else { | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |         state.value.search = queryDefaults.search; | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |       if (query.orderBy?.length) { | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |         state.value.orderBy = query.orderBy as string; | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       } | 
					
						
							|  |  |  |       else { | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |         state.value.orderBy = queryDefaults.orderBy; | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |       if (query.orderDirection?.length) { | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |         state.value.orderDirection = query.orderDirection as "asc" | "desc"; | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       } | 
					
						
							|  |  |  |       else { | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |         state.value.orderDirection = queryDefaults.orderDirection; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (query.requireAllCategories?.length) { | 
					
						
							|  |  |  |         state.value.requireAllCategories = query.requireAllCategories === "true"; | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       } | 
					
						
							|  |  |  |       else { | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |         state.value.requireAllCategories = queryDefaults.requireAllCategories; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (query.requireAllTags?.length) { | 
					
						
							|  |  |  |         state.value.requireAllTags = query.requireAllTags === "true"; | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       } | 
					
						
							|  |  |  |       else { | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |         state.value.requireAllTags = queryDefaults.requireAllTags; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (query.requireAllTools?.length) { | 
					
						
							|  |  |  |         state.value.requireAllTools = query.requireAllTools === "true"; | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       } | 
					
						
							|  |  |  |       else { | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |         state.value.requireAllTools = queryDefaults.requireAllTools; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (query.requireAllFoods?.length) { | 
					
						
							|  |  |  |         state.value.requireAllFoods = query.requireAllFoods === "true"; | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       } | 
					
						
							|  |  |  |       else { | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |         state.value.requireAllFoods = queryDefaults.requireAllFoods; | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const promises: Promise<void>[] = []; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |       if (query.categories?.length) { | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |         promises.push( | 
					
						
							|  |  |  |           waitUntilAndExecute( | 
					
						
							| 
									
										
										
										
											2024-09-22 09:59:20 -05:00
										 |  |  |             () => categories.store.value.length > 0, | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |             () => { | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |               const result = categories.store.value.filter(item => | 
					
						
							|  |  |  |                 (query.categories as string[]).includes(item.id as string), | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |               ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               selectedCategories.value = result as NoUndefinedField<RecipeCategory>[]; | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |             }, | 
					
						
							|  |  |  |           ), | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |         ); | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       } | 
					
						
							|  |  |  |       else { | 
					
						
							| 
									
										
										
										
											2024-03-25 11:04:42 -05:00
										 |  |  |         selectedCategories.value = []; | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |       if (query.tags?.length) { | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |         promises.push( | 
					
						
							|  |  |  |           waitUntilAndExecute( | 
					
						
							| 
									
										
										
										
											2024-09-22 09:59:20 -05:00
										 |  |  |             () => tags.store.value.length > 0, | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |             () => { | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |               const result = tags.store.value.filter(item => (query.tags as string[]).includes(item.id as string)); | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |               selectedTags.value = result as NoUndefinedField<RecipeTag>[]; | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |             }, | 
					
						
							|  |  |  |           ), | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |         ); | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       } | 
					
						
							|  |  |  |       else { | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |         selectedTags.value = []; | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |       if (query.tools?.length) { | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |         promises.push( | 
					
						
							|  |  |  |           waitUntilAndExecute( | 
					
						
							| 
									
										
										
										
											2024-09-22 09:59:20 -05:00
										 |  |  |             () => tools.store.value.length > 0, | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |             () => { | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |               const result = tools.store.value.filter(item => (query.tools as string[]).includes(item.id)); | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |               selectedTools.value = result as NoUndefinedField<RecipeTool>[]; | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |             }, | 
					
						
							|  |  |  |           ), | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |         ); | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       } | 
					
						
							|  |  |  |       else { | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |         selectedTools.value = []; | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |       if (query.foods?.length) { | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |         promises.push( | 
					
						
							|  |  |  |           waitUntilAndExecute( | 
					
						
							|  |  |  |             () => { | 
					
						
							| 
									
										
										
										
											2024-09-22 09:59:20 -05:00
										 |  |  |               if (foods.store.value) { | 
					
						
							|  |  |  |                 return foods.store.value.length > 0; | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |               } | 
					
						
							|  |  |  |               return false; | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             () => { | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |               const result = foods.store.value?.filter(item => (query.foods as string[]).includes(item.id)); | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |               selectedFoods.value = result ?? []; | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |             }, | 
					
						
							|  |  |  |           ), | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |         ); | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       } | 
					
						
							|  |  |  |       else { | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |         selectedFoods.value = []; | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-22 09:59:20 -05:00
										 |  |  |       if (query.households?.length) { | 
					
						
							|  |  |  |         promises.push( | 
					
						
							|  |  |  |           waitUntilAndExecute( | 
					
						
							|  |  |  |             () => { | 
					
						
							|  |  |  |               if (households.store.value) { | 
					
						
							|  |  |  |                 return households.store.value.length > 0; | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |               return false; | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             () => { | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |               const result = households.store.value?.filter(item => (query.households as string[]).includes(item.id)); | 
					
						
							| 
									
										
										
										
											2024-09-22 09:59:20 -05:00
										 |  |  |               selectedHouseholds.value = result as NoUndefinedField<HouseholdSummary>[] ?? []; | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |             }, | 
					
						
							|  |  |  |           ), | 
					
						
							| 
									
										
										
										
											2024-09-22 09:59:20 -05:00
										 |  |  |         ); | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       } | 
					
						
							|  |  |  |       else { | 
					
						
							| 
									
										
										
										
											2024-09-22 09:59:20 -05:00
										 |  |  |         selectedHouseholds.value = []; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-25 11:04:42 -05:00
										 |  |  |       await Promise.allSettled(promises); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     onMounted(async () => { | 
					
						
							|  |  |  |       // restore the user's last search query
 | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       if (searchQuerySession.value.recipe && !(Object.keys(route.query).length > 0)) { | 
					
						
							| 
									
										
										
										
											2024-03-25 11:04:42 -05:00
										 |  |  |         try { | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |           const query = JSON.parse(searchQuerySession.value.recipe); | 
					
						
							| 
									
										
										
										
											2024-03-25 11:04:42 -05:00
										 |  |  |           await router.replace({ query }); | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         } | 
					
						
							|  |  |  |         catch { | 
					
						
							| 
									
										
										
										
											2024-05-06 10:01:56 -05:00
										 |  |  |           searchQuerySession.value.recipe = ""; | 
					
						
							| 
									
										
										
										
											2024-03-25 11:04:42 -05:00
										 |  |  |           router.replace({ query: {} }); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       await hydrateSearch(); | 
					
						
							|  |  |  |       await search(); | 
					
						
							|  |  |  |       state.value.ready = true; | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     watchDebounced( | 
					
						
							|  |  |  |       [ | 
					
						
							|  |  |  |         () => state.value.search, | 
					
						
							|  |  |  |         () => state.value.requireAllCategories, | 
					
						
							|  |  |  |         () => state.value.requireAllTags, | 
					
						
							|  |  |  |         () => state.value.requireAllTools, | 
					
						
							|  |  |  |         () => state.value.requireAllFoods, | 
					
						
							|  |  |  |         () => state.value.orderBy, | 
					
						
							|  |  |  |         () => state.value.orderDirection, | 
					
						
							|  |  |  |         selectedCategories, | 
					
						
							|  |  |  |         selectedFoods, | 
					
						
							| 
									
										
										
										
											2024-09-22 09:59:20 -05:00
										 |  |  |         selectedHouseholds, | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |         selectedTags, | 
					
						
							|  |  |  |         selectedTools, | 
					
						
							|  |  |  |       ], | 
					
						
							|  |  |  |       async () => { | 
					
						
							| 
									
										
										
										
											2024-03-25 11:04:42 -05:00
										 |  |  |         if (state.value.ready && state.value.auto) { | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |           await search(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         debounce: 500, | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |       }, | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |     ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |       sortText, | 
					
						
							|  |  |  |       search, | 
					
						
							|  |  |  |       reset, | 
					
						
							|  |  |  |       state, | 
					
						
							| 
									
										
										
										
											2024-09-22 09:59:20 -05:00
										 |  |  |       categories: categories.store as unknown as NoUndefinedField<RecipeCategory>[], | 
					
						
							|  |  |  |       tags: tags.store as unknown as NoUndefinedField<RecipeTag>[], | 
					
						
							|  |  |  |       foods: foods.store, | 
					
						
							|  |  |  |       tools: tools.store as unknown as NoUndefinedField<RecipeTool>[], | 
					
						
							|  |  |  |       households: households.store as unknown as NoUndefinedField<HouseholdSummary>[], | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |       sortable, | 
					
						
							|  |  |  |       toggleOrderDirection, | 
					
						
							|  |  |  |       hideKeyboard, | 
					
						
							|  |  |  |       input, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       selectedCategories, | 
					
						
							|  |  |  |       selectedFoods, | 
					
						
							| 
									
										
										
										
											2024-09-22 09:59:20 -05:00
										 |  |  |       selectedHouseholds, | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |       selectedTags, | 
					
						
							|  |  |  |       selectedTools, | 
					
						
							|  |  |  |       appendRecipes, | 
					
						
							|  |  |  |       assignSorted, | 
					
						
							|  |  |  |       recipes, | 
					
						
							|  |  |  |       removeRecipe, | 
					
						
							|  |  |  |       replaceRecipes, | 
					
						
							| 
									
										
										
										
											2024-03-25 11:04:42 -05:00
										 |  |  |       passedQueryWithSeed, | 
					
						
							| 
									
										
										
										
											2025-01-14 09:18:16 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |       filterItems, | 
					
						
							| 
									
										
										
										
											2023-09-14 09:01:24 -05:00
										 |  |  |     }; | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | </script> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <style lang="css"> | 
					
						
							|  |  |  | .search-row { | 
					
						
							|  |  |  |   display: flex; | 
					
						
							|  |  |  |   flex-wrap: wrap; | 
					
						
							|  |  |  |   gap: 0.65rem; | 
					
						
							|  |  |  |   margin-top: 1rem; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .search-container { | 
					
						
							|  |  |  |   display: flex; | 
					
						
							|  |  |  |   justify-content: center; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .search-box { | 
					
						
							|  |  |  |   width: 950px; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .search-button-container { | 
					
						
							|  |  |  |   margin: 3rem auto 0 auto; | 
					
						
							|  |  |  |   max-width: 500px; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | </style> |