mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-01 04:10:06 -05:00
very basic app import working
This commit is contained in:
@@ -54,7 +54,7 @@ export function useFileApi() {
|
|||||||
* @param file file object to upload or null to delete image (if no imageUrl is given)
|
* @param file file object to upload or null to delete image (if no imageUrl is given)
|
||||||
* @param imageUrl url of an image to download by server
|
* @param imageUrl url of an image to download by server
|
||||||
*/
|
*/
|
||||||
function updateRecipeImage(recipeId: number, file: File|null, imageUrl?: string){
|
function updateRecipeImage(recipeId: number, file: File | null, imageUrl?: string) {
|
||||||
let formData = new FormData()
|
let formData = new FormData()
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
formData.append('image', file)
|
formData.append('image', file)
|
||||||
@@ -80,7 +80,7 @@ export function useFileApi() {
|
|||||||
* uploads the given file to the image recognition endpoint
|
* uploads the given file to the image recognition endpoint
|
||||||
* @param file file object to upload
|
* @param file file object to upload
|
||||||
*/
|
*/
|
||||||
function convertImageToRecipe(file: File){
|
function convertImageToRecipe(file: File) {
|
||||||
let formData = new FormData()
|
let formData = new FormData()
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
formData.append('image', file)
|
formData.append('image', file)
|
||||||
@@ -99,5 +99,31 @@ export function useFileApi() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return {fileApiLoading, createOrUpdateUserFile, updateRecipeImage, convertImageToRecipe}
|
/**
|
||||||
|
* uploads the given files to the app import endpoint
|
||||||
|
* @param file array to import
|
||||||
|
* @param app app to import
|
||||||
|
* @param includeDuplicates if recipes that were found as duplicates should be imported as well
|
||||||
|
*/
|
||||||
|
function doAppImport(file: File, app: string, includeDuplicates: boolean) {
|
||||||
|
let formData = new FormData()
|
||||||
|
formData.append('type', app);
|
||||||
|
formData.append('duplicates', includeDuplicates ? 'true' : 'false')
|
||||||
|
// files.forEach(file => {
|
||||||
|
// formData.append('files', file)
|
||||||
|
// })
|
||||||
|
formData.append('files', file)
|
||||||
|
|
||||||
|
return fetch(getDjangoUrl(`api/import/`), {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {'X-CSRFToken': getCookie('csrftoken')},
|
||||||
|
body: formData
|
||||||
|
}).then(r => {
|
||||||
|
console.log(r)
|
||||||
|
}).finally(() => {
|
||||||
|
fileApiLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {fileApiLoading, createOrUpdateUserFile, updateRecipeImage, convertImageToRecipe, doAppImport}
|
||||||
}
|
}
|
||||||
@@ -25,6 +25,13 @@
|
|||||||
<v-divider></v-divider>
|
<v-divider></v-divider>
|
||||||
<v-stepper-item :title="$t('Save')" value="confirm" icon=" "></v-stepper-item>
|
<v-stepper-item :title="$t('Save')" value="confirm" icon=" "></v-stepper-item>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-if="importType == 'app'">
|
||||||
|
<v-stepper-item :title="$t('App')" value="app" icon=" "></v-stepper-item>
|
||||||
|
<v-divider></v-divider>
|
||||||
|
<v-stepper-item :title="$t('File')" value="file" icon=" "></v-stepper-item>
|
||||||
|
<v-divider></v-divider>
|
||||||
|
<v-stepper-item :title="$t('Import')" value="import_log" icon=" "></v-stepper-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
</v-stepper-header>
|
</v-stepper-header>
|
||||||
|
|
||||||
@@ -82,11 +89,15 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #next>
|
<template #next>
|
||||||
<v-btn @click="stepper = 'url'" v-if="['url','ai'].includes(importType)">{{ $t('Next') }}</v-btn>
|
<v-btn @click="stepper = 'url'" v-if="['url','ai'].includes(importType)">{{ $t('Next') }}</v-btn>
|
||||||
|
<v-btn @click="stepper = 'app'" v-if="importType == 'app'">{{ $t('Next') }}</v-btn>
|
||||||
</template>
|
</template>
|
||||||
</v-stepper-actions>
|
</v-stepper-actions>
|
||||||
</v-stepper-window-item>
|
</v-stepper-window-item>
|
||||||
<v-stepper-window-item value="url">
|
|
||||||
|
|
||||||
|
<!-- ------------ -->
|
||||||
|
<!-- ULR/AI Items -->
|
||||||
|
<!-- ------------ -->
|
||||||
|
<v-stepper-window-item value="url">
|
||||||
<v-text-field :label="$t('Website') + ' (https://...)'" v-model="importUrl" v-if="importType == 'url'" :loading="loading"></v-text-field>
|
<v-text-field :label="$t('Website') + ' (https://...)'" v-model="importUrl" v-if="importType == 'url'" :loading="loading"></v-text-field>
|
||||||
|
|
||||||
<v-file-input v-model="image" :label="$t('Image')" v-if="importType == 'ai'" :loading="loading"></v-file-input>
|
<v-file-input v-model="image" :label="$t('Image')" v-if="importType == 'ai'" :loading="loading"></v-file-input>
|
||||||
@@ -120,7 +131,6 @@
|
|||||||
</template>
|
</template>
|
||||||
</v-stepper-actions>
|
</v-stepper-actions>
|
||||||
</v-stepper-window-item>
|
</v-stepper-window-item>
|
||||||
|
|
||||||
<v-stepper-window-item value="image_chooser">
|
<v-stepper-window-item value="image_chooser">
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col cols="12" md="6">
|
<v-col cols="12" md="6">
|
||||||
@@ -292,22 +302,60 @@
|
|||||||
</template>
|
</template>
|
||||||
</v-stepper-actions>
|
</v-stepper-actions>
|
||||||
</v-stepper-window-item>
|
</v-stepper-window-item>
|
||||||
|
<!-- ------------ -->
|
||||||
|
<!-- App Import Items -->
|
||||||
|
<!-- ------------ -->
|
||||||
|
<v-stepper-window-item value="app">
|
||||||
|
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="12" md="3" v-for="i in INTEGRATIONS">
|
||||||
|
<v-card prepend-icon="fa-solid fa-carrot" :title="i.name" @click="importApp = i.id" variant="outlined" elevation="1"
|
||||||
|
:color="(importApp == i.id) ? 'primary' : ''">
|
||||||
|
<template #append>
|
||||||
|
<v-btn icon="$help" variant="plain" :href="i.helpUrl" target="_blank"></v-btn>
|
||||||
|
</template>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
|
||||||
|
<v-stepper-actions>
|
||||||
|
<template #prev>
|
||||||
|
<v-btn @click="stepper = 'type'">{{ $t('Back') }}</v-btn>
|
||||||
|
</template>
|
||||||
|
<template #next>
|
||||||
|
<v-btn @click="stepper = 'file'">{{ $t('Next') }}</v-btn>
|
||||||
|
</template>
|
||||||
|
</v-stepper-actions>
|
||||||
|
</v-stepper-window-item>
|
||||||
|
<v-stepper-window-item value="file">
|
||||||
|
<v-file-upload v-model="appImportFile"></v-file-upload>
|
||||||
|
|
||||||
|
|
||||||
|
<v-stepper-actions>
|
||||||
|
<template #prev>
|
||||||
|
<v-btn @click="stepper = 'app'">{{ $t('Back') }}</v-btn>
|
||||||
|
</template>
|
||||||
|
<template #next>
|
||||||
|
<v-btn @click="appImport()" :disabled="appImportFile == null" :loading="fileApiLoading">{{ $t('Import') }}</v-btn>
|
||||||
|
</template>
|
||||||
|
</v-stepper-actions>
|
||||||
|
</v-stepper-window-item>
|
||||||
|
<v-stepper-window-item value="import_log">
|
||||||
|
|
||||||
|
<v-stepper-actions>
|
||||||
|
<template #prev>
|
||||||
|
<v-btn @click="stepper = 'app'">{{ $t('Back') }}</v-btn>
|
||||||
|
</template>
|
||||||
|
<template #next>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
</v-stepper-actions>
|
||||||
|
</v-stepper-window-item>
|
||||||
|
|
||||||
|
|
||||||
</v-stepper-window>
|
</v-stepper-window>
|
||||||
|
|
||||||
<!-- <v-stepper-actions>-->
|
|
||||||
<!-- <template #prev>-->
|
|
||||||
<!-- <v-btn @click="stepper = (parseInt(stepper) - 1).toString()">Zurück</v-btn>-->
|
|
||||||
<!-- </template>-->
|
|
||||||
<!-- <template #next>-->
|
|
||||||
<!-- <v-btn @click="createRecipeFromImport()" color="success" :disabled="Object.keys(importResponse).length == 0" v-if="stepper == '1'">-->
|
|
||||||
<!-- {{ $t('Import') }}-->
|
|
||||||
<!-- </v-btn>-->
|
|
||||||
<!-- <v-btn @click="stepper = (parseInt(stepper) + 1).toString()" :disabled="Object.keys(importResponse).length == 0" v-if="stepper != '5'">-->
|
|
||||||
<!-- {{ stepper == '1' ? $t('Edit') : $t('Next') }}-->
|
|
||||||
<!-- </v-btn>-->
|
|
||||||
<!-- <v-btn @click="createRecipeFromImport()" color="success" :disabled="false" v-if="stepper == '5'">{{ $t('Import') }}</v-btn>-->
|
|
||||||
<!-- </template>-->
|
|
||||||
<!-- </v-stepper-actions>-->
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
</v-stepper>
|
</v-stepper>
|
||||||
@@ -333,19 +381,22 @@ import {useFileApi} from "@/composables/useFileApi";
|
|||||||
import ModelSelect from "@/components/inputs/ModelSelect.vue";
|
import ModelSelect from "@/components/inputs/ModelSelect.vue";
|
||||||
import {useDisplay} from "vuetify";
|
import {useDisplay} from "vuetify";
|
||||||
import {useUrlSearchParams} from "@vueuse/core";
|
import {useUrlSearchParams} from "@vueuse/core";
|
||||||
|
import {INTEGRATIONS} from "@/utils/integration_utils";
|
||||||
|
import {VFileUpload} from 'vuetify/labs/VFileUpload'
|
||||||
|
|
||||||
const params = useUrlSearchParams('history', {})
|
const params = useUrlSearchParams('history', {})
|
||||||
const {mobile} = useDisplay()
|
const {mobile} = useDisplay()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const {updateRecipeImage, convertImageToRecipe, fileApiLoading} = useFileApi()
|
const {updateRecipeImage, convertImageToRecipe, doAppImport, fileApiLoading} = useFileApi()
|
||||||
|
|
||||||
const importType = ref<'url' | 'ai' | 'app' | 'bookmarklet' | 'source'>("url")
|
const importType = ref<'url' | 'ai' | 'app' | 'bookmarklet' | 'source'>("url")
|
||||||
|
const importApp = ref('DEFAULT')
|
||||||
const stepper = ref("type")
|
const stepper = ref("type")
|
||||||
const dialog = ref(false)
|
const dialog = ref(false)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const importUrl = ref("")
|
const importUrl = ref("")
|
||||||
|
|
||||||
|
const appImportFile = ref<null | File>(null)
|
||||||
const image = ref<null | File>(null)
|
const image = ref<null | File>(null)
|
||||||
|
|
||||||
const importResponse = ref({} as RecipeFromSourceResponse)
|
const importResponse = ref({} as RecipeFromSourceResponse)
|
||||||
@@ -399,6 +450,12 @@ function uploadAndConvertImage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function appImport() {
|
||||||
|
doAppImport(appImportFile.value, importApp.value, true).then(r => {
|
||||||
|
stepper.value = 'import_log'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create recipe in database
|
* create recipe in database
|
||||||
*/
|
*/
|
||||||
|
|||||||
35
vue3/src/utils/integration_utils.ts
Normal file
35
vue3/src/utils/integration_utils.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
|
||||||
|
|
||||||
|
export type Integration = {
|
||||||
|
id: string,
|
||||||
|
name: string,
|
||||||
|
import: boolean,
|
||||||
|
export: boolean,
|
||||||
|
helpUrl: string,
|
||||||
|
imgSrc?: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const INTEGRATIONS: Array<Integration> = [
|
||||||
|
{id: 'DEFAULT', name: "Tandoor", import: true, export: true, helpUrl: 'https://docs.tandoor.dev/features/import_export/#default', imgSrc: 'https://raw.githubusercontent.com/TandoorRecipes/recipes/develop/docs/logo_color.svg'},
|
||||||
|
{id: 'CHEFTAP', name: "Cheftap", import: true, export: false, helpUrl: 'https://docs.tandoor.dev/features/import_export/#cheftap'},
|
||||||
|
{id: 'CHOWDOWN', name: "Chowdown", import: true, export: false, helpUrl: 'https://docs.tandoor.dev/features/import_export/#chowdown'},
|
||||||
|
{id: 'COOKBOOKAPP', name: "CookBookApp", import: true, export: false, helpUrl: 'https://docs.tandoor.dev/features/import_export/#cookbookapp'},
|
||||||
|
{id: 'COOKMATE', name: "Cookmate", import: true, export: false, helpUrl: 'https://docs.tandoor.dev/features/import_export/#cookmate'},
|
||||||
|
{id: 'COPYMETHAT', name: "CopyMeThat", import: true, export: false, helpUrl: 'https://docs.tandoor.dev/features/import_export/#copymethat'},
|
||||||
|
{id: 'DOMESTICA', name: "Domestica", import: true, export: false, helpUrl: 'https://docs.tandoor.dev/features/import_export/#domestica'},
|
||||||
|
{id: 'MEALIE', name: "Mealie", import: true, export: false, helpUrl: 'https://docs.tandoor.dev/features/import_export/#mealie'},
|
||||||
|
{id: 'MEALMASTER', name: "Mealmaster", import: true, export: false, helpUrl: 'https://docs.tandoor.dev/features/import_export/#mealmaster'},
|
||||||
|
{id: 'MELARECIPES', name: "Melarecipes", import: true, export: false, helpUrl: 'https://docs.tandoor.dev/features/import_export/#melarecipes'},
|
||||||
|
{id: 'NEXTCLOUD', name: "Nextcloud Cookbook", import: true, export: false, helpUrl: 'https://docs.tandoor.dev/features/import_export/#nextcloud'},
|
||||||
|
{id: 'OPENEATS', name: "Openeats", import: true, export: false, helpUrl: 'https://docs.tandoor.dev/features/import_export/#openeats'},
|
||||||
|
{id: 'PAPRIKA', name: "Paprika", import: true, export: false, helpUrl: 'https://docs.tandoor.dev/features/import_export/#paprika'},
|
||||||
|
{id: 'PEPPERPLATE', name: "Pepperplate", import: true, export: false, helpUrl: 'https://docs.tandoor.dev/features/import_export/#pepperplate'},
|
||||||
|
{id: 'PLANTOEAT', name: "Plantoeat", import: true, export: false, helpUrl: 'https://docs.tandoor.dev/features/import_export/#plantoeat'},
|
||||||
|
{id: 'RECETTETEK', name: "RecetteTek", import: true, export: false, helpUrl: 'https://docs.tandoor.dev/features/import_export/#recettetek'},
|
||||||
|
{id: 'RECIPEKEEPER', name: "Recipekeeper", import: true, export: false, helpUrl: 'https://docs.tandoor.dev/features/import_export/#recipekeeper'},
|
||||||
|
{id: 'RECIPESAGE', name: "Recipesage", import: true, export: true, helpUrl: 'https://docs.tandoor.dev/features/import_export/#recipesage'},
|
||||||
|
{id: 'REZKONV', name: "Rezkonv", import: true, export: false, helpUrl: 'https://docs.tandoor.dev/features/import_export/#rezkonv'},
|
||||||
|
{id: 'SAFRON', name: "Safron", import: true, export: true, helpUrl: 'https://docs.tandoor.dev/features/import_export/#safron'},
|
||||||
|
{id: 'REZEPTSUITEDE', name: "Rezeptsuite.de", import: true, export: false, helpUrl: 'https://docs.tandoor.dev/features/import_export/#rezeptsuitede'},
|
||||||
|
{id: 'GOURMET', name: "Gourmet", import: true, export: false, helpUrl: 'https://docs.tandoor.dev/features/import_export/#gourmet'},
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user