mirror of
				https://github.com/mealie-recipes/mealie.git
				synced 2025-10-31 02:03:35 -04:00 
			
		
		
		
	feat: Cookbook Create & Delete Improvements (#2902)
* add delete dialog
* put editor into component
* return data on createCookbook store action
* verry basic dialog with create & cancel functions
* 🧹
* cleanup
* add translation
* add dialog-closed to BaseDialog
* update delete dialog messaging
* use cancel instead of dialog-closed
			
			
This commit is contained in:
		
							
								
								
									
										55
									
								
								frontend/components/Domain/Cookbook/CookbookEditor.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								frontend/components/Domain/Cookbook/CookbookEditor.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <v-card-text v-if="cookbook"> | ||||
|       <v-text-field v-model="cookbook.name" :label="$t('cookbook.cookbook-name')"></v-text-field> | ||||
|       <v-textarea v-model="cookbook.description" auto-grow :rows="2" :label="$t('recipe.description')"></v-textarea> | ||||
|       <RecipeOrganizerSelector v-model="cookbook.categories" selector-type="categories" /> | ||||
|       <RecipeOrganizerSelector v-model="cookbook.tags" selector-type="tags" /> | ||||
|       <RecipeOrganizerSelector v-model="cookbook.tools" selector-type="tools" /> | ||||
|       <v-switch v-model="cookbook.public" hide-details single-line> | ||||
|         <template #label> | ||||
|           {{ $t('cookbook.public-cookbook') }} | ||||
|           <HelpIcon small right class="ml-2"> | ||||
|             {{ $t('cookbook.public-cookbook-description') }} | ||||
|           </HelpIcon> | ||||
|         </template> | ||||
|       </v-switch> | ||||
|       <div class="mt-4"> | ||||
|         <h3 class="text-subtitle-1 d-flex align-center mb-0 pb-0"> | ||||
|           {{ $t('cookbook.filter-options') }} | ||||
|           <HelpIcon right small class="ml-2"> | ||||
|             {{ $t('cookbook.filter-options-description') }} | ||||
|           </HelpIcon> | ||||
|         </h3> | ||||
|         <v-switch v-model="cookbook.requireAllCategories" class="mt-0" hide-details single-line> | ||||
|           <template #label> {{ $t('cookbook.require-all-categories') }} </template> | ||||
|         </v-switch> | ||||
|         <v-switch v-model="cookbook.requireAllTags" hide-details single-line> | ||||
|           <template #label> {{ $t('cookbook.require-all-tags') }} </template> | ||||
|         </v-switch> | ||||
|         <v-switch v-model="cookbook.requireAllTools" hide-details single-line> | ||||
|           <template #label> {{ $t('cookbook.require-all-tools') }} </template> | ||||
|         </v-switch> | ||||
|       </div> | ||||
|     </v-card-text> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| import { defineComponent } from "@nuxtjs/composition-api"; | ||||
| import { ReadCookBook } from "~/lib/api/types/cookbook"; | ||||
| import RecipeOrganizerSelector from "~/components/Domain/Recipe/RecipeOrganizerSelector.vue"; | ||||
| export default defineComponent({ | ||||
|   components: { RecipeOrganizerSelector }, | ||||
|   props: { | ||||
|     cookbook: { | ||||
|       type: Object as () => ReadCookBook, | ||||
|       required: true, | ||||
|     }, | ||||
|     actions: { | ||||
|       type: Object as () => any, | ||||
|       required: true, | ||||
|     }, | ||||
|   }, | ||||
| }); | ||||
| </script> | ||||
| @@ -12,6 +12,8 @@ | ||||
|         $emit('submit'); | ||||
|         dialog = false; | ||||
|       " | ||||
|       @click:outside="$emit('cancel')" | ||||
|       @keydown.esc="$emit('cancel')" | ||||
|     > | ||||
|       <v-card height="100%"> | ||||
|         <v-app-bar dark dense :color="color" class=""> | ||||
|   | ||||
| @@ -109,6 +109,7 @@ export const useCookbooks = function () { | ||||
|       } | ||||
|  | ||||
|       loading.value = false; | ||||
|       return data; | ||||
|     }, | ||||
|     async updateOne(updateData: UpdateCookBook) { | ||||
|       if (!updateData.id) { | ||||
|   | ||||
| @@ -1185,6 +1185,8 @@ | ||||
|     "require-all-tags": "Require All Tags", | ||||
|     "require-all-tools": "Require All Tools", | ||||
|     "cookbook-name": "Cookbook Name", | ||||
|     "cookbook-with-name": "Cookbook {0}" | ||||
|     "cookbook-with-name": "Cookbook {0}", | ||||
|     "create-a-cookbook": "Create a Cookbook", | ||||
|     "cookbook": "Cookbook" | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,41 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <!-- Create Dialog --> | ||||
|     <BaseDialog | ||||
|       v-if="createTarget" | ||||
|       v-model="dialogStates.create" | ||||
|       :width="650" | ||||
|       :icon="$globals.icons.pages" | ||||
|       :title="$t('cookbook.create-a-cookbook')" | ||||
|       :submit-icon="$globals.icons.save" | ||||
|       :submit-text="$tc('general.save')" | ||||
|       @submit="actions.updateOne(createTarget)" | ||||
|       @cancel="actions.deleteOne(createTarget.id)" | ||||
|     > | ||||
|       <v-card-text> | ||||
|         <CookbookEditor | ||||
|           :cookbook=createTarget | ||||
|           :actions="actions" | ||||
|         /> | ||||
|       </v-card-text> | ||||
|     </BaseDialog> | ||||
|  | ||||
|     <!-- Delete Dialog --> | ||||
|     <BaseDialog | ||||
|       v-model="dialogStates.delete" | ||||
|       :title="$t('general.delete-with-name', { name: $t('cookbook.cookbook') })" | ||||
|       :icon="$globals.icons.alertCircle" | ||||
|       color="error" | ||||
|       @confirm="deleteCookbook()" | ||||
|     > | ||||
|       <v-card-text> | ||||
|         <p>{{ $t("general.confirm-delete-generic-with-name", { name: $t('cookbook.cookbook') }) }}</p> | ||||
|         <p v-if="deleteTarget" class="mt-4 ml-4">{{ deleteTarget.name }}</p> | ||||
|       </v-card-text> | ||||
|     </BaseDialog> | ||||
|  | ||||
|     <!-- Cookbook Page --> | ||||
|     <!-- Page Title --> | ||||
|     <v-container class="narrow-container"> | ||||
|       <BasePageTitle divider> | ||||
|         <template #header> | ||||
| @@ -8,7 +45,10 @@ | ||||
|         {{ $t('cookbook.description') }} | ||||
|       </BasePageTitle> | ||||
|  | ||||
|     <BaseButton create @click="actions.createOne()" /> | ||||
|       <!-- Create New --> | ||||
|       <BaseButton create @click="createCookbook" /> | ||||
|  | ||||
|       <!-- Cookbook List --> | ||||
|       <v-expansion-panels class="mt-2"> | ||||
|         <draggable v-model="cookbooks" handle=".handle" style="width: 100%" @change="actions.updateOrder()"> | ||||
|           <v-expansion-panel v-for="(cookbook, index) in cookbooks" :key="index" class="my-2 left-border rounded"> | ||||
| @@ -31,43 +71,11 @@ | ||||
|               </template> | ||||
|             </v-expansion-panel-header> | ||||
|             <v-expansion-panel-content> | ||||
|             <v-card-text v-if="cookbooks"> | ||||
|               <v-text-field v-model="cookbooks[index].name" :label="$t('cookbook.cookbook-name')"></v-text-field> | ||||
|               <v-textarea v-model="cookbooks[index].description" auto-grow :rows="2" :label="$t('recipe.description')"></v-textarea> | ||||
|               <RecipeOrganizerSelector v-model="cookbooks[index].categories" selector-type="categories" /> | ||||
|               <RecipeOrganizerSelector v-model="cookbooks[index].tags" selector-type="tags" /> | ||||
|               <RecipeOrganizerSelector v-model="cookbooks[index].tools" selector-type="tools" /> | ||||
|               <v-switch v-model="cookbooks[index].public" hide-details single-line> | ||||
|                 <template #label> | ||||
|                   {{ $t('cookbook.public-cookbook') }} | ||||
|                   <HelpIcon small right class="ml-2"> | ||||
|                     {{ $t('cookbook.public-cookbook-description') }} | ||||
|                   </HelpIcon> | ||||
|                 </template> | ||||
|               </v-switch> | ||||
|               <div class="mt-4"> | ||||
|                 <h3 class="text-subtitle-1 d-flex align-center mb-0 pb-0"> | ||||
|                   {{ $t('cookbook.filter-options') }} | ||||
|                   <HelpIcon right small class="ml-2"> | ||||
|                     {{ $t('cookbook.filter-options-description') }} | ||||
|                   </HelpIcon> | ||||
|                 </h3> | ||||
|                 <v-switch v-model="cookbooks[index].requireAllCategories" class="mt-0" hide-details single-line> | ||||
|                   <template #label> {{ $t('cookbook.require-all-categories') }} </template> | ||||
|                 </v-switch> | ||||
|                 <v-switch v-model="cookbooks[index].requireAllTags" hide-details single-line> | ||||
|                   <template #label> {{ $t('cookbook.require-all-tags') }} </template> | ||||
|                 </v-switch> | ||||
|                 <v-switch v-model="cookbooks[index].requireAllTools" hide-details single-line> | ||||
|                   <template #label> {{ $t('cookbook.require-all-tools') }} </template> | ||||
|                 </v-switch> | ||||
|               </div> | ||||
|             </v-card-text> | ||||
|               <CookbookEditor :cookbook="cookbook" :actions="actions" :collapsable="false" @delete="deleteEventHandler" /> | ||||
|               <v-card-actions> | ||||
|                 <v-spacer></v-spacer> | ||||
|                 <BaseButtonGroup | ||||
|                 :buttons="[ | ||||
|                   { | ||||
|                   :buttons="[{ | ||||
|                     icon: $globals.icons.delete, | ||||
|                     text: $tc('general.delete'), | ||||
|                     event: 'delete', | ||||
| @@ -78,26 +86,27 @@ | ||||
|                     event: 'save', | ||||
|                   }, | ||||
|                 ]" | ||||
|                 @delete="actions.deleteOne(cookbook.id)" | ||||
|                 @save="actions.updateOne(cookbook)" | ||||
|               /> | ||||
|                 @delete="deleteEventHandler(cookbook)" | ||||
|                 @save="actions.updateOne(cookbook)" /> | ||||
|               </v-card-actions> | ||||
|             </v-expansion-panel-content> | ||||
|           </v-expansion-panel> | ||||
|         </draggable> | ||||
|       </v-expansion-panels> | ||||
|     </v-container> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| import { defineComponent, useRouter } from "@nuxtjs/composition-api"; | ||||
| import { defineComponent, reactive, ref, useRouter } from "@nuxtjs/composition-api"; | ||||
| import draggable from "vuedraggable"; | ||||
| import { useCookbooks } from "@/composables/use-group-cookbooks"; | ||||
| import { useLoggedInState } from "~/composables/use-logged-in-state"; | ||||
| import RecipeOrganizerSelector from "~/components/Domain/Recipe/RecipeOrganizerSelector.vue"; | ||||
| import CookbookEditor from "~/components/Domain/Cookbook/CookbookEditor.vue"; | ||||
| import { ReadCookBook } from "~/lib/api/types/cookbook"; | ||||
|  | ||||
| export default defineComponent({ | ||||
|   components: { draggable, RecipeOrganizerSelector }, | ||||
|   components: { CookbookEditor, draggable }, | ||||
|   setup() { | ||||
|     const { isOwnGroup, loggedIn } = useLoggedInState(); | ||||
|     const router = useRouter(); | ||||
| @@ -106,11 +115,50 @@ export default defineComponent({ | ||||
|       router.back(); | ||||
|     } | ||||
|  | ||||
|     const dialogStates = reactive({ | ||||
|       create: false, | ||||
|       delete: false, | ||||
|     }); | ||||
|  | ||||
|     const { cookbooks, actions } = useCookbooks(); | ||||
|  | ||||
|  | ||||
|     // create | ||||
|     const createTarget = ref<ReadCookBook | null>(null); | ||||
|     async function createCookbook() { | ||||
|       await actions.createOne().then((cookbook) => { | ||||
|         createTarget.value = cookbook as ReadCookBook; | ||||
|       }); | ||||
|       dialogStates.create = true; | ||||
|     } | ||||
|  | ||||
|     // delete | ||||
|     const deleteTarget = ref<ReadCookBook | null>(null); | ||||
|     function deleteEventHandler(item: ReadCookBook){ | ||||
|       deleteTarget.value = item; | ||||
|       dialogStates.delete = true; | ||||
|     } | ||||
|     function deleteCookbook() { | ||||
|       if (!deleteTarget.value) { | ||||
|         return; | ||||
|       } | ||||
|       actions.deleteOne(deleteTarget.value.id); | ||||
|       dialogStates.delete = false; | ||||
|       deleteTarget.value = null; | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|       cookbooks, | ||||
|       actions, | ||||
|       dialogStates, | ||||
|       // create | ||||
|       createTarget, | ||||
|       createCookbook, | ||||
|  | ||||
|       // delete | ||||
|       deleteTarget, | ||||
|       deleteEventHandler, | ||||
|       deleteCookbook, | ||||
|     }; | ||||
|   }, | ||||
|   head() { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user