mirror of
				https://github.com/mealie-recipes/mealie.git
				synced 2025-10-30 17:53:31 -04:00 
			
		
		
		
	* implemented copymethat migration * added migration tree * added translation support * genericized example jpgs * added test data * fixed test archive * switched recipe create to service added test for timeline event creation * linting * lxml go brrr
		
			
				
	
	
		
			334 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			334 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <template>
 | |
|   <v-container>
 | |
|     <BasePageTitle divider>
 | |
|       <template #header>
 | |
|         <v-img
 | |
|           max-height="200"
 | |
|           max-width="200"
 | |
|           class="mb-2"
 | |
|           :src="require('~/static/svgs/manage-data-migrations.svg')"
 | |
|         ></v-img>
 | |
|       </template>
 | |
|       <template #title> Recipe Data Migrations</template>
 | |
|       Recipes can be migrated from another supported application to Mealie. This is a great way to get started with
 | |
|       Mealie.
 | |
|     </BasePageTitle>
 | |
|     <v-container>
 | |
|       <BaseCardSectionTitle :title="$i18n.tc('migration.new-migration')"> </BaseCardSectionTitle>
 | |
|       <v-card outlined :loading="loading">
 | |
|         <v-card-title> Choose Migration Type </v-card-title>
 | |
|         <v-card-text v-if="content" class="pb-0">
 | |
|           <div class="mb-2">
 | |
|             <BaseOverflowButton v-model="migrationType" mode="model" :items="items" />
 | |
|           </div>
 | |
|           {{ content.text }}
 | |
|           <v-treeview v-if="content.tree" dense :items="content.tree">
 | |
|             <template #prepend="{ item }">
 | |
|               <v-icon> {{ item.icon }}</v-icon>
 | |
|             </template>
 | |
|           </v-treeview>
 | |
|         </v-card-text>
 | |
| 
 | |
|         <v-card-title class="mt-0"> Upload File </v-card-title>
 | |
|         <v-card-text>
 | |
|           <AppButtonUpload
 | |
|             accept=".zip"
 | |
|             class="mb-2"
 | |
|             :post="false"
 | |
|             file-name="file"
 | |
|             :text-btn="false"
 | |
|             @uploaded="setFileObject"
 | |
|           />
 | |
|           {{ fileObject.name || $i18n.tc('migration.no-file-selected') }}
 | |
|         </v-card-text>
 | |
| 
 | |
|         <v-card-text>
 | |
|           <v-checkbox v-model="addMigrationTag">
 | |
|             <template #label>
 | |
|               Tag all recipes with <b class="mx-1"> {{ migrationType }} </b> tag
 | |
|             </template>
 | |
|           </v-checkbox>
 | |
|         </v-card-text>
 | |
| 
 | |
|         <v-card-actions class="justify-end">
 | |
|           <BaseButton :disabled="!fileObject.name" submit @click="startMigration">
 | |
|             {{ $t("general.submit") }}</BaseButton
 | |
|           >
 | |
|         </v-card-actions>
 | |
|       </v-card>
 | |
|     </v-container>
 | |
|     <v-container>
 | |
|       <BaseCardSectionTitle :title="$i18n.tc('migration.previous-migrations')"> </BaseCardSectionTitle>
 | |
|       <ReportTable :items="reports" @delete="deleteReport" />
 | |
|     </v-container>
 | |
|   </v-container>
 | |
| </template>
 | |
| 
 | |
| <script lang="ts">
 | |
| import { defineComponent, reactive, toRefs, useContext, computed, onMounted } from "@nuxtjs/composition-api";
 | |
| import { ReportSummary } from "~/lib/api/types/reports";
 | |
| import { MenuItem } from "~/components/global/BaseOverflowButton.vue";
 | |
| import { useUserApi } from "~/composables/api";
 | |
| import { SupportedMigrations } from "~/lib/api/types/group";
 | |
| 
 | |
| const MIGRATIONS = {
 | |
|   nextcloud: "nextcloud",
 | |
|   chowdown: "chowdown",
 | |
|   copymethat: "copymethat",
 | |
|   paprika: "paprika",
 | |
|   mealie: "mealie_alpha",
 | |
| };
 | |
| 
 | |
