| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  | <template> | 
					
						
							|  |  |  |   <div v-if="items"> | 
					
						
							| 
									
										
										
										
											2023-03-12 12:59:28 -08:00
										 |  |  |     <RecipeOrganizerDialog v-model="dialogs.organizer" :item-type="itemType" /> | 
					
						
							| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     <BaseDialog | 
					
						
							|  |  |  |       v-if="deleteTarget" | 
					
						
							| 
									
										
										
										
											2023-03-12 12:59:28 -08:00
										 |  |  |       v-model="dialogs.delete" | 
					
						
							| 
									
										
										
										
											2024-01-04 11:44:04 +00:00
										 |  |  |       :title="$t('general.delete-with-name', { name: $t(translationKey) })" | 
					
						
							| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  |       color="error" | 
					
						
							|  |  |  |       :icon="$globals.icons.alertCircle" | 
					
						
							|  |  |  |       @confirm="deleteOne()" | 
					
						
							|  |  |  |     > | 
					
						
							| 
									
										
										
										
											2024-01-04 11:44:04 +00:00
										 |  |  |       <v-card-text> | 
					
						
							| 
									
										
										
										
											2024-01-08 10:45:37 +01:00
										 |  |  | <p>{{ $t("general.confirm-delete-generic-with-name", { name: $t(translationKey) }) }}</p> | 
					
						
							| 
									
										
										
										
											2024-01-04 11:44:04 +00:00
										 |  |  |         <p class="mt-4 mb-0 ml-4">{{ deleteTarget.name }}</p> | 
					
						
							|  |  |  |       </v-card-text> | 
					
						
							| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  |     </BaseDialog> | 
					
						
							| 
									
										
										
										
											2022-12-01 06:31:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-12 12:59:28 -08:00
										 |  |  |     <BaseDialog v-if="updateTarget" v-model="dialogs.update" :title="$t('general.update')" @confirm="updateOne()"> | 
					
						
							|  |  |  |       <v-card-text> | 
					
						
							| 
									
										
										
										
											2024-01-30 15:24:46 +01:00
										 |  |  |         <v-text-field v-model="updateTarget.name" :label="$t('general.name')"> </v-text-field> | 
					
						
							| 
									
										
										
										
											2023-11-21 15:31:05 +01:00
										 |  |  |         <v-checkbox v-if="itemType === Organizer.Tool" v-model="updateTarget.onHand" :label="$t('tool.on-hand')"></v-checkbox> | 
					
						
							| 
									
										
										
										
											2023-03-12 12:59:28 -08:00
										 |  |  |       </v-card-text> | 
					
						
							|  |  |  |     </BaseDialog> | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-01 06:31:16 +01:00
										 |  |  |     <v-row dense> | 
					
						
							| 
									
										
										
										
											2023-03-12 12:59:28 -08:00
										 |  |  |       <v-col> | 
					
						
							|  |  |  |         <v-text-field | 
					
						
							|  |  |  |           v-model="searchString" | 
					
						
							|  |  |  |           outlined | 
					
						
							|  |  |  |           autofocus | 
					
						
							|  |  |  |           color="primary accent-3" | 
					
						
							|  |  |  |           :placeholder="$t('search.search-placeholder')" | 
					
						
							|  |  |  |           :prepend-inner-icon="$globals.icons.search" | 
					
						
							|  |  |  |           clearable | 
					
						
							|  |  |  |         > | 
					
						
							|  |  |  |         </v-text-field> | 
					
						
							|  |  |  |       </v-col> | 
					
						
							|  |  |  |     </v-row> | 
					
						
							| 
									
										
										
										
											2022-12-01 06:31:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  |     <v-app-bar color="transparent" flat class="mt-n1 rounded align-center"> | 
					
						
							|  |  |  |       <v-icon large left> | 
					
						
							|  |  |  |         {{ icon }} | 
					
						
							|  |  |  |       </v-icon> | 
					
						
							|  |  |  |       <v-toolbar-title class="headline"> | 
					
						
							| 
									
										
										
										
											2023-03-12 12:59:28 -08:00
										 |  |  |         <slot name="title"> </slot> | 
					
						
							| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  |       </v-toolbar-title> | 
					
						
							|  |  |  |       <v-spacer></v-spacer> | 
					
						
							| 
									
										
										
										
											2023-03-12 12:59:28 -08:00
										 |  |  |       <BaseButton create @click="dialogs.organizer = true" /> | 
					
						
							| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  |     </v-app-bar> | 
					
						
							| 
									
										
										
										
											2023-03-12 12:59:28 -08:00
										 |  |  |     <section v-for="(itms, key, idx) in itemsSorted" :key="'header' + idx" :class="idx === 1 ? null : 'my-4'"> | 
					
						
							|  |  |  |       <BaseCardSectionTitle v-if="isTitle(key)" :title="key" /> | 
					
						
							| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  |       <v-row> | 
					
						
							|  |  |  |         <v-col v-for="(item, index) in itms" :key="'cat' + index" cols="12" :sm="12" :md="6" :lg="4" :xl="3"> | 
					
						
							| 
									
										
										
										
											2023-11-05 19:07:02 -06:00
										 |  |  |           <v-card v-if="item" class="left-border" hover :to="`/g/${groupSlug}?${itemType}=${item.id}`"> | 
					
						
							| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  |             <v-card-actions> | 
					
						
							|  |  |  |               <v-icon> | 
					
						
							|  |  |  |                 {{ icon }} | 
					
						
							|  |  |  |               </v-icon> | 
					
						
							|  |  |  |               <v-card-title class="py-1"> | 
					
						
							|  |  |  |                 {{ item.name }} | 
					
						
							|  |  |  |               </v-card-title> | 
					
						
							|  |  |  |               <v-spacer></v-spacer> | 
					
						
							| 
									
										
										
										
											2023-03-12 12:59:28 -08:00
										 |  |  |               <ContextMenu | 
					
						
							|  |  |  |                 :items="[presets.delete, presets.edit]" | 
					
						
							|  |  |  |                 @delete="confirmDelete(item)" | 
					
						
							|  |  |  |                 @edit="openUpdateDialog(item)" | 
					
						
							|  |  |  |               /> | 
					
						
							| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  |             </v-card-actions> | 
					
						
							|  |  |  |           </v-card> | 
					
						
							|  |  |  |         </v-col> | 
					
						
							|  |  |  |       </v-row> | 
					
						
							|  |  |  |     </section> | 
					
						
							|  |  |  |   </div> | 
					
						
							|  |  |  | </template> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <script lang="ts"> | 
					
						
							| 
									
										
										
										
											2022-12-01 06:31:16 +01:00
										 |  |  | import Fuse from "fuse.js"; | 
					
						
							| 
									
										
										
										
											2023-11-05 19:07:02 -06:00
										 |  |  | import { defineComponent, computed, ref, reactive, useContext, useRoute } from "@nuxtjs/composition-api"; | 
					
						
							| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  | import { useContextPresets } from "~/composables/use-context-presents"; | 
					
						
							|  |  |  | import RecipeOrganizerDialog from "~/components/Domain/Recipe/RecipeOrganizerDialog.vue"; | 
					
						
							| 
									
										
										
										
											2023-11-21 15:31:05 +01:00
										 |  |  | import { Organizer, RecipeOrganizer } from "~/lib/api/types/non-generated"; | 
					
						
							| 
									
										
										
										
											2022-12-01 06:31:16 +01:00
										 |  |  | import { useRouteQuery } from "~/composables/use-router"; | 
					
						
							| 
									
										
										
										
											2023-03-12 12:59:28 -08:00
										 |  |  | import { deepCopy } from "~/composables/use-utils"; | 
					
						
							| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | interface GenericItem { | 
					
						
							| 
									
										
										
										
											2023-03-12 12:59:28 -08:00
										 |  |  |   id: string; | 
					
						
							| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  |   name: string; | 
					
						
							|  |  |  |   slug: string; | 
					
						
							| 
									
										
										
										
											2023-11-21 15:31:05 +01:00
										 |  |  |   onHand: boolean; | 
					
						
							| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export default defineComponent({ | 
					
						
							|  |  |  |   components: { | 
					
						
							|  |  |  |     RecipeOrganizerDialog, | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   props: { | 
					
						
							|  |  |  |     items: { | 
					
						
							|  |  |  |       type: Array as () => GenericItem[], | 
					
						
							|  |  |  |       required: true, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     icon: { | 
					
						
							|  |  |  |       type: String, | 
					
						
							|  |  |  |       required: true, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     itemType: { | 
					
						
							|  |  |  |       type: String as () => RecipeOrganizer, | 
					
						
							|  |  |  |       required: true, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   setup(props, { emit }) { | 
					
						
							| 
									
										
										
										
											2022-12-01 06:31:16 +01:00
										 |  |  |     const state = reactive({ | 
					
						
							|  |  |  |       // Search Options
 | 
					
						
							|  |  |  |       options: { | 
					
						
							|  |  |  |         ignoreLocation: true, | 
					
						
							|  |  |  |         shouldSort: true, | 
					
						
							|  |  |  |         threshold: 0.6, | 
					
						
							|  |  |  |         location: 0, | 
					
						
							|  |  |  |         distance: 100, | 
					
						
							|  |  |  |         findAllMatches: true, | 
					
						
							|  |  |  |         maxPatternLength: 32, | 
					
						
							|  |  |  |         minMatchCharLength: 1, | 
					
						
							|  |  |  |         keys: ["name"], | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-05 19:07:02 -06:00
										 |  |  |     const { $auth } = useContext(); | 
					
						
							|  |  |  |     const route = useRoute(); | 
					
						
							|  |  |  |     const groupSlug = computed(() => route.value.params.groupSlug || $auth.user?.groupSlug || ""); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-12 12:59:28 -08:00
										 |  |  |     // =================================================================
 | 
					
						
							|  |  |  |     // Context Menu
 | 
					
						
							| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-12 12:59:28 -08:00
										 |  |  |     const dialogs = ref({ | 
					
						
							|  |  |  |       organizer: false, | 
					
						
							|  |  |  |       update: false, | 
					
						
							|  |  |  |       delete: false, | 
					
						
							| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const presets = useContextPresets(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-04 11:44:04 +00:00
										 |  |  |     const translationKey = computed<string>(() => { | 
					
						
							|  |  |  |       const typeMap = { | 
					
						
							|  |  |  |         "categories": "category.category", | 
					
						
							|  |  |  |         "tags": "tag.tag", | 
					
						
							|  |  |  |         "tools": "tool.tool" | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       return typeMap[props.itemType] || ""; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  |     const deleteTarget = ref<GenericItem | null>(null); | 
					
						
							| 
									
										
										
										
											2023-03-12 12:59:28 -08:00
										 |  |  |     const updateTarget = ref<GenericItem | null>(null); | 
					
						
							| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     function confirmDelete(item: GenericItem) { | 
					
						
							|  |  |  |       deleteTarget.value = item; | 
					
						
							| 
									
										
										
										
											2023-03-12 12:59:28 -08:00
										 |  |  |       dialogs.value.delete = true; | 
					
						
							| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function deleteOne() { | 
					
						
							|  |  |  |       if (!deleteTarget.value) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       emit("delete", deleteTarget.value.id); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-12 12:59:28 -08:00
										 |  |  |     function openUpdateDialog(item: GenericItem) { | 
					
						
							|  |  |  |       updateTarget.value = deepCopy(item); | 
					
						
							|  |  |  |       dialogs.value.update = true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function updateOne() { | 
					
						
							|  |  |  |       if (!updateTarget.value) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       emit("update", updateTarget.value); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-01 06:31:16 +01:00
										 |  |  |     // ================================================================
 | 
					
						
							|  |  |  |     // Search Functions
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const searchString = useRouteQuery("q", ""); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const fuse = computed(() => { | 
					
						
							|  |  |  |       return new Fuse(props.items, state.options); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-12 12:59:28 -08:00
										 |  |  |     const fuzzyItems = computed<GenericItem[]>(() => { | 
					
						
							| 
									
										
										
										
											2022-12-01 06:31:16 +01:00
										 |  |  |       if (searchString.value.trim() === "") { | 
					
						
							|  |  |  |         return props.items; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       const result = fuse.value.search(searchString.value.trim() as string); | 
					
						
							|  |  |  |       return result.map((x) => x.item); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-12 12:59:28 -08:00
										 |  |  |     // =================================================================
 | 
					
						
							|  |  |  |     // Sorted Items
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const itemsSorted = computed(() => { | 
					
						
							|  |  |  |       const byLetter: { [key: string]: Array<GenericItem> } = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (!fuzzyItems.value) { | 
					
						
							|  |  |  |         return byLetter; | 
					
						
							| 
									
										
										
										
											2022-12-01 06:31:16 +01:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2023-03-12 12:59:28 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |       fuzzyItems.value | 
					
						
							|  |  |  |         .sort((a, b) => a.name.localeCompare(b.name)) | 
					
						
							|  |  |  |         .forEach((item) => { | 
					
						
							|  |  |  |           const letter = item.name[0].toUpperCase(); | 
					
						
							|  |  |  |           if (!byLetter[letter]) { | 
					
						
							|  |  |  |             byLetter[letter] = []; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           byLetter[letter].push(item); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return byLetter; | 
					
						
							| 
									
										
										
										
											2022-12-01 06:31:16 +01:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-12 12:59:28 -08:00
										 |  |  |     function isTitle(str: number | string) { | 
					
						
							|  |  |  |       return typeof str === "string" && str.length === 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  |     return { | 
					
						
							| 
									
										
										
										
											2023-11-05 19:07:02 -06:00
										 |  |  |       groupSlug, | 
					
						
							| 
									
										
										
										
											2023-03-12 12:59:28 -08:00
										 |  |  |       isTitle, | 
					
						
							|  |  |  |       dialogs, | 
					
						
							| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  |       confirmDelete, | 
					
						
							| 
									
										
										
										
											2023-03-12 12:59:28 -08:00
										 |  |  |       openUpdateDialog, | 
					
						
							|  |  |  |       updateOne, | 
					
						
							|  |  |  |       updateTarget, | 
					
						
							| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  |       deleteOne, | 
					
						
							|  |  |  |       deleteTarget, | 
					
						
							| 
									
										
										
										
											2023-11-21 15:31:05 +01:00
										 |  |  |       Organizer, | 
					
						
							| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  |       presets, | 
					
						
							|  |  |  |       itemsSorted, | 
					
						
							| 
									
										
										
										
											2022-12-01 06:31:16 +01:00
										 |  |  |       searchString, | 
					
						
							| 
									
										
										
										
											2024-01-04 11:44:04 +00:00
										 |  |  |       translationKey, | 
					
						
							| 
									
										
										
										
											2022-06-03 20:12:32 -08:00
										 |  |  |     }; | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   // Needed for useMeta
 | 
					
						
							|  |  |  |   head: {}, | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | </script> |