| 
									
										
										
										
											2022-04-01 09:50:31 -08:00
										 |  |  | <template> | 
					
						
							|  |  |  |   <v-autocomplete | 
					
						
							|  |  |  |     v-model="selected" | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     v-bind="inputAttrs" | 
					
						
							|  |  |  |     v-model:search="searchInput" | 
					
						
							| 
									
										
										
										
											2025-07-29 23:43:13 +02:00
										 |  |  |     :items="items" | 
					
						
							| 
									
										
										
										
											2022-04-01 09:50:31 -08:00
										 |  |  |     :label="label" | 
					
						
							|  |  |  |     chips | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     closable-chips | 
					
						
							|  |  |  |     item-title="name" | 
					
						
							| 
									
										
										
										
											2022-04-01 09:50:31 -08:00
										 |  |  |     multiple | 
					
						
							| 
									
										
										
										
											2025-06-28 15:59:58 +02:00
										 |  |  |     :variant="variant" | 
					
						
							| 
									
										
										
										
											2024-10-17 10:35:39 -05:00
										 |  |  |     :prepend-inner-icon="icon" | 
					
						
							| 
									
										
										
										
											2025-06-28 15:59:58 +02:00
										 |  |  |     :append-icon="showAdd ? $globals.icons.create : undefined" | 
					
						
							| 
									
										
										
										
											2022-04-01 09:50:31 -08:00
										 |  |  |     return-object | 
					
						
							| 
									
										
										
										
											2024-03-26 14:02:20 +01:00
										 |  |  |     auto-select-first | 
					
						
							| 
									
										
										
										
											2024-10-17 10:35:39 -05:00
										 |  |  |     class="pa-0" | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     @update:model-value="resetSearchInput" | 
					
						
							|  |  |  |     @click:append="dialog = true" | 
					
						
							| 
									
										
										
										
											2022-04-01 09:50:31 -08:00
										 |  |  |   > | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |     <template #chip="{ item, index }"> | 
					
						
							| 
									
										
										
										
											2022-04-01 09:50:31 -08:00
										 |  |  |       <v-chip | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         :key="index" | 
					
						
							| 
									
										
										
										
											2022-04-01 09:50:31 -08:00
										 |  |  |         class="ma-1" | 
					
						
							|  |  |  |         color="accent" | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         variant="flat" | 
					
						
							|  |  |  |         label | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         closable | 
					
						
							|  |  |  |         @click:close="removeByIndex(index)" | 
					
						
							| 
									
										
										
										
											2022-04-01 09:50:31 -08:00
										 |  |  |       > | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  |         {{ item.value }} | 
					
						
							| 
									
										
										
										
											2022-04-01 09:50:31 -08:00
										 |  |  |       </v-chip> | 
					
						
							|  |  |  |     </template> | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     <template | 
					
						
							|  |  |  |       v-if="showAdd" | 
					
						
							|  |  |  |       #append | 
					
						
							|  |  |  |     > | 
					
						
							|  |  |  |       <RecipeOrganizerDialog | 
					
						
							|  |  |  |         v-model="dialog" | 
					
						
							|  |  |  |         :item-type="selectorType" | 
					
						
							|  |  |  |         @created-item="appendCreated" | 
					
						
							|  |  |  |       /> | 
					
						
							| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  |     </template> | 
					
						
							| 
									
										
										
										
											2022-04-01 09:50:31 -08:00
										 |  |  |   </v-autocomplete> | 
					
						
							|  |  |  | </template> | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-29 23:43:13 +02:00
										 |  |  | <script setup lang="ts"> | 
					
						
							| 
									
										
										
										
											2025-06-20 00:09:12 +07:00
										 |  |  | import type { IngredientFood, RecipeCategory, RecipeTag } from "~/lib/api/types/recipe"; | 
					
						
							|  |  |  | import type { RecipeTool } from "~/lib/api/types/admin"; | 
					
						
							|  |  |  | import { Organizer, type RecipeOrganizer } from "~/lib/api/types/non-generated"; | 
					
						
							|  |  |  | import type { HouseholdSummary } from "~/lib/api/types/household"; | 
					
						
							| 
									
										
										
										
											2024-10-17 10:35:39 -05:00
										 |  |  | import { useCategoryStore, useFoodStore, useHouseholdStore, useTagStore, useToolStore } from "~/composables/store"; | 
					
						
							| 
									
										
										
										
											2022-04-01 09:50:31 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-29 23:43:13 +02:00
										 |  |  | interface Props { | 
					
						
							|  |  |  |   selectorType: RecipeOrganizer; | 
					
						
							|  |  |  |   inputAttrs?: Record<string, any>; | 
					
						
							|  |  |  |   returnObject?: boolean; | 
					
						
							|  |  |  |   showAdd?: boolean; | 
					
						
							|  |  |  |   showLabel?: boolean; | 
					
						
							|  |  |  |   showIcon?: boolean; | 
					
						
							|  |  |  |   variant?: "filled" | "underlined" | "outlined" | "plain" | "solo" | "solo-inverted" | "solo-filled"; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const props = withDefaults(defineProps<Props>(), { | 
					
						
							|  |  |  |   inputAttrs: () => ({}), | 
					
						
							|  |  |  |   returnObject: true, | 
					
						
							|  |  |  |   showAdd: true, | 
					
						
							|  |  |  |   showLabel: true, | 
					
						
							|  |  |  |   showIcon: true, | 
					
						
							|  |  |  |   variant: "outlined", | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const selected = defineModel<( | 
					
						
							|  |  |  |   | HouseholdSummary | 
					
						
							|  |  |  |   | RecipeTag | 
					
						
							|  |  |  |   | RecipeCategory | 
					
						
							|  |  |  |   | RecipeTool | 
					
						
							|  |  |  |   | IngredientFood | 
					
						
							|  |  |  |   | string | 
					
						
							|  |  |  | )[] | undefined>({ required: true }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | onMounted(() => { | 
					
						
							|  |  |  |   if (selected.value === undefined) { | 
					
						
							|  |  |  |     selected.value = []; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const i18n = useI18n(); | 
					
						
							|  |  |  | const { $globals } = useNuxtApp(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const label = computed(() => { | 
					
						
							|  |  |  |   if (!props.showLabel) { | 
					
						
							|  |  |  |     return ""; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   switch (props.selectorType) { | 
					
						
							|  |  |  |     case Organizer.Tag: | 
					
						
							|  |  |  |       return i18n.t("tag.tags"); | 
					
						
							|  |  |  |     case Organizer.Category: | 
					
						
							|  |  |  |       return i18n.t("category.categories"); | 
					
						
							|  |  |  |     case Organizer.Tool: | 
					
						
							|  |  |  |       return i18n.t("tool.tools"); | 
					
						
							|  |  |  |     case Organizer.Food: | 
					
						
							|  |  |  |       return i18n.t("general.foods"); | 
					
						
							|  |  |  |     case Organizer.Household: | 
					
						
							|  |  |  |       return i18n.t("household.households"); | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |       return i18n.t("general.organizer"); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const icon = computed(() => { | 
					
						
							|  |  |  |   if (!props.showIcon) { | 
					
						
							|  |  |  |     return ""; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   switch (props.selectorType) { | 
					
						
							|  |  |  |     case Organizer.Tag: | 
					
						
							|  |  |  |       return $globals.icons.tags; | 
					
						
							|  |  |  |     case Organizer.Category: | 
					
						
							|  |  |  |       return $globals.icons.categories; | 
					
						
							|  |  |  |     case Organizer.Tool: | 
					
						
							|  |  |  |       return $globals.icons.tools; | 
					
						
							|  |  |  |     case Organizer.Food: | 
					
						
							|  |  |  |       return $globals.icons.foods; | 
					
						
							|  |  |  |     case Organizer.Household: | 
					
						
							|  |  |  |       return $globals.icons.household; | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |       return $globals.icons.tags; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ===========================================================================
 | 
					
						
							|  |  |  | // Store & Items Setup
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const storeMap = { | 
					
						
							|  |  |  |   [Organizer.Category]: useCategoryStore(), | 
					
						
							|  |  |  |   [Organizer.Tag]: useTagStore(), | 
					
						
							|  |  |  |   [Organizer.Tool]: useToolStore(), | 
					
						
							|  |  |  |   [Organizer.Food]: useFoodStore(), | 
					
						
							|  |  |  |   [Organizer.Household]: useHouseholdStore(), | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const store = computed(() => { | 
					
						
							|  |  |  |   const { store } = storeMap[props.selectorType]; | 
					
						
							|  |  |  |   return store.value; | 
					
						
							| 
									
										
										
										
											2022-04-01 09:50:31 -08:00
										 |  |  | }); | 
					
						
							| 
									
										
										
										
											2025-07-29 23:43:13 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | const items = computed(() => { | 
					
						
							|  |  |  |   if (!props.returnObject) { | 
					
						
							|  |  |  |     return store.value.map(item => item.name); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return store.value; | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function removeByIndex(index: number) { | 
					
						
							|  |  |  |   if (selected.value === undefined) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   const newSelected = selected.value.filter((_, i) => i !== index); | 
					
						
							|  |  |  |   selected.value = [...newSelected]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function appendCreated(item: any) { | 
					
						
							|  |  |  |   if (selected.value === undefined) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   selected.value = [...selected.value, item]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const dialog = ref(false); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const searchInput = ref(""); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function resetSearchInput() { | 
					
						
							|  |  |  |   searchInput.value = ""; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-04-01 09:50:31 -08:00
										 |  |  | </script> | 
					
						
							| 
									
										
										
										
											2024-10-17 10:35:39 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | <style scoped> | 
					
						
							|  |  |  | .v-autocomplete { | 
					
						
							|  |  |  |   /* This aligns the input with other standard input fields */ | 
					
						
							|  |  |  |   margin-top: 6px; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | </style> |