| export default defineComponent({
 | |
|   setup() {
 | |
|     const { $globals, i18n } = useContext();
 | |
| 
 | |
|     const api = useUserApi();
 | |
| 
 | |
|     const state = reactive({
 | |
|       addMigrationTag: false,
 | |
|       loading: false,
 | |
|       treeState: true,
 | |
|       migrationType: MIGRATIONS.nextcloud as SupportedMigrations,
 | |
|       fileObject: {} as File,
 | |
|       reports: [] as ReportSummary[],
 | |
|     });
 | |
| 
 | |
|     const items: MenuItem[] = [
 | |
|       {
 | |
|         text: i18n.tc("migration.nextcloud.title"),
 | |
|         value: MIGRATIONS.nextcloud,
 | |
|       },
 | |
|       {
 | |
|         text: i18n.tc("migration.chowdown.title"),
 | |
|         value: MIGRATIONS.chowdown,
 | |
|       },
 | |
|       {
 | |
|         text: i18n.tc("migration.copymethat.title"),
 | |
|         value: MIGRATIONS.copymethat,
 | |
|       },
 | |
|       {
 | |
|         text: i18n.tc("migration.paprika.title"),
 | |
|         value: MIGRATIONS.paprika,
 | |
|       },
 | |
|       {
 | |
|         text: i18n.tc("migration.mealie-pre-v1.title"),
 | |
|         value: MIGRATIONS.mealie,
 | |
|       },
 | |
|     ];
 | |
| 
 | |
|     const _content = {
 | |
|       [MIGRATIONS.nextcloud]: {
 | |
|         text: i18n.tc("migration.nextcloud.description-long"),
 | |
|         tree: [
 | |
|           {
 | |
|             id: 1,
 | |
|             icon: $globals.icons.zip,
 | |
|             name: "nextcloud.zip",
 | |
|             children: [
 | |
|               {
 | |
|                 id: 2,
 | |
|                 name: "Recipe 1",
 | |
|                 icon: $globals.icons.folderOutline,
 | |
|                 children: [
 | |
|                   { id: 3, name: "recipe.json", icon: $globals.icons.codeJson },
 | |
|                   { id: 4, name: "full.jpg", icon: $globals.icons.fileImage },
 | |
|                   { id: 5, name: "thumb.jpg", icon: $globals.icons.fileImage },
 | |
|                 ],
 | |
|               },
 | |
|               {
 | |
|                 id: 6,
 | |
|                 name: "Recipe 2",
 | |
|                 icon: $globals.icons.folderOutline,
 | |
|                 children: [
 | |
|                   { id: 7, name: "recipe.json", icon: $globals.icons.codeJson },
 | |
|                   { id: 8, name: "full.jpg", icon: $globals.icons.fileImage },
 | |
|                   { id: 9, name: "thumb.jpg", icon: $globals.icons.fileImage },
 | |
|                 ],
 | |
|               },
 | |
|             ],
 | |
|           },
 | |
|         ],
 | |
|       },
 | |
|       [MIGRATIONS.chowdown]: {
 | |
|         text: i18n.tc("migration.chowdown.description-long"),
 | |
|         tree: [
 | |
|           {
 | |
|             id: 1,
 | |
|             icon: $globals.icons.zip,
 | |
|             name: "nextcloud.zip",
 | |
|             children: [
 | |
|               {
 | |
|                 id: 2,
 | |
|                 name: "Recipe 1",
 | |
|                 icon: $globals.icons.folderOutline,
 | |
|                 children: [
 | |
|                   { id: 3, name: "recipe.json", icon: $globals.icons.codeJson },
 | |
|                   { id: 4, name: "full.jpg", icon: $globals.icons.fileImage },
 | |
|                   { id: 5, name: "thumb.jpg", icon: $globals.icons.fileImage },
 | |
|                 ],
 | |
|               },
 | |
|               {
 | |
|                 id: 6,
 | |
|                 name: "Recipe 2",
 | |
|                 icon: $globals.icons.folderOutline,
 | |
|                 children: [
 | |
|                   { id: 7, name: "recipe.json", icon: $globals.icons.codeJson },
 | |
|                   { id: 8, name: "full.jpg", icon: $globals.icons.fileImage },
 | |
|                   { id: 9, name: "thumb.jpg", icon: $globals.icons.fileImage },
 | |
|                 ],
 | |
|               },
 | |
|             ],
 | |
|           },
 | |
|         ],
 | |
|       },
 | |
|       [MIGRATIONS.copymethat]: {
 | |
|         text: i18n.tc("migration.copymethat.description-long"),
 | |
|         tree: [
 | |
|           {
 | |
|             id: 1,
 | |
|             icon: $globals.icons.zip,
 | |
|             name: "Copy_Me_That_20230306.zip",
 | |
|             children: [
 | |
|               {
 | |
|                 id: 2,
 | |
|                 name: "images",
 | |
|                 icon: $globals.icons.folderOutline,
 | |
|                 children: [
 | |
|                   { id: 3, name: "recipe_1_an5zy.jpg", icon: $globals.icons.fileImage },
 | |
|                   { id: 4, name: "recipe_2_82el8.jpg", icon: $globals.icons.fileImage },
 | |
|                   { id: 5, name: "recipe_3_j75qg.jpg", icon: $globals.icons.fileImage },
 | |
|                 ],
 | |
|               },
 | |
|               { id: 6, name: "recipes.html", icon: $globals.icons.codeJson }
 | |
|             ]
 | |
|           }
 | |
|         ],
 | |
|       },
 | |
|       [MIGRATIONS.paprika]: {
 | |
|         text: i18n.tc("migration.paprika.description-long"),
 | |
|         tree: false,
 | |
|       },
 | |
|       [MIGRATIONS.mealie]: {
 | |
|         text: i18n.tc("migration.mealie-pre-v1.description-long"),
 | |
|         tree: [
 | |
|           {
 | |
|             id: 1,
 | |
|             icon: $globals.icons.zip,
 | |
|             name: "mealie.zip",
 | |
|             children: [
 | |
|               {
 | |
|                 id: 2,
 | |
|                 name: "recipes",
 | |
|                 icon: $globals.icons.folderOutline,
 | |
|                 children: [
 | |
|                   {
 | |
|                     id: 3,
 | |
|                     name: "recipe-name",
 | |
|                     icon: $globals.icons.folderOutline,
 | |
|                     children: [
 | |
|                       { id: 4, name: "recipe-name.json", icon: $globals.icons.codeJson },
 | |
|                       {
 | |
|                         id: 5,
 | |
|                         name: "images",
 | |
|                         icon: $globals.icons.folderOutline,
 | |
|                         children: [
 | |
|                           { id: 6, name: "original.webp", icon: $globals.icons.codeJson },
 | |
|                           { id: 7, name: "full.jpg", icon: $globals.icons.fileImage },
 | |
|                           { id: 8, name: "thumb.jpg", icon: $globals.icons.fileImage },
 | |
|                         ],
 | |
|                       },
 | |
|                     ],
 | |
|                   },
 | |
|                   {
 | |
|                     id: 9,
 | |
|                     name: "recipe-name-1",
 | |
|                     icon: $globals.icons.folderOutline,
 | |
|                     children: [
 | |
|                       { id: 10, name: "recipe-name-1.json", icon: $globals.icons.codeJson },
 | |
|                       {
 | |
|                         id: 11,
 | |
|                         name: "images",
 | |
|                         icon: $globals.icons.folderOutline,
 | |
|                         children: [
 | |
|                           { id: 12, name: "original.webp", icon: $globals.icons.codeJson },
 | |
|                           { id: 13, name: "full.jpg", icon: $globals.icons.fileImage },
 | |
|                           { id: 14, name: "thumb.jpg", icon: $globals.icons.fileImage },
 | |
|                         ],
 | |
|                       },
 | |
|                     ],
 | |
|                   },
 | |
|                 ],
 | |
|               },
 | |
|             ],
 | |
|           },
 | |
|         ],
 | |
|       },
 | |
|     };
 | |
| 
 | |
|     function setFileObject(fileObject: File) {
 | |
|       state.fileObject = fileObject;
 | |
|     }
 | |
| 
 | |
|     async function startMigration() {
 | |
|       state.loading = true;
 | |
|       const payload = {
 | |
|         addMigrationTag: state.addMigrationTag,
 | |
|         migrationType: state.migrationType,
 | |
|         archive: state.fileObject,
 | |
|       };
 | |
| 
 | |
|       const { data } = await api.groupMigration.startMigration(payload);
 | |
| 
 | |
|       state.loading = false;
 | |
| 
 | |
|       if (data) {
 | |
|         state.reports.unshift(data);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     async function getMigrationReports() {
 | |
|       const { data } = await api.groupReports.getAll("migration");
 | |
| 
 | |
|       if (data) {
 | |
|         state.reports = data;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     async function deleteReport(id: string) {
 | |
|       await api.groupReports.deleteOne(id);
 | |
|       getMigrationReports();
 | |
|     }
 | |
| 
 | |
|     onMounted(() => {
 | |
|       getMigrationReports();
 | |
|     });
 | |
| 
 | |
|     const content = computed(() => {
 | |
|       const data = _content[state.migrationType];
 | |
| 
 | |
|       if (data) {
 | |
|         return data;
 | |
|       } else {
 | |
|         return {
 | |
|           text: "",
 | |
|           tree: false,
 | |
|         };
 | |
|       }
 | |
|     });
 | |
| 
 | |
|     return {
 | |
|       ...toRefs(state),
 | |
|       items,
 | |
|       content,
 | |
|       setFileObject,
 | |
|       deleteReport,
 | |
|       startMigration,
 | |
|       getMigrationReports,
 | |
|     };
 | |
|   },
 | |
| });
 | |
| </script>
 | |
| 
 | |
| <style lang="scss" scoped></style>
 |